在PHP5.5.0版本中,新增了生成器*(Generators)特性,用於簡化實現迭代器接口(Iterator)*建立簡單的迭代器的複雜性。php
經過生成器,咱們能夠輕鬆的使用foreach
迭代一系列的數據,而不須要事先在內存中構建要被迭代的對象,大大減小了內存開銷。html
當生成器函數被調用的時候,它會返回一個可迭代的對象,當對該對象進行迭代的時候,PHP將會在須要的時候調用生成器函數,而且在生成器使用新增的關鍵字yield
產生一個新的值的時候,保存迭代器內部的狀態。迭代器沒有新的值須要產生的時候,生成器函數就能夠直接退出,外部函數繼續執行。git
注意,在生成器函數中,不能使用
return
語句返回值,使用return
返回值的話會產生編譯器錯誤。可是,使用空的return
是能夠的,它會使迭代器終止。github
生成器函數與普通函數同樣的,惟一的區別函數內使用了yield
關鍵字。yield
語句能夠說是生成器函數的核心,簡單來講,yield
就像return
語句同樣,區別是return
語句返回後函數就結束了,而使用yield
返回後,只是暫停了函數的執行,轉到外部函數繼續執行,下次調用生成器函數的時候,繼續執行生成器函數內部的代碼。數組
####一個簡單的例子 - 生成器版本的range函數函數
一個簡單的例子是使用foreach
迭代函數range
的返回值,若是調用的是range(0, 1000000)
的話,將會消耗超過100M的內存。而使用生成器的話,可能只須要消耗1KB內存都不到。oop
<?php function xrange($start, $end) { if ($start > $end) { throw new RuntimeException("起始值不能大於截止值"); } for ($i = $start; $i <= $end; $i += 1) { // 使用yield關鍵字,每次到這裏函數都會返回$i的值,而且控制權交給外部函數繼續執行 yield $i; } } foreach (xrange(1, 9) as $number) { echo "$number "; }
上面的例子輸出以下:.net
上述例子中,咱們建立了一個名爲xrange
的函數,函數中使用yield
不斷產生返回值,而調用xrange(1, 9)
將會建立一個生成器對象。咱們能夠修改foreach
這一行打印出xrange
對象看看code
... $xrange_res = xrange(1, 9); var_dump($xrange_res); foreach( $xrange_res as $number){ ...
輸出htm
能夠看出,執行xrange(1, 9)
的時候確實是返回了一個Generator
對象。
####使用Generator對象的send方法
在上面的例子中,咱們使用yield
語句的時候都是做爲單獨的一行語句執行的,也就是yield
語句產生結果給外部,那麼在迭代過程當中有沒有辦法從生成器函數外部獲取值呢?
辦法老是有的,由於調用生成器函數後返回的是一個Generator
對象,所以咱們能夠經過調用該對象的send
方法從外部給生成器函數傳遞一個值,在調用send
方法以後,yield
會收到send
函數發送的值。
<?php function gen() { $ret = (yield 'yield1'); var_dump("-->" . $ret); $ret = (yield 'yield2'); var_dump("-->" . $ret); } $gen = gen(); var_dump($gen->current()); var_dump($gen->send('ret1')); var_dump($gen->send('ret2'));
輸出:
這裏咱們首先建立了名爲gen
的生成器對象,而後打印$gen->current()
方法的返回值,該返回值就是迭代器第一次迭代時產生的當前值,所以輸出了yield1
。
接下來咱們調用了$gen->send('ret')
方法,這時,生成器內第一個yield
語句返回該方法傳遞的值ret1
,所以輸出了$ret
的值爲ret1
。
接着因爲生成器內部執行到了第三條語句$ret = (yield 'yield2')
,所以外部的第二個var_dump
輸出了yield2
。最後調用$gen->send('ret2')
與第一次相似,不過此次生成器內部調用yield
以後已經沒有yield
了,所以返回的是NULL
。
注意,這裏的
$ret = (yield 'yield2')
語句中,使用括號包含了yield 'yield2'
語句,這裏是必須的,若是在表達式上下文中使用yield
,必須將yield
放在括號內,不然會報錯。
####返回關聯數組
前面的例子中,咱們使用yield
關鍵字返回的老是單個值,實際上PHP也對返回關聯數組提供了支持,基本語法:
yield key => val
使用該語法格式能夠在foreach的時候,返回與遍歷管理數組相同的結果。
<?php function gen2() { $array = [ 'username' => 'mylxsw', 'site' => 'http://aicode.cc' ]; foreach ($array as $key => $val) { yield $key => $val; } } foreach(gen2() as $key => $val) { var_dump($key . ' : ' . $val); }
輸出:
####使用引用
咱們還可讓生成器以引用的方式返回數據,這樣就能夠在生成器外部直接修改生成器內部數據的值。
<?php function &gen_reference() { $value = 3; while ($value > 0) { yield $value; } } foreach (gen_reference() as &$number) { echo (--$number).'... '; }
上述例子中,須要注意的是,生成器函數的定義和遍歷的時候使用了&$number
。
最後,生成器與自定義的迭代器對象是不徹底相同的,生成器一旦開始迭代,就不能再rewind
了,只能一直向前迭代,直到迭代完成。若是但願屢次迭代一個生成器對象的話,能夠屢次調用生成器函數建立新的生成器對象或者是使用clone關鍵字。
參考: