從 PHP 5.5 開始,PHP 加入了一個新的特性,那就是 Generator
,中文譯爲生成器
。生成器能夠簡單地用來實現對象的迭代,讓咱們先從官方的一個小例子提及。php
在 PHP 中,咱們都知道,有一個函數叫作 range
,用來生成一個等差數列的數組,而後咱們能夠用這個數組進行 foreach
的迭代。具體就想這樣。數組
foreach (range(1, 100, 2) as $num) { echo $num . PHP_EOL; }
這一段代碼就會輸出首項爲 1,末項爲 100,公差爲 2 的等差數列。它的執行順序是這樣的。首先,range(1, 100, 2)
會生成一個數組,裏面存了上面那樣的一個等差數列,以後在 foreach
中對這個數組進行迭代。ide
那麼,這樣就會出現一個問題,若是我要生成 100 萬個數字呢?那咱們就要佔用上百兆內存。雖然如今內存很便宜,可是咱們也不能這麼浪費內存嘛。那麼這時,咱們的生成器就能夠排上用場了。考慮下面的代碼。函數
function xrange($start, $limit, $step = 1) { while ($start <= $limit) { yield $start; $start += $step; } } foreach (xrange(1, 100, 2) as $num) { echo $num . PHP_EOL; }
這段代碼所的出來的結果,和前面的那段代碼如出一轍,可是,它內部的原理是天翻地覆了。code
咱們剛纔說了,前面的代碼,range
會生成一個數組,而後 foreach
來迭代這個數組,從而取出某一個值。可是這段代碼呢,咱們從新定義了一個 xrange
函數,在函數中,咱們用了一個關鍵字 yield
。咱們都知道定義一個函數,但願它返回一個值得時候,用 return
來返回。那麼這個 yield
呢,也能夠返回一個值,可是,它和 return
是大相徑庭的。協程
使用 yield
關鍵字,可讓函數在運行的時候,中斷,同時會保存整個函數的上下文,返回一個 Generator
類型的對象。在執行對象的 next
方法時,會從新加載中斷時的上下文,繼續運行,直到出現下一個 yield
爲止,若是後面沒有再出現 yield
,那麼就認爲整個生成器結束了。對象
這樣,咱們上面的函數調用能夠等價地寫成這樣。內存
$nums = xrange(1, 100, 2); while ($nums->valid()) { echo $nums->current() . "\n"; $nums->next(); }
在這裏,$num
是一個 Generator
的對象。咱們在這裏看到三個方法,valid
、current
和 next
。當咱們函數執行完了,後面沒有 yield
中斷了,那麼咱們在 xrange
函數就執行完了,那麼 valid
方法就會變成 false
。而 current
呢,會返回當前 yield
後面的值,這是,生成器的函數會中斷。那麼在調用 next
方法以後,函數會繼續執行,直到下一個 yield
出現,或者函數結束。字符串
好了,到這裏,咱們看到了經過 yield
來「生成」一個值並返回。其實,yield
其實也能夠這麼寫 $ret = yield;
。同返回值同樣,這裏是將一個值在繼續執行函數的時候,傳值進函數,能夠經過 Generator::send($value)
來使用。例如。it
function sum() { $ret = yield; echo $ret . PHP_EOL; } $sum = sum(); $sum->send('I am from outside.');
這樣,程序就會打印出 send
方法傳進去的字符串了。在 yield
的兩邊能夠同時有調用。
function xrange($start, $limit, $step = 1) { while ($start <= $limit) { $ret = yield $start; $start += $step; echo $ret . PHP_EOL; } } $nums = xrange(1, 100, 2); while ($nums->valid()) { echo $nums->current() . "\n"; $nums->send($nums->current() + 1); }
而像這樣的使用,send()
能夠返回下一個 yield
的返回。
對於 yield
,咱們能夠這樣使用 yield $id => $value
,這是,咱們能夠經過 key
方法來獲取 $id
,而 current
方法返回的是 $value
。
這個方法,能夠幫咱們讓生成器從新開始執行並保存上下文,同時呢,會返回第一個 yield
返回的內容。在第一次執行 send
方法的時候,rewind
會被隱式調用。
這個方法,向生成器中,拋送一個異常。
yield
做爲 PHP 5.5 的新特性,讓咱們用了新的方法來高效地迭代數據。同時,咱們還可使用 yield
來實現協程。