zoukankan      html  css  js  c++  java
  • PHP7中生成器的新特性(生成器委托)=> yield-from && return-values

     

    生成器委托

    简单地翻译官方文档的描述:

    PHP7中,通过生成器委托(yield from),可以将其他生成器、可迭代的对象、数组委托给外层生成器。外层的生成器会先顺序 yield 委托出来的值,然后继续 yield 本身中定义的值。

    利用 yield from 可以方便我们编写比较清晰生成器嵌套,而代码嵌套调用是编写复杂系统所必需的。
    上例子:

     1 <?php
     2 function echoTimes($msg, $max) {
     3     for ($i = 1; $i <= $max; ++$i) {
     4         echo "$msg iteration $i
    ";
     5         yield;
     6     }
     7 }
     8  
     9 function task() {
    10     yield from echoTimes('foo', 10); // print foo ten times
    11     echo "---
    ";
    12     yield from echoTimes('bar', 5); // print bar five times
    13 }
    14 
    15 foreach (task() as $item) {
    16     ;
    17 }

    以上将输出:

     1 foo iteration 1
     2 foo iteration 2
     3 foo iteration 3
     4 foo iteration 4
     5 foo iteration 5
     6 foo iteration 6
     7 foo iteration 7
     8 foo iteration 8
     9 foo iteration 9
    10 foo iteration 10
    11 ---
    12 bar iteration 1
    13 bar iteration 2
    14 bar iteration 3
    15 bar iteration 4
    16 bar iteration 5

    自然,内部生成器也可以接受它的父生成器发送的信息或者异常,因为 yield from 为父子生成器建立一个双向的通道。不多说,上例子:

     1 <?php
     2 function echoMsg($msg) {
     3     while (true) {
     4         $i = yield;
     5         if($i === null){
     6             break;
     7         }
     8         if(!is_numeric($i)){
     9             throw new Exception("Hoo! must give me a number");
    10         }
    11         echo "$msg iteration $i
    ";
    12     }
    13 }
    14 function task2() {
    15     yield from echoMsg('foo');
    16     echo "---
    ";
    17     yield from echoMsg('bar');
    18 }
    19 $gen = task2();
    20 foreach (range(1,10) as $num) {
    21     $gen->send($num);
    22 }
    23 $gen->send(null);
    24 foreach (range(1,5) as $num) {
    25     $gen->send($num);
    26 }
    27 //$gen->send("hello world"); //try it ,gay

    输出和上个例子是一样的。

    生成器返回值

    如果生成器被迭代完成,或者运行到 return 关键字,是会给这个生成器返回值的。
    可以有两种方法获取这个返回值:

    1. 使用 $ret = Generator::getReturn() 方法。
    2. 使用 $ret = yield from Generator() 表达式。

    上例子:

     1 <?php
     2 function echoTimes($msg, $max) {
     3     for ($i = 1; $i <= $max; ++$i) {
     4         echo "$msg iteration $i
    ";
     5         yield;
     6     }
     7     return "$msg the end value : $i
    ";
     8 }
     9 
    10 function task() {
    11     $end = yield from echoTimes('foo', 10);
    12     echo $end;
    13     $gen = echoTimes('bar', 5);
    14     yield from $gen;
    15     echo $gen->getReturn();
    16 }
    17 
    18 foreach (task() as $item) {
    19     ;
    20 }

    输出结果就不贴了,想必大家都猜到。

    可以看到 yield from 和 return 结合使得 yield 的写法更像平时我们写的同步模式的代码了,毕竟,这就是 PHP 出生成器特性的原因之一呀。

    一个非阻塞的web服务器

    时间回到2015年,鸟哥博客上转载的一篇《 在PHP中使用协程实现多任务调度》。文章介绍了PHP5 的迭代生成器,协程,并实现了一个简单的非阻塞 web 服务器。(链接见文末引用)

    现在我们利用 PHP7 中的这两个新特性重写这个 web 服务器,只需要 100 多行代码。

    代码如下:

      1 <?php
      2 
      3 class CoSocket
      4 {
      5     protected $masterCoSocket = null;
      6     public $socket;
      7     protected $handleCallback;
      8     public $streamPoolRead = [];
      9     public $streamPoolWrite = [];
     10 
     11     public function __construct($socket, CoSocket $master = null)
     12     {
     13         $this->socket = $socket;
     14         $this->masterCoSocket = $master ?? $this;
     15     }
     16 
     17     public function accept()
     18     {
     19         $isSelect = yield from $this->onRead();
     20         $acceptS = null;
     21         if ($isSelect && $as = stream_socket_accept($this->socket, 0)) {
     22             $acceptS = new CoSocket($as, $this);
     23         }
     24         return $acceptS;
     25     }
     26 
     27     public function read($size)
     28     {
     29         yield from $this->onRead();
     30         yield ($data = fread($this->socket, $size));
     31         return $data;
     32     }
     33 
     34     public function write($string)
     35     {
     36         yield from $this->onWriter();
     37         yield fwrite($this->socket, $string);
     38     }
     39 
     40     public function close()
     41     {
     42         unset($this->masterCoSocket->streamPoolRead[(int)$this->socket]);
     43         unset($this->masterCoSocket->streamPoolWrite[(int)$this->socket]);
     44         yield ($success = @fclose($this->socket));
     45         return $success;
     46     }
     47 
     48     public function onRead($timeout = null)
     49     {
     50         $this->masterCoSocket->streamPoolRead[(int)$this->socket] = $this->socket;
     51         $pool = $this->masterCoSocket->streamPoolRead;
     52         $rSocks = [];
     53         $wSocks = $eSocks = null;
     54         foreach ($pool as $item) {
     55             $rSocks[] = $item;
     56         }
     57         yield ($num = stream_select($rSocks, $wSocks, $eSocks, $timeout));
     58         return $num;
     59     }
     60 
     61     public function onWriter($timeout = null)
     62     {
     63         $this->masterCoSocket->streamPoolWrite[(int)$this->socket] = $this->socket;
     64         $pool = $this->masterCoSocket->streamPoolRead;
     65         $wSocks = [];
     66         $rSocks = $eSocks = null;
     67         foreach ($pool as $item) {
     68             $wSocks[] = $item;
     69         }
     70         yield ($num = stream_select($rSocks, $wSocks, $eSocks, $timeout));
     71         return $num;
     72     }
     73 
     74     public function onRequest()
     75     {
     76         /** @var self $socket */
     77         $socket = yield from $this->accept();
     78         if (empty($socket)) {
     79             return false;
     80         }
     81         $data = yield from $socket->read(8192);
     82         $response = call_user_func($this->handleCallback, $data);
     83         yield from $socket->write($response);
     84         return yield from $socket->close();
     85     }
     86 
     87     public static function start($port, callable $callback)
     88     {
     89         echo "Starting server at port $port...
    ";
     90         $socket = @stream_socket_server("tcp://0.0.0.0:$port", $errNo, $errStr);
     91         if (!$socket) throw new Exception($errStr, $errNo);
     92         stream_set_blocking($socket, 0);
     93         $coSocket = new self($socket);
     94         $coSocket->handleCallback = $callback;
     95         function gen($coSocket)
     96         {
     97             /** @var self $coSocket */
     98             while (true) yield from $coSocket->onRequest();
     99         }
    100         foreach (gen($coSocket) as $item){};
    101     }
    102 }
    103 
    104 CoSocket::start(8000, function ($data) {
    105     $response = <<<RES
    106 HTTP/1.1 200 OK
    107 Content-Type: text/plain
    108 Content-Length: 12
    109 Connection: close
    110 
    111 hello world!
    112 RES;
    113     return $response;
    114 });
  • 相关阅读:
    如何查看MySQL的当前存储引擎?
    避免生产环境执行更新删除语句忘记加where条件的解决方案
    物联网发展的现状
    目前行业内比较流行的开源时序数据库产品
    如何查看端口(3306)被那个程序占用
    MySQL数据库开发的36条军规
    介绍 MySQL 8 中值得关注的新特性和改进。
    IE浏览器 兼容性(IE9-11 差异说明)
    python3:(unicode error) 'utf-8' codec can't decode
    静态代码块
  • 原文地址:https://www.cnblogs.com/zgxblog/p/10912315.html
Copyright © 2011-2022 走看看