Understanding PHP Generators
为什么需要谈论Generators,作为互联网应有,更快的速度意味着更好的用户体验,而Generators则是php中解决速度和内存的杀手锏
What are PHP Generators?
在php5.5中加入,generators解决了在一个loop中不需要在内存中构建整个数组的问题,可能这么说还不是很明了
talk is cheap, show me the code
我们来看下一段代码,保存为文件generator.php
<?php
function getRange ($max = 10) {
$array = [];
for ($i = 1; $i < $max; $i++) {
$array[] = $i;
}
return $array;
}
foreach (getRange(15) as $range) {
echo "Dataset {$range} <br>";
}
我们可以通过命令快速启动以个server
php -S localhost:8000
如果我们访问http://localhost:8000/generator.php我们会看到下面
Dataset 1
Dataset 2
Dataset 3
Dataset 4
Dataset 5
Dataset 6
Dataset 7
Dataset 8
Dataset 9
Dataset 10
Dataset 11
Dataset 12
Dataset 13
Dataset 14
代码很容易理解,也没什么问题,但是如果我们改动下代码:
<?php
foreach (getRange(PHP_INT_MAX) as $range) {
echo "Dataset {$range} <br>";
}
此时最大数变为了PHP_INT_MAX,再次运行就会出现下面的错误:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 134217736 bytes) in /private/tmp/php-generator/generator.php on line 7
超过最大内存了,此时一个可能的解决方案是修改php.ini 然后增加memory_limit。但是这个真能解决问题吗,我们是否真的希望一个脚本耗尽了我们的内存,显然是no!
Using Generators
我们还是实现同样的功能,这次我们创建一个generator函数
<?php
function getRange ($max = 10) {
for ($i = 1; $i < $max; $i++) {
yield $i;
}
}
foreach (getRange(PHP_INT_MAX) as $range) {
echo "Dataset {$range} <br>";
}
分析这次的getRange
函数,这次我们只是在loop中yield值,yield类似于return,不同之处在于yield只有在被返回的值需要的时候才会产生这个值,不会让整个返回集合都在内存中
此时我们再次访问http://localhost:8000/generator.php,只要给浏览器足够的时间,所有的数据都会返回
Why Do This?
我们的目标是提升速度,但是内存不增加。很多时候,我们在处理log文件的时候,经常会遇到内存耗尽的情况,使用generator就能很好的解决
Returning Keys
除了返回简单的值,我们还能返回键值对
<?php
function getRange ($max = 10) {
for ($i = 1; $i < $max; $i++) {
$value = $i * mt_rand();
yield $i => $value;
}
}
foreach (getRange(PHP_INT_MAX) as $range => $value) {
echo "Dataset {$range} has {$value} value<br>";
}
Sending Values to Generator
我们除了可以从generators中读数据,还能往里面写数据
<?php
function getRange ($max = 10) {
for ($i = 1; $i < $max; $i++) {
$injected = yield $i;
if ($injected === 'stop') return;
}
}
$generator = getRange(PHP_INT_MAX);
foreach ($generator as $range) {
if ($range === 10000) {
$generator->send('stop');
}
echo "Dataset {$range} <br>";
}
注意: 在generators中使用return,将会退出generator
Don't Misuse Generators
Generators用来高效使用内存,但是使用Generators并不意味着不会出现内存耗尽的情况,如果错误使用的话,也会遇到同样的问题。
Conclusion
Generators给我们提供了无法拒绝的性能提升。大多数时候,我们不需要高性能的服务器来解决问题,需要的这是我们重构下代码。Generators非常棒,我们应该多使用它。