生成器委托

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

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

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

上例子:

function echoTimes($msg, $max) {

for ($i = 1; $i <= $max; ++$i) {

echo "$msg iteration $i\n";

yield;

}

}

function task() {

yield from echoTimes('foo', 10); // print foo ten times

echo "---\n";

yield from echoTimes('bar', 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 为父子生成器建立一个双向的通道。不多说,上例子:

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('foo');

echo "---\n";

yield from echoMsg('bar');

}

$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

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

生成器返回值

如果生成器被迭代完成,或者运行到 return 关键字,是会给这个生成器返回值的。

可以有两种方法获取这个返回值:

使用 $ret = Generator::getReturn() 方法。

使用 $ret = yield from Generator() 表达式。

上例子:

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('foo', 10);

echo $end;

$gen = echoTimes('bar', 5);

yield from $gen;

echo $gen->getReturn();

}

foreach (task() as $item) {

;

}

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

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

一个非阻塞的web服务器

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

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

代码如下:

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

HTTP/1.1 200 OK

Content-Type: text/plain

Content-Length: 12

Connection: close

hello world!

RES;

return $response;

});

参考资料

php7.0 yield,PHP7中生成器的新特性 yield-from amp;amp; return-values相关推荐

  1. godaddy停止支持php哪个版本,GoDaddy cPanel虚拟主机新增PHP7.0和PHP7.1版本 | Godaddy美国主机中文指南...

    自从上次GoDaddy虚拟主机取消PHP5.3以下版本后,近日再次做出调整.小编登录GoDaddy Linux主机的cPanel控制面板时发现,已经新增了PHP7.0和PHP7.1两个版本.目前GoD ...

  2. MySQL 8.0 在关系数据库方面有这些新特性

    作者 | 捏造的信仰 原文 | https://segmentfault.com/a/1190000013803247 本文介绍几个 8.0 在关系数据库方面的主要新特性. 你可能已经知道 MySQL ...

  3. accept 返回0_从0开始理解Vite的主要新特性(一)

    此文已同步到公众号[因卓诶]以及因卓诶博客: 从0开始理解Vite的主要新特性(一) - 因卓诶-爱分享爱原创的技术博客 ~ 个人博客​www.yinzhuoei.com vite这个工具确实尤大在微 ...

  4. Kafka 2.8.0 正式发布,增加了哪些新特性?

    导读:目前 Kafka 已经定位为一个分布式流式处理平台,它以高吞吐.可持久化.可水平扩展.支持流数据处理等多种特性而被广泛使用.目前越来越多的开源分布式处理系统如 Cloudera.Storm.Sp ...

  5. CSS3中的一些新特性(CSS)

    CSS3中的一些新特性 CSS3能做什么 边框 阴影 box-shadow 颜色 文字与字体 背景 CSS3中的动画 CSS3是CSS2的升级版本,3只是版本号,它在CSS2.1的基础上增加了很多强大 ...

  6. Python 3.11 中的最佳新特性和修正

    更快的解释器,更易懂的错误,更强大的类型提示,以及其他一系列的加速和调整,现在都可以试用了. Python 每年都会发布新版本,上半年是功能锁定的测试版,年底是最终版本. Python 3.11 的特 ...

  7. 解读ASP.NET 5 MVC6系列(17):MVC中的其他新特性

    原文:解读ASP.NET 5 & MVC6系列(17):MVC中的其他新特性 (GlobalImport全局导入功能) 默认新建立的MVC程序中,在Views目录下,新增加了一个_Global ...

  8. 详细解读Windows8.1 Update中的WIMBoot新特性

    微软在Windows8.1 Update发布免费更新后,越来越多win8系统用户选择了升级,在Windows8.1 Update全新系统中有一项WIMBoot新技术,面对新特性很多用户还是一筹莫展,不 ...

  9. php7 参数类型限定,PHP 7.2新特性:参数类型声明(附代码)

    php7.2版本具有新特性,功能和改进,可以让我们编写更好的代码,下面的文章中我将介绍php7.2中的一个新的功能:参数类型声明,话不多说,让我们来具体看看正文内容. 参数类型声明 从PHP 5开始, ...

最新文章

  1. 基于视频理解TSM和数据集20bn-jester-v1的27类手势识别
  2. 电脑主板线路连接图解_电工速学手册:306页现场电工全能图解,实用技术精选大合集!...
  3. 现代软件工程 第十二章 【用户体验】练习与讨论
  4. jQuery-层次选择器的学习
  5. 【Linux】一步一步学Linux——bzip2命令(65)
  6. Android之走手机流量让电脑能上网几种方法
  7. c++远征之继承篇——多重继承,多继承,虚继承,多继承时的重复定义解决方法
  8. 互联网日报 | 5月12日 星期三 | 全国人口共14.1178亿人;长安汽车迈入2000万辆时代;Soul递交纳斯达克招股书...
  9. 只有ajax会跨域吗_ajax解决跨域
  10. 解决Ubuntu系统“无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系”的有效方法
  11. # 学号20155308 2006-2007-2 《Java程序设计》第4周学习总结
  12. 算法:动态规划解决爬楼梯Climbing Stairs python3
  13. 通过SQL语句建立数据库. 表
  14. MPQ文档布局分析[转帖]
  15. 差距越来越大, 直播行业割终结束, 虎牙、斗鱼平分天下?
  16. window计算机截屏快捷键,Win7系统电脑截图快捷键是什么?
  17. 计算机视觉及色彩空间RGB,HSV,HLS,Lab,LMS,XYZ,CMYK
  18. 当物流行业遇见MongoDB
  19. SHU-“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛-K-购买装备
  20. windows简单命令

热门文章

  1. VHDL六层电梯控制器及仿真
  2. 消息队列服务器 轻量,PHP的轻量消息队列php-resque使用说明
  3. mysql+两行+一样+筛选_MySQL计算相邻两行某列差值的方法
  4. java三点确定圆弧_圆弧方向判断方法和三点确定一个圆的计算方法
  5. 2021龙岩一中高考成绩查询,喜报!2020年高考成绩已经揭晓,龙岩这七所一中,看看有没有你的母校!...
  6. latex beamer 空一行_握草!一行Python代码写的游戏,我能这样玩一天
  7. ubuntu20.1 查看apt仓库_上海食品冷藏仓库出租查看
  8. BZOJ4205卡牌配对——最大流+建图优化
  9. 【laravel5.4】迁移文件的生成、修改、删除
  10. Python模块: 命令行解析optionparser