PHP 7のジェネレータの新しい特性yield-from&&return-values


ジェネレータの委任
公式文書の説明を簡単に翻訳します.
PHP 7では、ジェネレータ依頼(yield from)により、他のジェネレータ、反復可能なオブジェクト、配列を外層ジェネレータに依頼することができる.外層のジェネレータは、yieldから委任された値を順次yieldに委任し、yield自体で定義された値を続行します.
yield fromを使用すると、比較的明確なジェネレータネストを記述するのに便利ですが、コードネスト呼び出しは複雑なシステムを記述するために必要です.前の例:
";
        yield;
    }
}
 
function task() {
    yield from echoTimes('foo', 10); // print foo ten times
    echo "---
"; yield from echoTimes('bar', 5); // print bar five times } foreach (task() as $item) { ; }

以上、出力されます.
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

もちろん、yield fromが親子ジェネレータに双方向のチャネルを確立するため、内部ジェネレータは親ジェネレータから送信された情報または異常を受け入れることもできます.あまり言わないで、前の例:
";
    }
}
function task2() {
    yield from echoMsg('foo');
    echo "---
"; 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キーに実行されたりすると、このジェネレータに値が返されます.この戻り値を取得するには、次の2つの方法があります.
  • $ret=Generator::getReturn()メソッドを使用します.
  • $ret=yield from Generator()式を使用します.

  • 前の例:
    ";
            yield;
        }
        return "$msg the end value : $i
    "; } 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サーバ
    2015年に戻って、鳥兄のブログに転載された「PHPで協程を使ってマルチタスクスケジューリングを実現する」.PHP 5の反復生成器,コプロセッサを紹介し,単純な非ブロックウェブサーバを実現した.(リンクは文末参照)
    PHP 7の2つの新しい特性を利用して、このwebサーバを書き換えるには、100行以上のコードしか必要ありません.
    コードは次のとおりです.
    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...
    "; $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 = <<

    参考資料
  • [1] http://www.php.net/manual/zh/...
  • [2] http://www.laruence.com/2015/...
  • [3] http://blog.csdn.net/u0101613...