yield 调用
PHP中的range() 函数在使用的时候会在内存中创建一个包含指定范围单元的数组并返回,一般来说,这个并没什么不妥,但是当所传的limit入参值很大的时候,那么也就意味着将会在内存中创建的数组也会很大,内存无法承受。此时我们可以通过生成器来实现一个更高效的range函数
function xrange($start, $limit, $step = 1) { //校验参数,此处省略 for ($i = $start; $i <= $limit; $i += $step) { //向外产出值 yield $i; } } //xrange此时返回的是一个生成器对象 $gen = xrange(1, 9); //对生成器进行迭代 foreach ($gen as $number) { echo "$number "; }
这里在xrange和range函数的效果相同,均是产生了一个可迭代的变量,但是不同的是,range函数有点像ORM里面常说的 预加载 ,而xrange则是懒加载只是等到 迭代到那个点(循环到当前的值再运行一次xrange 这个函数)才会产生对应的值,因此xrange并不需要分配大块内存来存放变量,大大节约了内存,提升效率。
生成器和普通函数有哪些异同
-
生成器中必须包含yield关键字(用来生成结果),而且可以是多次出现,普通函数中向外部返回结果只能使用return,且函数执行完毕;
-
一个生成器不可以通过return返回值,这样做会产生一个编译错误PHP Fatal error: Generators cannot return values using "return"(注意:这个在PHP7下面不会出错,但是会终止生成器继续执行,即调valid()方法会返回false,然而在PHP5中return空是一个有效的语法并且它将会终止生成器继续执行)
3. yield关键词,它最简单的调用形式看起来像普通函数的return,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码,并且只是暂停执行生成器函数。
PHP yield 读取大文件
// 老式读取方式 function readLocalFile($fileName) { $handle = fopen($fileName, ‘r‘); $lins = []; 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"; } else if ($bytes < 1048576) { return round($bytes / 1024, 2) . "kb"; } return round($bytes / 1048576, 2) . ‘mb‘; }
测试,这里准备了一个 7M 大小的文本文件来做测试
# 第一种
readLocalFile(‘./all.txt‘);
echo formatBytes(memory_get_peak_usage()); // 结果为 7.59mb
# 第二种
$lines = readYieldFile(‘./all.txt‘);
foreach ($lines as $row) {}
echo formatBytes(memory_get_peak_usage()); // 结果为 137.79kb