Understanding PHP Generators
為什么需要談?wù)揋enerators,作為互聯(lián)網(wǎng)應(yīng)有,更快的速度意味著更好的用戶體驗(yàn),而Generators則是php中解決速度和內(nèi)存的殺手锏
What are PHP Generators?
在php5.5中加入,generators解決了在一個(gè)loop中不需要在內(nèi)存中構(gòu)建整個(gè)數(shù)組的問(wèn)題,可能這么說(shuō)還不是很明了
talk is cheap, show me the code
我們來(lái)看下一段代碼,保存為文件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>";
}
我們可以通過(guò)命令快速啟動(dòng)以個(gè)server
php -S localhost:8000
如果我們?cè)L問(wèn)http://localhost:8000/generator.php我們會(huì)看到下面
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
代碼很容易理解,也沒(méi)什么問(wèn)題,但是如果我們改動(dòng)下代碼:
<?php
foreach (getRange(PHP_INT_MAX) as $range) {
echo "Dataset {$range} <br>";
}
此時(shí)最大數(shù)變?yōu)榱?strong>PHP_INT_MAX,再次運(yùn)行就會(huì)出現(xiàn)下面的錯(cuò)誤:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 134217736 bytes) in /private/tmp/php-generator/generator.php on line 7
超過(guò)最大內(nèi)存了,此時(shí)一個(gè)可能的解決方案是修改php.ini 然后增加memory_limit。但是這個(gè)真能解決問(wèn)題嗎,我們是否真的希望一個(gè)腳本耗盡了我們的內(nèi)存,顯然是no!
Using Generators
我們還是實(shí)現(xiàn)同樣的功能,這次我們創(chuàng)建一個(gè)generator函數(shù)
<?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函數(shù),這次我們只是在loop中yield值,yield類似于return,不同之處在于yield只有在被返回的值需要的時(shí)候才會(huì)產(chǎn)生這個(gè)值,不會(huì)讓整個(gè)返回集合都在內(nèi)存中
此時(shí)我們?cè)俅卧L問(wèn)http://localhost:8000/generator.php,只要給瀏覽器足夠的時(shí)間,所有的數(shù)據(jù)都會(huì)返回
Why Do This?
我們的目標(biāo)是提升速度,但是內(nèi)存不增加。很多時(shí)候,我們?cè)谔幚韑og文件的時(shí)候,經(jīng)常會(huì)遇到內(nèi)存耗盡的情況,使用generator就能很好的解決
Returning Keys
除了返回簡(jiǎn)單的值,我們還能返回鍵值對(duì)
<?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中讀數(shù)據(jù),還能往里面寫數(shù)據(jù)
<?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,將會(huì)退出generator
Don't Misuse Generators
Generators用來(lái)高效使用內(nèi)存,但是使用Generators并不意味著不會(huì)出現(xiàn)內(nèi)存耗盡的情況,如果錯(cuò)誤使用的話,也會(huì)遇到同樣的問(wèn)題。
Conclusion
Generators給我們提供了無(wú)法拒絕的性能提升。大多數(shù)時(shí)候,我們不需要高性能的服務(wù)器來(lái)解決問(wèn)題,需要的這是我們重構(gòu)下代碼。Generators非常棒,我們應(yīng)該多使用它。