有时,当我必须将多个迭代器连接成一个迭代器时,它只是一个空迭代器。我使用
AppendIterator
来连接迭代器。当我将一个空的 Generator
从 yield
ing 方法添加到 AppendIterator
时,我希望它成为一个具有 0 个元素的迭代器:
<?php
namespace Tests\cases\domain;
use AppendIterator;
use PHPUnit\Framework\TestCase;
class IteratorTest extends TestCase
{
public function test_iterator() {
$fruits = new Fruits(['banana', 'banana', 'orange']);
$concat = new AppendIterator();
$concat->append($fruits->getApples());
$array = iterator_to_array($concat);
$this->assertEquals(0, count($array));
}
}
class Fruits
{
public function __construct(protected array $items) {
}
public function getApples(): \Iterator {
foreach ($this->items as $item) {
if ($item === 'apple') {
yield $item;
}
}
}
}
但是,当遍历这样的迭代器时,会产生错误:
PHPUnit 11.3.6 by Sebastian Bergmann and contributors.
Runtime: PHP 8.3.12
Configuration: /home/me/Projects/personal/myproject/framework/phpunit.xml
Exception: Cannot traverse an already closed generator
/home/me/Projects/personal/myproject/tests/cases/domain/IteratorTest.php:15
Time: 00:00.002, Memory: 10.00 MB
There was 1 error:
1) Tests\cases\domain\IteratorTest::test_iterator
Exception: Cannot traverse an already closed generator
/home/me/Projects/personal/myproject/tests/cases/domain/IteratorTest.php:15
ERRORS!
Tests: 1, Assertions: 0, Errors: 1.
Process finished with exit code 2
它似乎试图遍历生成器两次,这是不可接受的,因为生成器不会自动倒带。
我该如何解决这个问题?我没发现我的发电机有什么问题。一切都指向
AppendIterator
实现中的可疑之处,这是 PHP 标准库中的错误吗?我可以在代码中仍然使用生成器的同时解决这个问题吗?
我理解你的问题!出现“无法遍历已关闭的生成器”异常是因为 PHP 中的生成器是单遍的,这意味着它们不能被遍历多次。 AppendIterator 正在尝试遍历已经关闭的生成器。这是一个继续使用发电机同时避免发电机关闭问题的解决方案:
我们可以将生成器逻辑封装在一个单独的类中,并确保遍历逻辑得到正确处理:
<?php
namespace Tests\cases\domain;
use AppendIterator;
use IteratorAggregate;
use ArrayIterator;
use PHPUnit\Framework\TestCase;
class IteratorTest extends TestCase
{
public function test_iterator() {
$fruits = new Fruits(['banana', 'banana', 'orange']);
$concat = new AppendIterator();
$concat->append($fruits->getApples());
$array = iterator_to_array($concat);
$this->assertEquals(0, count($array));
}
}
class Fruits implements IteratorAggregate
{
protected array $items;
public function __construct(array $items) {
$this->items = $items;
}
public function getApples(): \Iterator {
return new ArrayIterator(array_filter($this->items, function ($item) {
return $item === 'apple';
}));
}
public function getIterator(): \Iterator {
return $this->getApples();
}
}
在此解决方案中:
我使用 IteratorAggregate 和 ArrayIterator 来处理生成器逻辑。
getApples 方法返回一个经过过滤的 ArrayIterator 而不是生成器。
ArrayIterator 是一个可以多次遍历而不需要关闭的迭代器。
通过此修改,您应该避免“无法遍历已关闭的生成器”错误并保持代码按预期工作。
希望这对您有帮助! 🚀