說到php中的Generator(生成器),有人可能會想到協程,這裡我們先不說php如何實現協程,我們探究下Generator的執行過程。
Generator是通過yield實現,yield 關鍵字是php5.5版本推出的一個特性。 首先,看下面的代碼:
<?php
function gen(){
while(true){
yield "gen\n";
}
}
$gen = gen();
echo "Generator";
如果沒有了解過yield的話,你會認為上面代碼執行的結果是:死循環。但實際上,它會echo出Generator。
到這裡,也許你會覺得奇怪,yield怎麼可以結束循環?下面就為大家說明一下:
Generator提供的方法:
Generator::current — 返回當前產生的值
Generator::key — 返回當前產生的鍵
Generator::next — 生成器繼續執行
Generator::rewind — 重置迭代器
Generator::send — 向生成器中傳入一個值
Generator::throw — 向生成器中拋入一個異常
Generator::valid — 檢查迭代器是否被關閉
Generator::__wakeup — 序列化回調
生成器提供了一種更容易的方法來實現簡單的對象迭代(迭代器),相比較定義類實現 Iterator 接口的方式,性能開銷和複雜性大大降低。
大家看下這個列子:
function gen(){
for($i=0;$i<5;$i++){
echo (yield $i).$i.'<br/>';
}
}
$gen = gen();
foreach($gen as $k=>$v){
echo "{$k}---{$v}".'<br/>';
}
結果:
從上面的結果,我們可以分析出以下幾點:1當Generator對象被foreach的時候,內部的valid,current,key方法會依次被調用,其返回值是foreach語句的value和key。2循環的終止條件則根據valid方法的返回而定。如果返回的是true則繼續循環,如果是false則終止整個循環,結束遍歷。3一次循環體結束之後,將調用next進行下一次的循環直到valid返回false。而rewind方法則是在整個循環開始前被調用(也就是生成Generator對象時),這樣保證了我們多次遍歷得到的結果都是一致的。
下面我們來證明一下這個流程:
$gen = gen();echo $gen->key();//結果是0,生成Generator對象時,rewind已經執行。echo $gen->key().'----'.$gen->current();// 0----0 var_dump($gen->next());//var_dump值是null,但是還會echo出多一個0;這個0是怎樣來的呢?原因是:next()執行後,第1個yield到第二個yieldz之間的的語法被執行,即是:echo (yield $i).$i.'<br/>';由於next()是沒有返回值,即(yield $i)這個表達式沒有值,而$i的值是0;echo $gen->key().'----'.$gen->current();// 1----1 目前是第2個yield
上面這個例子可以證明,Generator內部的流程,特別注意next()的理解。
最後,我們說一下,send():
官方解析:向生成器中傳入一個值,並且當做 yield 表達式的結果,然後繼續執行生成器。如果當這個方法被調用時,生成器不在 yield 表達式,那麼在傳入值之前,它會先運行到第一個 yield 表達式。
翻譯下的結論是:send()方法主要用於發送數據給當前yield,即yield表達式被當作一個值被替換,且繼續執行下一個yield,即next()
證明例子:$gen = gen();$gen->send(666);//6660
6660結果分析:首先把666代替當前yield表達式的值,然後執行next(),即運行echo (yield $i).$i.'<br/>',當前yield是666,所以最終結果是:6660。注意與next()的區別!!!
總結:1.yield只能用於函數內部,在非函數內部運用會拋出錯誤。2.如果函數包含了yield關鍵字的,那麼函數執行後的返回值永遠都是一個Generator對象。3.如果函數內部同事包含yield和return 該函數的返回值依然是Generator對象,但是在生成Generator對象時,return語句後的代碼被忽略。4.Generator類實現了Iterator接口。5.可以通過返回的Generator對象內部的方法,獲取到函數內部yield後面表達式的值。6.可以通過Generator的send方法給yield 關鍵字賦一個值。7.一旦返回的Generator對象被遍歷完成,便不能調用他的rewind方法來重置。8.Generator對象不能被clone關鍵字克隆 。
實際應用:1.協程2.Genenrator返回的是迭代器,在處理大數據的時候不用一次性的加載到內存中。