>  기사  >  백엔드 개발  >  PHP7 생성기의 새로운 기능을 이해하도록 안내합니다.

PHP7 생성기의 새로운 기능을 이해하도록 안내합니다.

藏色散人
藏色散人앞으로
2021-09-03 16:04:001524검색

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

당연히 내부 생성기는 상위 생성기가 보낸 정보나 예외도 허용할 수 있습니다. 왜냐하면 Yield from은 상위 및 하위 생성기에 대한 양방향 채널을 설정하기 때문입니다. 더 이상 고민하지 말고 예를 들어보겠습니다.

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

출력은 이전 예와 동일합니다.

생성기 반환 값

생성기가 반복되거나 반환 키워드로 실행되면 생성기가 값을 반환합니다.
이 반환 값을 얻는 방법에는 두 가지가 있습니다.

  1. $ret = Generator::getReturn() 메서드를 사용하세요.
  2. $ret = Generator() 표현식의 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에 생성기 기능이 있는 이유 중 하나입니다.

비차단 웹 서버

2015년에 "코루틴을 사용하여 PHP에서 다중 작업 예약 구현"이라는 기사가 Brother Niao의 블로그에 다시 게시되었습니다. 이 기사에서는 PHP5의 반복 생성기인 코루틴을 소개하고 간단한 비차단 웹 서버를 구현합니다. (기사 마지막에 있는 링크를 참조하세요)

이제 우리는 PHP7의 두 가지 새로운 기능을 사용하여 이 웹 서버를 다시 작성하는데, 이는 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제