PHP生成器是5.5.0引入的功能,生成器實際上就是簡單的迭代器。生成器會根據(jù)需求計算產(chǎn)出迭代的值,而標準的PHP迭代器經(jīng)常在內(nèi)存中執(zhí)行迭代操作,這要預(yù)先計算出數(shù)據(jù)集,性能較低。如果使用特定的防護計算大量數(shù)據(jù),可以使用生成器,即時計算并產(chǎn)出后續(xù)值,不占用內(nèi)存。
創(chuàng)建生成器
生成器從不返回值,只是產(chǎn)出值。
<?php function myGenerator() { yield 'v1'; yield 'v2'; yield 'v3'; }
調(diào)用生成器函數(shù)時,PHP會反悔一個屬于Generator類的對象。這個對象是可以foreach迭代的。每次迭代,PHP要求這個實例計算并提供下一個要迭代的值。
每次產(chǎn)出一個值,生成器的內(nèi)部狀態(tài)都會停頓。向生成器請求下一個值時,內(nèi)部狀態(tài)才會恢復(fù)。這種停頓-恢復(fù)的狀態(tài)會一直持續(xù)下去。
<?php foreach (myGenerator() as $yieldValue) { echo $yieldValue , PHP_EOL; }
使用生成器
<?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; }
上面的這個方法并沒有善用內(nèi)存,使用生成器只會為一個整數(shù)分配內(nèi)存。
<?php function makeRange($length) { for ($i = 0; $i < $length; $i++) { yield $i; } } foreach(makeRange(1000000) as $i) { echo $i, PHP_EOL; }
應(yīng)用場景
很多PHP開發(fā)者不了解生成器,其實主要是不了解應(yīng)用場景。那么,生成器在實際開發(fā)中有哪些應(yīng)用?
PHP開發(fā)很多時候都要讀取大文件,比如csv文件、txt文件,或者一些日志文件。這些文件如果很大,比如5個G。這時,直接一次性把所有的內(nèi)容讀取到內(nèi)存中計算不太現(xiàn)實。
這里生成器就可以派上用場啦。簡單看個例子:
<?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); }
這個例子中,生成器只會為CSV文件分配一行內(nèi)存,而不是讀入整個文件到內(nèi)存。使用生成器讀取文件,第一次讀取了第一行,第二次讀取了第二行,以此類推,每次被加載到內(nèi)存中的文字只有一行,大大的減小了內(nèi)存的使用。這樣,即使讀取上G的文本也不用擔心,完全可以像讀取很小文件一樣編寫代碼。