php高级特性yield生成器

在PHP5.5以上的版本有一个新的特性yield生成器,提供了一种更简单的可实现 Iterator 同样功能的方法。

PHP手册里的解释:

生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。

生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。

看了似乎也不明白,解释的比较晦涩,还是看下代码来的直接。

<?php
function gen_one_to_three() {
    for ($i = 1; $i <= 3; $i++) {
        yield $i;
    }
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
    echo "$value\n";
}

这样似乎感觉不到有什么用

// a.log
123.151.47.117 - - [16/Feb/2015:00:00:02 +0800] "GET / HTTP/1.1" 200 32535 "" "Mozilla/5.0 "
220.171.166.19 - - [16/Feb/2015:00:00:06 +0800] "GET /search/mp3 HTTP/1.1" 200 10307 "" "Mozilla/5.0 Safari/537.1"
210.71.66.59 - - [16/Feb/2015:00:00:07 +0800] "GET /mp3/page/1 HTTP/1.1" 200 10307 "" "Mozilla/5.0 Safari/537.1"
120.231.1.23 - - [16/Feb/2015:00:00:08 +0800] "GET /mp3 HTTP/1.1" 200 10307 "" "Mozilla/5.0 Safari/537.1"
<?php
function lineGenerator($file) {
    $fp = fopen($file, 'rb');
    try {
        while($line = fgets($fp)) {
            yield $line;
        }
    } finally {
        fclose($fp);
    }
}

$lines = lineGenerator("a.log"); 
foreach($lines as $line) {
    echo $line;
}
?>

可以通过生成器逐条产生(yield)供 foreach 遍历的数据,而且不需要事先在内存中建立整个数组。

生成器像一个函数,实际上是返回一个 Generator 对象,但是使用关键词 yield 返回数据。
这个 Generator对象类似于Iterator,但比Iterator多了一个send方法

这是php手册中的一个例子,可以产生key => value

<?php
/* The input is semi-colon separated fields, with the first
 * field being an ID to use as a key. */

$input = <<<'EOF'
1;PHP;Likes dollar signs
2;Python;Likes whitespace
3;Ruby;Likes blocks
EOF;

function input_parser($input) {
    foreach (explode("\n", $input) as $line) {
        $fields = explode(';', $line);
        $id = array_shift($fields);

        yield $id => $fields;
    }
}

foreach (input_parser($input) as $id => $fields) {
    echo "$id:\n";
    echo "    $fields[0]\n";
    echo "    $fields[1]\n";
}
?> 

结合IteratorAggregate使用

<?php
class Test implements IteratorAggregate {
    protected $data;

    public function __construct(array $data) {
        $this->data = $data;
    }

    public function getIterator() {
        foreach ($this->data as $key => $value) {
            yield $key => $value;
        }
    }
}

yield还有更高级的用法,可以参考“在PHP中使用协程实现多任务调度


参考资料

http://php.net/manual/zh/class.generator.php
http://php.net/manual/zh/language.generators.overview.php
http://www.laruence.com/2015/05/28/3038.html