You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AppendIterator适配空Generator报错:无法遍历已关闭生成器,如何解决?

问题解答

这是PHP标准库的Bug吗?

不是。这是AppendIterator的设计行为与PHP生成器(Generator)的特性冲突导致的正常现象:

  • PHP生成器是单向迭代器,一旦遍历结束(哪怕没有产出任何元素),生成器就会进入关闭状态,无法通过rewind()方法重置迭代位置。
  • AppendIterator在遍历前会对每个子迭代器调用一次rewind(),实际遍历过程中还会再次操作子迭代器。当子迭代器是已关闭的生成器时,就会触发Cannot traverse an already closed generator错误。

继续使用生成器的解决方案

方案1:用IteratorAggregate包装生成器

通过IteratorAggregate接口,让每次迭代都生成一个全新的Generator实例,避免重复使用已关闭的生成器。修改Fruits类的getApples方法:

class Fruits
{
    public function __construct(protected array $items) {}

    public function getApples(): \IteratorAggregate {
        return new class($this->items) implements \IteratorAggregate {
            public function __construct(private array $items) {}

            public function getIterator(): \Iterator {
                foreach ($this->items as $item) {
                    if ($item === 'apple') {
                        yield $item;
                    }
                }
            }
        };
    }
}

当AppendIterator需要迭代子元素时,会调用getIterator()方法,每次都会得到一个新的生成器,自然不会出现已关闭的问题。

方案2:将生成器转换为可重复迭代的容器

如果生成器的数据量不大,可以先将其转换为ArrayIterator,这类迭代器支持重复rewind()操作:

public function test_iterator() {
    $fruits = new Fruits(['banana', 'banana', 'orange']);
    $concat = new AppendIterator();
    // 先将生成器转换为ArrayIterator
    $applesArray = iterator_to_array($fruits->getApples());
    $concat->append(new ArrayIterator($applesArray));
    $array = iterator_to_array($concat);
    $this->assertEquals(0, count($array));
}

注意:此方法会将生成器的所有元素一次性加载到内存中,不适合处理大数据量的生成器。

内容的提问来源于stack exchange,提问作者gvlasov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 01:17:05