Home  >  Article  >  Backend Development  >  New features of generators in PHP7: generator delegate (yield-from) & return value (return-value)

New features of generators in PHP7: generator delegate (yield-from) & return value (return-value)

不言
不言Original
2018-08-04 09:36:131973browse

This article introduces to you the new features of generators in PHP7: generator delegation (yield-from) & return value (return-value). It has certain reference value. Friends in need can refer to it. I hope it helps you.

Generator delegate

Simply translate the description of the official document:

In PHP7, through the generator delegate (yield from), other Generators, iterable objects, and arrays delegate to outer generators. The outer generator will first sequentially yield the delegated value, and then continue to yield the value defined in itself.

Using yield from can make it easier for us to write clearer generator nesting, and code nesting calls are necessary for writing complex systems.
Above example:

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

The above will output:

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

Naturally, the internal generator can also accept information or exceptions sent by its parent generator, because yield from is a parent-child generator Create a two-way channel. Without further ado, here’s an example:

<?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

The output is the same as the previous example.

Generator return value

If the generator is iterated, or runs to the return keyword, the generator will return a value.
There are two ways to obtain this return value:

  1. Use the $ret = Generator::getReturn() method.

  2. Use $ret = yield from Generator() expression.

The above example:

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

The output result will not be posted, everyone must have guessed it.

You can see that the combination of yield from and return makes the writing method of yield more like the synchronous mode code we usually write. After all, this is one of the reasons why PHP has the generator feature.

A non-blocking web server

Now we use these two new features in PHP7 to rewrite this web server, which only requires more than 100 lines of code.

The code is as follows:

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

Recommended related articles:

Simple analysis of PHP generator Generators

The above is the detailed content of New features of generators in PHP7: generator delegate (yield-from) & return value (return-value). For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn