Варварский генератор
Недавно с коллегами обсудили, что, строго говоря, генератор не соблюдает принцип подстановки Барбары Лисков. Он кидает исключение при попытке перемотки после начала обхода и тем самым нарушает контракт Iterable, где такое поведение не предусмотрено.
Понятно, почему конструктивно так сделали. Тем не менее передача генератора в функцию, которая принимает Traversable или iterable, приводит к WTF казусу, если она вполне законно пытается обойти аргумент больше одного раза.
Если очень хочется, чтобы инстанс генератора отрабатывал многократно, и при этом вы уверены, что это не приведёт к ненужным побочным эффектам, можно обернуть функцию, порождающую генератор, в RewindableGenerator:
/**
* @template TKey
* @template TValue
* @implements IteratorAggregate<TKey, TValue>
*/
final class RewindableGenerator implements IteratorAggregate
{
/**
* @param callable(): Generator<TKey, TValue> $generatorFunction
*/
public function __construct(private $generatorFunction) {}
public function getIterator(): Traversable
{
return ($this->generatorFunction)();
}
}
Вот как это будет работать: https://3v4l.org/46Evq.
Пишите в комментариях, где ещё PHP нарушает LSP.