php生成器是 PHP5.5.0引入的功能,往往没有被充分利用,其实这是非常有用的功能,很多开发者并不知道生成器,因为生成器的作用不是很明显,生成器就是简单的迭代器,仅此而已。
与标准的PHP迭代器不同,PHP生成器不要求实现 Iterator 接口,从而减轻了类的负担,生成器会根据需求计算并产出要迭代的值,这对应用的性能有重大影响。
注意: PHP生成器不能满足所有迭代的需求,如果不查询,生成器是不知道下一个要迭代的值是什么,生成器也不能后退或快进,生成器还是一次性的,无法多次迭代一个生成器。如果需要多次操作可以克隆或重建生成器。
创建生成器
生成器创建方式很简单,因为生成器就是PHP 函数,只不过要在函数内一次或多次使用 yield 关键字,与普通函数不同的是,生成器只产出值,从不返回值。如示例:
一个简单的生成器
function myGenerator(){
yield '冬瓜';
yield '香瓜';
yield '西瓜';
}
在调用 生成器函数时,PHP 会返回一个属于 Generator 类的对象 ,这个对象可以直接用 foreach() 函数迭代。每次迭代,PHP会要求 Generator 实例计算并提供下一个要迭代的值。生成器的优雅体现于,每次产出一个值之后,生成器的内部状态都会停顿,向生成器请求下一个值时,内部状态又会恢复。生成器的内部状态会一直在停顿和恢复之间切换,直接抵达函数定义体的末尾或遇到空的 return;
调用并迭代 上面定义的生成器:
加上分隔符 ‘-’ 可以更直观看到,每次调用只产出一个值。
public function test()
{
$v= [];
foreach ($this->myGenerator() as $value) {
$v[] = '-' .$value;
}
var_dump($v);
}
结果为:
array(3) {
[0]=>
string(7) "-冬瓜"
[1]=>
string(7) "-香瓜"
[2]=>
string(7) "-西瓜"
}
如打印生成器函数则为:
object(Generator)#1245 (0) {
}
使用生成器
下面实现一个简单的函数,用于生成一个范围内的数值,以此说明 PHP 生成器是如何节省内存的,首先,我们使用常规(错误)方式使现:
生成一个零至一百万范围内的数值
function makeRange($length) {
$dataset = [];
for ($i=0; $i< $length; $i++){
$dataset[] = $i;
}
return $dataset;
}
$customRange = makeRange(1000000);
foreach ($customRange as $value) {
echo $value;
}
以上的代码也能完成功能,但是并不是一个好的示范,代码并没有善用内存,makeRange() 函数要为预先创建的由一百万个整数组成的数组分配内存。而使用 PHP 生成器操作完成相同的功能,不需要为一百万个整数分配内存,仅为一个整数分配内存即可实现,示例:
function makeRange2($length){
for($i=0; $i<$length; $i++){
yield $i;
}
}
foreach (makeRange2(1000000) as $value) {
echo $value;
}
很明显的是,以上例题,PHP生成器能更轻松的实现,因为生成器不需要把一百万个数字加载到内存当中。
使用 PHP 生成器计算斐波那契数列也是一个很好的例子,我们还可以迭代流资源,假如我想要迭代一个大小为 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);
}
}
foreach (getRows('data.csv') as $row) {
print_r($row);
}
原文件和最终输出:
上述示例一次只会为CSV文件中的一行分配内存,而不会把整个 4GB 的 CSV 文件都读取到内存中,且上述代码把迭代的实现方式封装到一个简洁的包中,因此,可以很方便的调用。
生成器是功能多样性和简洁之间的折中方案。生成器是只能向前进的迭代器,这意味着不能使用生成器在数据集中执行后退、跳过、快进或查找操作,只能让生成器产生下一个值。迭代大型数据集或数列时最适合使用生成器,因为这样占用的系统内存量极少。
如果需要更多功能,例如在数据集执行后退、快进或查找操作,最好自己编写类,实现 Iterator 接口参考(http://php.net/manual/class.iterator.php)
更多示例参考 安东尼.费拉拉写的文章 What Generators Can Do For You (http://bit.ly/ircmaxwell)
————————————————
版权声明:本文为优快云博主「一筐大白菜啊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/sphinx1122/article/details/90302238