Generator-Delegat
Übersetzen Sie einfach die Beschreibung der offiziellen Dokumentation:
PHP7, über den Generator-Delegaten (Ausbeute von), Sie können Delegieren Sie andere Generatoren, iterierbare Objekte und Arrays an äußere Generatoren. Der äußere Generator liefert zunächst nacheinander den delegierten Wert und dann weiterhin den in ihm selbst definierten Wert.
Die Verwendung von yield from kann es uns erleichtern, klarere Generatorverschachtelungen zu schreiben, und Codeverschachtelungsaufrufe sind zum Schreiben komplexer Systeme erforderlich.
Das obige Beispiel:
<?php function 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) { ; }
Das Obige gibt Folgendes aus:
foo iteration 1 foo iteration 2 foo iteration 3 foo iteration 4 foo iteration 5 foo iteration 6 foo iteration 7 foo iteration 8 foo iteration 9 foo iteration 10 --- bar iteration 1 bar iteration 2 bar iteration 3 bar iteration 4 bar iteration 5
Natürlich kann der interne Generator auch von seinem übergeordneten Generator gesendete Informationen oder Ausnahmen akzeptieren, da yield from einen Zwei-Wege-Kanal für die übergeordneten und untergeordneten Generatoren herstellt. Hier ist ohne weiteres ein Beispiel:
<?php function 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
Die Ausgabe ist die gleiche wie im vorherigen Beispiel.
Generator-Rückgabewert
Wenn der Generator iteriert wird oder zum Schlüsselwort „return“ läuft, gibt der Generator einen Wert zurück.
Es gibt zwei Möglichkeiten, diesen Rückgabewert zu erhalten:
- Verwenden Sie die Methode $ret = Generator::getReturn().
- Verwenden Sie den Ausdruck $ret = yield from Generator().
Das obige Beispiel:
<?php function 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) { ; }
Das Ausgabeergebnis wird nicht veröffentlicht, jeder muss es erraten haben.
Sie können sehen, dass die Kombination aus „Yield from“ und „Return“ dazu führt, dass die Schreibweise von Yield eher dem synchronen Moduscode ähnelt, den wir normalerweise schreiben. Dies ist schließlich einer der Gründe, warum PHP über die Generatorfunktion verfügt.
Ein nicht blockierender Webserver
Bereits im Jahr 2015 wurde ein Artikel „Using Coroutines to Implement Multi-Task Scheduling in PHP“ auf dem Blog von Bruder Niao erneut veröffentlicht. Der Artikel stellt den iterativen Generator Coroutine von PHP5 vor und implementiert einen einfachen nicht blockierenden Webserver. (Siehe den Link am Ende des Artikels)
Jetzt nutzen wir diese beiden neuen Funktionen in PHP7, um diesen Webserver neu zu schreiben, der nur mehr als 100 Zeilen Code benötigt.
Der Code lautet wie folgt:
<?php class 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 = <<<RES HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 12 Connection: close hello world! RES; return $response; });