ホームページ  >  記事  >  バックエンド開発  >  PHP7 のジェネレーターの新機能を理解します。

PHP7 のジェネレーターの新機能を理解します。

藏色散人
藏色散人転載
2021-09-03 16:04:001534ブラウズ

Generator delegate

公式ドキュメントの説明を簡単に翻訳します。

PHP7 では、ジェネレーターの委任 (yield from) を通じて、他のジェネレーター、反復可能オブジェクト、および配列を外部ジェネレーターに委任できます。外部ジェネレーターは、まず委任された値を順番に生成し、次にそれ自体で定義された値を生成し続けます。

yield from を使用すると、より明確なジェネレーターのネストを簡単に作成できます。複雑なシステムを作成するには、コードのネスト呼び出しが必要です。

上記の例:

<?php
function echoTimes($msg, $max) {
    for ($i = 1; $i <= $max; ++$i) {
        echo "$msg iteration $i\n";
        yield;
    }
}
 
function task() {
    yield from echoTimes(&#39;foo&#39;, 10); // print foo ten times
    echo "---\n";
    yield from echoTimes(&#39;bar&#39;, 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

当然のことながら、内部ジェネレーターは親ジェネレーターから送信された情報や例外も受け入れることができます。親子ジェネレータは双方向チャネルを確立します。早速、例を示します。

<?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(&#39;foo&#39;);
    echo "---\n";
    yield from echoMsg(&#39;bar&#39;);
}
$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 つの方法があります:

  1. $ret = Generator::getReturn() メソッドを使用します。
  2. Generator() 式から $ret = yield を使用します。

上記の例:

<?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(&#39;foo&#39;, 10);
    echo $end;
    $gen = echoTimes(&#39;bar&#39;, 5);
    yield from $gen;
    echo $gen->getReturn();
}

foreach (task() as $item) {
    ;
}

出力結果は投稿されません。誰もがそれを推測したはずです。

yield from と return を組み合わせることで、yield の書き方が私たちが普段書く同期モードのコードに近くなっていることがわかりますが、これが PHP にジェネレーター機能がある理由の 1 つです。

ノンブロッキング Web サーバー

2015 年に、「Using Coroutines to Implement Multi-Task Scheduling in PHP」という記事が Brother Bird のブログに再投稿されました。この記事では、PHP5 の反復ジェネレーターであるコルーチンを紹介し、シンプルなノンブロッキング Web サーバーを実装します。 (記事の最後にあるリンクを参照してください)

ここで、PHP7 のこれら 2 つの新機能を使用して、この Web サーバーを書き換えます。必要なコードは 100 行を超えるだけです。

コードは次のとおりです:

<?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;
});

以上がPHP7 のジェネレーターの新機能を理解します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。