Generators
PHP generators are an underutilized yet remarkably helpful feature introduced in PHP 5.5.0 I think many PHP developers are unaware of generators because their purpose is not immediately obvious.generators are simple iterators.that’s it.
生成器
PHP生成器在5.5.0之前并未充分引入,很多PHP开发者并未察觉到PHP生成器的存在,因为他的作用并不显著,仅做为迭代器使用,仅此而已.
Unlike your standard PHP iterator,PHP generators don’t require you to implement the iterator interface in a heavyweight class.instead,generators compute and yield iteration values on-demand.this has profound implications for application performance.think about it.a standard PHP iterator often iterates in-memory,precomputed data sets.this is inefficient,especially with large and formulaic data sets that can be computed instead.this is why we use generators to compute and yield subsequent values on the fly without commandeering valuable memory.
与PHP标准迭代器不同,PHP生成器并不会包含重量级的迭代器实现类,反而生成器在计算时会忽略重复的计算并按需分配内存,这会极大的提高程序的运行效率,试想一个标准的PHP迭代器反复请求内存,将预先计算好的数据写入内存,这在大数据量和公式性的计算中显然是低效的,这就是为什么我们使用生成器进行迭代运算,这样可以忽略重复运算并避免强行占用大量的内存.
Create a generator
Generators are easy to create because they just PHP functions that use the yield keyword one or more times.unlike regular PHP functions,generators never return a value.they only yield values,example 2-15 shows a simple generator.
创建一个生成器
我们可以通过使用一个或多个yield关键词的函数轻松的创建生成器,PHP函数不会定期更新,生成器不会返回值,他们仅仅是隐藏的值,示例2-15展示了一个单独的生成器
<?php
Function myGenerator()
{
Yield ‘’value1;
Yield ‘’value2;
Yield ‘’value3;
}
Pretty simple,huh?when you invoke the generator function,PHP returns an object that belongs to the generator class.this object can be interated with the foreach() function.during each iteration,PHP asks the generator instance to compute and provide the next iteration value.what’s neat is that the generator pauses its internal state whennever it yields a value.the generator continues pausing and resuming until it reaches the end of its function definition or an empty return;statement.we can invoke and iterate the generator in Example 2-15 like this:
很简单不是吗?在你引用生成器函数的时候,PHP会返回一个属于生成器的对象,这个对象可以用foreach()函数迭代,在每一次迭代中,PHP会向生成器实例要求计算并提供下一次迭代的值,生成器内部的声明非常简洁,任何时候都只需隐藏值即可.生成器不断暂停,恢复直到返回了函数定义中的最后一个层级或返回空,我们可以在示例2-15中看到生成器的引用和声明
<?php
Foreach(myGenerator() as $yieldedvalue)
{
Echo $yieldedvalye,PHP_EOL;
}
This output
Value1
Value2
Value3
Use a generator
I like to demonstrate how a php generator saves memory by implementing a simple range() function.first,let’s do it the wrong way(example 2-16)
使用生成器
我将要使用一个单一的range()函数证明生成器如何在PHP实现中为我们节省内存,首先,我们需要一个错误的实现方法(实例2-16)
Example 2-16.range generator(bad)
<?php
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;
}
Example 2-16 makes poor use of memory.the makeRange() method in example 2-16 allocates one million integers into a precomputed array.a PHP generator can do the same thing while allocating memory for only one integer at any given time,as shown in Example 2-17
实例2-16的makRtange()方法使用了大量内存,在计算中为数组中分配一亿个整型的内存空间,PHP生成器做同样的事,只需要分配一个整型的内存空间,见实例2-17
Example 2-17 range generator(good)
<?php
Function makeRange($length)
{
For($i = 0;$i < $length;$i++)
{
Yield $i;
}
}
Foreach(makeRange(1000000) as $i)
{
Echo $i,PHP_EOF;
}
This is a contrived example.however,just imageine all of the potential data sets that you can compute.number sequences(e.g. fibonacci) are an obvious candidate.you can also iterate a stream resource.imagine you need to iterate a 4GB commaseparated value(CSV)file and your virtual private server(VPS)has only 1GB of memory available to PHP.there’s no way you can pull the entire file into memory.example 2-18 shows how we can use a generator instead!
这是一个虚构的实例,然而你可以想象成任何你可能会用到的数据计算.数序处理是一个显著的例子,你需要反复声明资源,你需要声明4GB用于处理用逗号分割的CSV文件,但是你的私有虚拟服务器的PHP只能使用1GB的内存,看上去你没有任何方法将这个文件放入内存处理.示例2-18将想你展示如何利用生成器完成这件事.
Example 2-18 CSV generator
<?php
Function getRows($file)
{
$handle = fopen($file,’rb’);
If($handle === false) throw new exception();
While(feof($handle) === false) yield fgetcsv($handle);
Fclose($handle);
}
Foreach(getRows(‘data.csv’) as $row) print_r($row);
This example allocates memory for only one CSV row at a time instead of reading the entire 4GB CSV file into memory.it also encapsulates the iteration implementation into a tidy package;this lets us quickly change how we get data(e.g.,CSV,XML,JSON)without interrupting our application code that iterates the data.
这个实例在读取4GB的CSV文件到内存时,能做到仅分配一行CSV文件的内存,他同样可以包含对包的完整的功能实现.他使得我们可以快速修改我们获取到的数据(例如 CSV,XML,JSON)而且可以在我们的应用代码里对数据进行完整的处理.
Generators are a tradeoff between versatillity and simplicity.generators are forward-only iterators.this means you can’t use a generator to rewind,fast-forward,or seek a data set.you can only ask a generator to compute and yield its next data sets with only a tiny a mount of system memory.they are also useful for accomplishing the same simple tasks as larger itertors with less code.
生成器是功能和简洁之间的权衡的结果,生成器只能向前迭代.这意味着你无法使用生成器进行回溯,快进或查找数据,你只能向迭代器请求迭代计算存储在系统内存中的下一次的迭代数据,但这对用简洁的代码完成大量的迭代计算同样很有用处.
Generators do not add functionality to PHP,you can do what generators do without a generator.however,generators greatly simply certain tasks while using less memory.if you require more versatillity to rewind,fast-forward,or seek through a data set,you’re better off writing a custom class that implements the iterator interface,or using one of PHP’s prebuilt standard PHP Library(SPL)iterators.
生成器并没有向PHP添加功能,你可以不通过生成器实现生成器的功能,生成器的伟大之处是用极少的内存实现功能,如果你需要对数据进行后退,快进或查找,你最好实现一个自定义类复写生成器接口或者用户PHP预建的标准库的迭代器.