现代 PHP 新特性系列(四) —— 生成器的创建和使用

摘要:
生成器与迭代器类似,但与标准PHP迭代器不同,PHP生成器不需要类实现迭代器接口,从而减少了类的开销和负担。与普通PHP函数不同,生成器从不返回值,只输出值。生成器还可以完成迭代器可以用更少代码完成的简单任务。

1、概述

     生成器是 PHP 5.5 引入的新特性,但是目测很少人用到它,其实这是个非常有用的功能。

     生成器和迭代器有点类似,但是与标准的PHP迭代器不同,PHP生成器不要求类实现Iterator接口,从而减轻了类的开销和负担。生成器会根据需求每次计算并产出需要迭代的值,这对应用的性能有很大的影响:试想假如标准的PHP迭代器经常在内存中执行迭代操作,这要预先计算出数据集,性能低下;如果要使用特定方式计算大量数据,如操作Excel表数据,对性能影响更甚。此时我们可以使用生成器,即时计算并产出后续值,不占用宝贵的内存空间。

2、创建生成器

     生成器的创建方式很简单,因为生成器就是PHP函数,只不过要在函数中一次或多次使用yield关键字。与普通的PHP函数不同的是,生成器从不返回值,只产出值。下面是一个简单的生成器实现:

function getLaravelAcademy() {
    yield 'http://LaravelAcademy.org';
    yield 'Laravel学院';
    yield 'Laravel Academy';
}

     很简单吧!调用此生成器函数时,PHP会返回一个属于Generator类的对象,这个对象可以使用foreach函数迭代,每次迭代,PHP会要求Generator实例计算并提供下一个要迭代的值。生成器的优雅体现在每次产出一个值之后,生成器的内部状态都会停顿;向生成器请求下一个值时,内部状态又会恢复。生成器内部的状态会一直在停顿和恢复之间切换,直到抵达函数定义体的末尾或遇到空的return语句为止。我们可以使用下面的代码调用并迭代上面定义的生成器:

foreach(getLaravelAcademy() as $yieldedValue) {
    echo $yieldedValue, PHP_EOL;
}

     上面代码输出如下:

http://LaravelAcademy.org
Laravel学院
Laravel Academy

3、使用生成器

     下面我们实现一个简单的函数用于生成一个范围内的数值,以此说明生成器是如何节省内存的。首先我们通过迭代器来实现:

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进程内存限制(要为100万个数字提供内存空间):

    现代 PHP 新特性系列(四) —— 生成器的创建和使用第1张

     下面我们来改进实现方案,使用生成器实现如下:

function makeRange($length) {
    for ($i=0; $i<$length; $i++) {
        yield $i;
    }
}

foreach (makeRange(1000000) as $i) {
    echo $i . PHP_EOL;
}

     再次执行就可以毫无压力的打印出结果,因为生成器每次只需要为一个整数分配内存。

     此外,一个常用的使用案例就是使用生成器迭代流资源(文件、音频等)。假设我们想要迭代一个大小为4GB的CSV文件,而虚拟私有服务器(VPS)只允许PHP使用1GB内存,因此不能把整个文件都加载到内存中,下面的代码展示了如何使用生成器完成这种操作:

function getRows($file) {
    $handle = fopen($file, 'rb');
    if ($handle == FALSE) {
        throw new Exception();
    }
    while (feof($handle) === FALSE) {
        yield fgetcsv($handle);
    }
    fclose($handle);
}

foreach (getRows($file) as $row) {
    print_r($row);
}

     上述示例一次只会为CSV文件中的一行分配内存,而不会把整个4GB的CSV文件都读取到内存中。

4、总结

     生成器是功能多样性和简洁性之间的折中方案,生成器只是向前进的迭代器,这意味着不能使用生成器在数据集中执行后退、快进或查找操作,只能让生成器计算并产出下一个值。迭代大型数据集或数列时最适合使用生成器,因为这样占用的系统内存最少。生成器也能完成迭代器能完成的简单任务,而且使用的代码更少。

     总而言之,生成器并没有为PHP添加新功能,不过使用生成器大大简化了某些任务,而且使用的内存更少,如果需要更多功能,例如在数据集中执行后退、快进以及查找功能,最好自己编写实现Iterator接口的类,或者使用PHP标准库(SPL)中某个原生的迭代器(http://php.net/manual/spl.iterators.php)。

免责声明:文章转载自《现代 PHP 新特性系列(四) —— 生成器的创建和使用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Postgresql在线备份和恢复layui下拉多选formSelects使用方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

PHP中获取远程文件的三种方法

1.file_get_contents <?php $url = 'http://www.xxx.com/'; $contents = file_get_contents($url); //如果出现中文乱码使用下面代码 //$getcontent = iconv(“gb2312″, “utf-8″,file_get_contents($url));...

微信退款流程,以及在过程中遇见的错误和解决方式(php 语言)

官方下载demo  1:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1    开发步骤  :    https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3    退款结果通知:https://pay.weixin.q...

PHP的深copy和浅copy

1、对象复制的由来   为什么对象会有“复制”这个概念,这与PHP5中对象的传值方式是密切相关的,让我们看看下面这段简单的代码 /** * 电视机类 */ class Television { /** * 屏幕高度 */...

zabbix准备:php安装

一.安装php依赖库 ftp://xmlsoft.org/libxml2/libxml2-2.9.3.tar.gz yum install python-devel -y cd /download/ wget -c ftp://xmlsoft.org/libxml2/libxml2-2.9.3.tar.gz tar xf libxml2-2.9.3...

netbeans工具使用xdebug断点调试php源码

对有有经验的程序员,使用echo、print_r()、print_f()、var_dump()等函数足以调试php代码,如果需要在IDE工具中使用断点调试,xdebug就是一个非常好的php调试工具。对于不熟悉的代码,可以用Xdebug一步一步的查看请求过程和代码执行过程。 准备工作本地部署的server环境,推荐用phpStudy(内置xdebug),开...

centos6 升级php版本

配置yum源 追加CentOS 6.8的epel及remi源。 # rpm -Uvh http://ftp.iij.ad.jp/pub/linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm # rpm -Uvh http://rpms.famillecollet.com/enterprise/remi...