Maison >développement back-end >PHP7 >Vous faire comprendre les nouvelles fonctionnalités des générateurs en PHP7

Vous faire comprendre les nouvelles fonctionnalités des générateurs en PHP7

藏色散人
藏色散人avant
2021-09-03 16:04:001576parcourir

Délégué du générateur

Traduisez simplement la description de la documentation officielle :

PHP7, via le délégué du générateur (yield from), Vous pouvez déléguez d'autres générateurs, objets itérables et tableaux à des générateurs externes. Le générateur externe produira d’abord la valeur déléguée de manière séquentielle, puis continuera à produire la valeur définie en lui-même.

L'utilisation de rendement from peut nous permettre d'écrire plus facilement une imbrication de générateur plus claire, et les appels d'imbrication de code sont nécessaires pour écrire des systèmes complexes.

L'exemple ci-dessus :

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

Ce qui précède affichera :

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

Naturellement, le générateur interne peut également accepter des informations ou des exceptions envoyées par son générateur parent, car le rendement de établit un canal bidirectionnel pour les générateurs parent et enfant. Sans plus tarder, voici un exemple :

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

Le résultat est le même que l’exemple précédent.

Valeur de retour du générateur

Si le générateur est itéré ou exécute le mot-clé return, le générateur renverra une valeur.
Il existe deux façons d'obtenir cette valeur de retour :

  1. Utilisez la méthode $ret = Generator::getReturn().
  2. Utilisez l'expression $ret = rendement de Generator().

L'exemple ci-dessus :

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

Le résultat de la sortie ne sera pas affiché, tout le monde a dû l'avoir deviné.

Vous pouvez voir que la combinaison de rendement depuis et retour rend la façon d'écrire rendement plus proche du code en mode synchrone que nous écrivons habituellement. Après tout, c'est l'une des raisons pour lesquelles PHP dispose de la fonctionnalité générateur.

Un serveur Web non bloquant

En 2015, un article "Utiliser des coroutines pour implémenter la planification multi-tâches en PHP" a été republié sur le blog de Brother Niao. L'article présente le générateur itératif de PHP5, la coroutine, et implémente un simple serveur Web non bloquant. (Voir le lien en fin d'article)

Nous utilisons désormais ces deux nouvelles fonctionnalités de PHP7 pour réécrire ce serveur web, qui ne nécessite que plus de 100 lignes de code.

Le code est le suivant :

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer