如果是做Python或者其他语言的小伙伴,对于生成器应该不陌生。但很多PHP开发者或许都不知道生成器这个功能,可能是因为生成器是PHP 5.5.0才引入的功能,也可以是生成器作用不是很明显。但是,生成器功能的确非常有用。
优点
生成器会对PHP应用的性能有非常大的影响
PHP代码运行时节省大量的内存
比较适合计算大量的数据
概念引入
来看一个普通php函数
/**
* 一个普通函数:把当前时间戳存入数组中
*/
function test($len)
{
$data = [];
for($i=0; $i
$data[] = time(); //把当前的时间戳存入数组中
}
return $data;
}
$d = test(10);
foreach($d as $val)
{
sleep(1); //暂停一秒
echo $val . '
';
}
得到的结果是:
test.php
创建生成器
看下这段和刚刚很像的代码,我们删除了数组 $data ,而且也没有返回任何内容,而是在 time() 之前使用了一个关键字yield。
/**
* 一个普通函数:把当前时间戳存入数组中
*/
function test($len)
{
for($i=0; $i
yield time(); //把当前的时间戳存入数组中
}
}
$d = test(10);
foreach($d as $val)
{
sleep(1); //暂停一秒
echo $val . '
';
}
得出结果是:
test.php
我们奇迹般的发现了,输出的值和第一次没有使用生成器的不一样。这里的值(时间戳)中间间隔了1秒。这里的间隔一秒其实就是 sleep(1) 造成的后果。但是为什么第一次没有间隔?那是因为:
未使用生成器时: test 函数内的 for 循环结果被很快放到 $data 中,并且立即返回。所以 foreach循环的是一个固定的数组。
使用生成器时: test 的值不是一次性快速生成,而是依赖于foreach 循环。 foreach 循环一次, for 执行一次。
到这里,你应该对生成器有点儿头绪。
yield实际开发应用
PHP开发很多时候都要读取大文件,比如csv文件、text文件,或者一些日志文件。这些文件如果很大,比如5个G。这时,直接一次性把所有的内容读取到内存中计算不太现实。
header("content-type:text/html;charset=utf-8");
function readTxt()
{
# code...
$handle = fopen("./test.txt", 'rb');
while (feof($handle)===false) {
# code...
yield fgets($handle);
}
fclose($handle);
}
foreach (readTxt() as $key => $value) {
# code...
echo $value.'
';
}
结果:
图片.png
通过上图的输出结果我们可以看出代码完全正常。
但是,背后的代码执行规则却一点儿也不一样。使用生成器读取文件,第一次读取了第一行,第二次读取了第二行,以此类推,每次被加载到内存中的文字只有一行,大大的减小了内存的使用。
这样,即使读取上G的文本也不用担心,完全可以像读取很小文件一样编写代码。