這篇文章給大家介紹的內容是關於PHP7中產生器的新特性:產生器委託( yield-from )&傳回值(return-value),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所協助。
產生器委託
簡單地翻譯官方文檔的描述:
PHP7中,通過產生器委託(yield from),可以將其他產生器、可迭代的對象、數組委託給外層產生器。外層的產生器會先順序 yield 委託出來的值,然後繼續 yield 本身中定義的值。
利用 yield from 可以方便我們編寫比較清晰產生器嵌套,而代碼嵌套調用是編寫複雜系統所必需的。
上例子:
<?phpfunction echoTimes($msg, $max) { for ($i = 1; $i <= $max; ++$i) { echo "$msg iteration $i\n"; yield; }} function task() { yield from echoTimes('foo', 10); // print foo ten times echo "---\n"; yield from echoTimes('bar', 5); // print bar five times}foreach (task() as $item) { ;}
以上將輸出:
foo iteration 1foo iteration 2foo iteration 3foo iteration 4foo iteration 5foo iteration 6foo iteration 7foo iteration 8foo iteration 9foo iteration 10---bar iteration 1bar iteration 2bar iteration 3bar iteration 4bar iteration 5
自然,內部產生器也可以接受它的父產生器發送的資訊或者異常,因為 yield from 為父子產生器建立一個雙向的通道。不多說,上例子:
<?phpfunction echoMsg($msg) { while (true) { $i = yield; if($i === null){ break; } if(!is_numeric($i)){ throw new Exception("Hoo! must give me a number"); } echo "$msg iteration $i\n"; }}function task2() { yield from echoMsg('foo'); echo "---\n"; yield from echoMsg('bar');}$gen = task2();foreach (range(1,10) as $num) { $gen->send($num);}$gen->send(null);foreach (range(1,5) as $num) { $gen->send($num);}//$gen->send("hello world"); //try it ,gay
輸出和上個例子是一樣的。
產生器傳回值
如果產生器被迭代完成,或者運行到 return 關鍵字,是會給這個產生器傳回值的。
可以有兩種方法擷取這個傳回值:
使用 $ret = Generator::getReturn() 方法。
使用 $ret = yield from Generator() 運算式。
上例子:
<?phpfunction echoTimes($msg, $max) { for ($i = 1; $i <= $max; ++$i) { echo "$msg iteration $i\n"; yield; } return "$msg the end value : $i\n";}function task() { $end = yield from echoTimes('foo', 10); echo $end; $gen = echoTimes('bar', 5); yield from $gen; echo $gen->getReturn();}foreach (task() as $item) { ;}
輸出結果就不貼了,想必大家都猜到。
可以看到 yield from 和 return 結合使得 yield 的寫法更像平時我們寫的同步模式的代碼了,畢竟,這就是 PHP 出產生器特性的原因之一呀。
一個非阻塞的web伺服器
現在我們利用 PHP7 中的這兩個新特性重寫這個 網頁伺服器,只需要 100 多行代碼。
代碼如下:
<?phpclass CoSocket{ protected $masterCoSocket = null; public $socket; protected $handleCallback; public $streamPoolRead = []; public $streamPoolWrite = []; public function __construct($socket, CoSocket $master = null) { $this->socket = $socket; $this->masterCoSocket = $master ?? $this; } public function accept() { $isSelect = yield from $this->onRead(); $acceptS = null; if ($isSelect && $as = stream_socket_accept($this->socket, 0)) { $acceptS = new CoSocket($as, $this); } return $acceptS; } public function read($size) { yield from $this->onRead(); yield ($data = fread($this->socket, $size)); return $data; } public function write($string) { yield from $this->onWriter(); yield fwrite($this->socket, $string); } public function close() { unset($this->masterCoSocket->streamPoolRead[(int)$this->socket]); unset($this->masterCoSocket->streamPoolWrite[(int)$this->socket]); yield ($success = @fclose($this->socket)); return $success; } public function onRead($timeout = null) { $this->masterCoSocket->streamPoolRead[(int)$this->socket] = $this->socket; $pool = $this->masterCoSocket->streamPoolRead; $rSocks = []; $wSocks = $eSocks = null; foreach ($pool as $item) { $rSocks[] = $item; } yield ($num = stream_select($rSocks, $wSocks, $eSocks, $timeout)); return $num; } public function onWriter($timeout = null) { $this->masterCoSocket->streamPoolWrite[(int)$this->socket] = $this->socket; $pool = $this->masterCoSocket->streamPoolRead; $wSocks = []; $rSocks = $eSocks = null; foreach ($pool as $item) { $wSocks[] = $item; } yield ($num = stream_select($rSocks, $wSocks, $eSocks, $timeout)); return $num; } public function onRequest() { /** @var self $socket */ $socket = yield from $this->accept(); if (empty($socket)) { return false; } $data = yield from $socket->read(8192); $response = call_user_func($this->handleCallback, $data); yield from $socket->write($response); return yield from $socket->close(); } public static function start($port, callable $callback) { echo "Starting server at port $port...\n"; $socket = @stream_socket_server("tcp://0.0.0.0:$port", $errNo, $errStr); if (!$socket) throw new Exception($errStr, $errNo); stream_set_blocking($socket, 0); $coSocket = new self($socket); $coSocket->handleCallback = $callback; function gen($coSocket) { /** @var self $coSocket */ while (true) yield from $coSocket->onRequest(); } foreach (gen($coSocket) as $item){}; }}CoSocket::start(8000, function ($data) { $response = <<<RESHTTP/1.1 200 OKContent-Type: text/plainContent-Length: 12Connection: closehello world!RES; return $response;});