PHP生成器是5.5.0引入的功能。
生成器实际上就是简单的迭代器。
与标准的PHP迭代器不同,生成器不要求类实现Iterator接口,从而减轻了类的负担。
生成器会根据需求计算产出迭代的值。而标准的PHP迭代器经常在内存中执行迭代操作,这要预先计算出数据集,性能较低。
如果使用特定的防护计算大量数据,可以使用生成器,即时计算并产出后续值,不占用内存。
生成器不能完成所有迭代器的操作。无法后退,快进,并且生成器是一次性的,无法对此迭代同一个生成器。
创建
生成器从不返回值,只是产出值。
<?php
function myGenerator() {
yield 'v1';
yield 'v2';
yield 'v3';
}
调用生成器函数时,PHP会反悔一个属于Generator
类的对象。这个对象是可以foreach
迭代的。每次迭代,PHP要求这个实例计算并提供下一个要迭代的值。
每次产出一个值,生成器的内部状态都会停顿。向生成器请求下一个值时,内部状态才会恢复。这种停顿-恢复的状态会一直持续下去。
<?php
foreach (myGenerator() as $yieldValue) {
echo $yieldValue , PHP_EOL;
}
使用
<?php
function makeRange($length) {
$dataset = [];
for ($i = 0; $i < $length; $i++) {
$dataset[] = $i;
}
return $dataset;
}
$customRange = makeRange(1000000);
foreach ($customRange as $i) {
echo $i, PHP_EOL;
}
上面的这个方法并没有善用内存,使用生成器只会为一个整数分配内存。
<?php
function makeRange($length) {
for ($i = 0; $i < $length; $i++) {
yield $i;
}
}
foreach(makeRange(1000000) as $i) {
echo $i, PHP_EOL;
}
再举个例子:使用生成器处理CSV文件
<?php
function getRows($file) {
$handle = fopen($file, 'rb');
if ($handle === false) {
throw new Exception();
}
while (feof($handle) === false) {
yield fgetcsv($handle);
}
fclose($handle);
}
foreach (getRows('data.csv') as $row) {
print_r($row);
}
这个例子中,生成器只会为CSV文件分配一行内存,而不是读入整个文件到内存。
如果需要更多功能,例如在数据集中执行后腿,快进或查找操作,最好自己编写类,实现Iterator接口(http://php.net/manual/class.iterator.php),或者使用PHP标准库中某个原生的迭代器(http://php.net/manual/spl.iterators.php)
参考:
Modern PHP