生成器的核心是一个yield关键字,一个生成器函数看起来像一个普通的函数,不同的是。普通函数返回一个值,而一个生成器可以yield生成许多它所需要的值。生成器函数被调用时,返回的是一个可以被遍历的对象。yield和return有点类似,不过不同的是,return会返回值并且终止代码的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。
<?php function get_one_to_three(){ for($i=1;$i<=3;$i++){ yield $i; } } $generator=get_one_to_three(); var_dump($generator); echo '<br/>'; var_dump($generator instanceof Iterator); echo '<br/>'; foreach ($generator as $value){ echo $value,'<br/>'; }
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/2d9b9590c1e250a40926fa1fcf8bd0e7.png)
调用get_one_to_three()的时候,里面的代码并没有真正的执行,而是返回了一个生产期对象$generator=Generator Object(),$genetator instanceof Iterator说明Generator实现了Iterator接口,可以用foreach进行遍历,每次遍历都会隐式调用current()、next()、key()、valid()等方法。(Generator类中的方法)
<?php Generator implements Iterator{ public mixed current(void)//返回当初产生的值 public mixed key(void)//返回当前产生的键 public void next(void)//生产器继续执行 public void rewind(void)//重置迭代器,如果迭代已经开始了,这里会抛出一个异常。 public mixed send(mixed $value)//向生成器中传入一个值,当前yield接收值,然后继续执行下一个yield public void throw(Exception $exception)//向生成器中抛入一个异常 public bool valid(void)//检查迭代器是否被关闭,已被关闭返回FALSE,否则返回TRUE public void __wakeup(void)//序列化回调 public mixed getReturn(void)//返回generator函数的返回值,PHP version 7+ }
处理大数据
下面通过实现一个xrange函数来简单说明:
<?php function xrange($start,$end,$step=1){ for($i=$start;$i<=$end;$i+=$step){ yield $i; } } foreach(xrange(1,10) as $num){ echo $num,'<br/>'; }
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/aaaba031e5810d71c08d8df292357387.png)
上面这个xrange()函数提供了和PHP的内建函数range()一样的功能,但是不同的是range()函数返回的是一个包含值从1到10的数组,而xrange()函数返回的是依次输出的这些值得一个迭代器,而不会真正以数组形式返回。这种方法的优点是显而易见的,它可以让你在处理大数据集合的时候不用一次性的加载到内存中,甚至你可以处理无限大的数据流。
处理大文件
来优化下读取大文件,在PHP读取大文件的时候,经常会出现内存不足的情况,如果文件过大的话,没法一次读取完,采用yield来实现大文件的读取。
老式读取
function readLocalFile($fileName){ $handle=fopen($fileName,'r'); $lines=[]; while(!feof($handle)){ $lines[]=fgets($handle); } fclose($handle); return $lines; }
yield读取方式
function readYieldFile($fileName){ $handle=fopen($fileName,'r'); while(!feof($handle)){ yield fgets($handle); } fclose($handle); }
为了便于测试,我们写一个读取内存的辅助函数
function formatBytes($bytes) { if ($bytes < 1024) { return $bytes . 'b'; } elseif ($bytes) { return round($bytes / 1024, 2) . 'kb'; } return round($bytes / 1048576, 2) . 'mb'; }
测试
//第一种 var_dump(readLocalFile('./test.txt')); echo '<br/>',formatBytes(memory_get_peak_usage()),'<br/>'; //第二种 $lines = readYieldFile('./test.txt'); foreach ($lines as $row) { echo $row,'<br/>'; } echo formatBytes(memory_get_peak_usage());
输出结果:
![](https://i-blog.csdnimg.cn/blog_migrate/86643d14b8f62f643530e2ff8ee2b744.png)
总结
使用老式读取,返回的是一个包含每行数据的数组,而yield方式则返回的是一个迭代器,而不会以真正的数组返回。
这种方法的优点是显而易见的,它可以让你在处理大数据集合的时候不用一次性的加载到内存中,甚至你可以处理无限大的数据流。