如果你想在 foreach 循环中使用 generator::send(),你很可能会得到一个意外的结果。Generator::send() 方法恢复生成器,这意味着生成器中的指针将移动到生成器列表中的下一个元素。
这是一个例子
<?php
class ApiDummy
{
private static $apiDummyData = ['a', 'b', 'c', 'd', 'e'];
public static function getAll(): Generator {
foreach (self::$apiDummyData as $entry) {
echo 'yielding $elem' . PHP_EOL;
$newElem = (yield $entry);
echo 'yield return: ' . $newElem . PHP_EOL;
}
}
}
$generator = ApiDummy::getAll();
foreach ($generator as $elem) {
echo 'value from generator: ' . $elem . PHP_EOL;
$generator->send($elem . '+');
}
while ($generator->valid()) {
$elem = $generator->current();
echo 'value from generator: ' . $elem . PHP_EOL;
$generator->send($elem . '+');
}
?>
第一个示例的结果
yielding $elem
value from generator: a
yield return: a+
yielding $elem
yield return
yielding $elem
value from generator: c
yield return: c+
yielding $elem
yield return
yielding $elem
value from generator: e
yield return: e+
正如你所看到的,b 和 d 没有被打印出来,也没有加上 + 号。
foreach 循环接收第一个 yield,send 调用在第一个循环内导致第二个 yield。因此,第二个循环已经接收了第三个 yield,以此类推。
为了避免这种情况,一种解决方案是使用 while 循环和 Generator::send() 方法来移动生成器游标,以及 Generator::current() 方法来检索当前值。可以使用 Generator::valid() 方法控制循环,如果生成器已完成,则返回 false。参见示例迭代二。
示例迭代二的预期结果
yielding $elem
value from generator: a
yield return: a+
yielding $elem
生成器值:b
yield 返回:b+
yielding $elem
value from generator: c
yield return: c+
yielding $elem
生成器值:d
yield 返回:d+
yielding $elem
value from generator: e
yield return: e+