Maison > développement back-end > PHP7 > le corps du texte

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

藏色散人
Libérer: 2023-02-18 08:02:02
avant
1543 Les gens l'ont consulté

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) {
    ;
}
Copier après la connexion

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
Copier après la connexion

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
Copier après la connexion

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) {
    ;
}
Copier après la connexion

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;
});
Copier après la connexion

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!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal