生成器语法
一个生成器函数看起来像一个普通的函数,不同的是普通函数返回一个值,而一个生成器可以 yield
生成许多它所需要的值。当一个生成器被调用的时候,它返回一个可以被遍历的对象。当你遍历这个对象的时候(例如通过一个 foreach
循环), PHP 将会在每次需要值的时候调用生成器函数,并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。一旦不再需要产生更多的值,生成器函数可以简单退出,而调用生成器的代码还可以继续执行,就像一个数组已经被遍历完了。生成器函数的核心是 yield
关键字。它最简单的调用形式看起来像一个 return
申明,不同之处在于普通 return
会返回值并终止函数的执行,而 yield
会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。
<?php
/**
* Yielding test values.
*
* @param void
* @return void
*/
function foo()
{
for ($i = 0; $i < 10; $i++) {
yield $i;
}
}
$foo = foo();
foreach ($foo as $value) {
var_dump($value);
}
// int(0)
// int(1)
// int(2)
// int(3)
// int(4)
// int(5)
// int(6)
// int(7)
// int(8)
// int(9)
PHP 的数组支持关联键值对数组,生成器也一样支持。所以除了生成简单的值,也可以在生成值的时候指定键名。
<?php
/**
* Yielding test values.
*
* @param string $string
* @return void
*/
function foo(string $string)
{
$items = explode(';', $string);
foreach ($items as $item) {
list($key, $value) = explode('.', $item);
yield $key => $value;
}
}
$foo = foo('1.foo;2.bar;3.baz;4.qux');
foreach ($foo as $key => $value) {
var_dump("$key => $value");
}
// string(8) "1 => foo"
// string(8) "2 => bar"
// string(8) "3 => baz"
// string(8) "4 => qux"
yield
可以在没有参数传入的情况下被调用来生成一个 NULL
值并配对一个自动的键名。
<?php
/**
* Yielding test values.
*
* @param void
* @return void
*/
function foo()
{
for ($i = 0; $i < 10; $i++) {
yield;
}
}
$foo = foo();
var_dump(iterator_to_array($foo)); // array(10) { [0]=> NULL [1]=> NULL [2]=> NULL [3]=> NULL [4]=> NULL [5]=> NULL [6]=> NULL [7]=> NULL [8]=> NULL [9]=> NULL }
生成器函数可以像使用值一样来使用引用生成。这个和从函数返回一个引用一样,通过在函数名前面加一个引用符号。
<?php
/**
* Yielding test values.
*
* @param void
* @return void
*/
function &foo()
{
$foo = 3;
while ($foo > 0) {
yield $foo;
}
}
$foo = foo();
foreach ($foo as &$value) {
var_dump($value--);
}
// int(3)
// int(2)
// int(1)
生成器委派允许你通过使用 yield from
来从另一个生成器, Traversable
对象或数组中生成值。 然后外部生成器将从内部生成器,对象或数组中生成所有值,直到它不再有效,之后将在外部生成器中继续执行。如果生成器与 yield from
一起使用,则表达式的 yield
也将返回内部生成器返回的任何值。
<?php
/**
* Yielding test values.
*
* @param void
* @return void
*/
function foo()
{
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from bar();
yield 9;
yield 10;
}
/**
* Yielding test values.
*
* @param void
* @return void
*/
function bar()
{
yield 7;
yield from baz();
}
/**
* Yielding test values.
*
* @param void
* @return void
*/
function baz()
{
yield 8;
}
$foo = foo();
foreach ($foo as $value) {
var_dump($value);
}
// int(1)
// int(2)
// int(3)
// int(4)
// int(5)
// int(6)
// int(7)
// int(8)
// int(9)
// int(10)
同时使用 yield
和 return
。
<?php
/**
* Yielding test values.
*
* @param void
* @return void
*/
function foo()
{
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from bar();
return yield from qux();
}
/**
* Yielding test values.
*
* @param void
* @return void
*/
function bar()
{
yield 7;
yield from baz();
}
/**
* Yielding test values.
*
* @param void
* @return void
*/
function baz()
{
yield 8;
}
/**
* Yielding test values.
*
* @param void
* @return void
*/
function qux()
{
yield 9;
return 10;
}
$foo = foo();
foreach ($foo as $value) {
var_dump($value);
}
var_dump($foo->getReturn());
// int(1)
// int(2)
// int(3)
// int(4)
// int(5)
// int(6)
// int(7)
// int(8)
// int(9)
// int(10)