0

0

PHP yield 协程 生成器用法的了解

coldplay.xixi

coldplay.xixi

发布时间:2020-07-02 18:02:15

|

3034人浏览过

|

来源于learnku

转载

PHP yield 协程 生成器用法的了解

写在前面

这篇文章,要和大家探讨的是 php yield 在 生成器用法,不带 foreachfor, while 循环的那种。就讨论 yield 将一个函数变成为生成器的用法。

关于yield 特性,是在开发 PHP5 时被提上日程,PHP5.5 版本正式加入。

关于yield的使用,我看到大部分文章都停留在,使用yield如何在foreach中穿出数据,今天想给大家讲讲 生成器 所有语法。

相关学习推荐:PHP编程从入门到精通

官网讲解

生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。

看了下官网对他讲解:php.net 生成器语法  . 每个字都认识,但似乎还是体会到它讲的内涵。官网我们主要看两部分内容:

立即学习PHP免费学习笔记(深入)”;

  1. yield 的语法。

  2. 代码例子。

先说语法, yield 的左边是一个赋值语句,右边可以是值(也可是表达式) 。而yield 会先执行右边的表达式,并把值$value送到生成器外面。当生成器收到值后,会执行yield左边的语句,赋值给$data.

<?phpfunction func(){
    $data = (yield [$express]);}

语法就这样,估计大家还是有些懵,那就看看官网下面代码例子吧,我看里面例子参差不齐。

注意yield 外面包的这一层括号,如果是在php5.5,右侧$express的优先级是判断,可能会比左侧$data的赋值语句低的。所以在php5用yield,yield 右边是可运行表达式,左侧需要接受返回并赋值,那么这个括号是有必要的。在php7不会有这个问题。

通过例子来了解它

不论是学 人类语言,计算机语言,都是模仿开始

对于一个用人类语言来描述,都不那么明晰时,所以那就通过例子告诉你它能做什么,不能做什么。

相关代码,我放到gitee了,希望你能复制到你本地运行下,亲自运行感受下,有助于了理解接下来的内容。

git clone gitee.com/xupaul/PHP-generator-yie...

怎样才能产生 Generator

先定义一个函数,在函数内 写个 yield 关键词,将这个函数调用赋值给一个变量。一个生成器就产生了。

代码 /php-yield-test/yieldFunctions.php 是生成器按照不同语法组合定义了多个生成器。

测试代码 /php-yield-test/whatIsGenerator.php,用来检查哪些函数能构成生成器,哪些不能。运行结果如下

test result

  1. 函数内必须有 yield 关键词,函数可以是全剧函数,或者类的方法。
  2. 哪怕 yield 肯定不会被执行,也会产生生成器。见:yield_func4
  3. 光秃秃 的 yield 关键词就行(不向外送出,不处理外面的输入)。见: yield_func2
  4. 函数内使用 生成器 并不能让自己也成为生成器,见:yield_func5
  5. eval函数中直接运行 yield 会报错, 见:yield_func11

是的,函数内有没有foreach,while,for 语句都不是关键,关键是 yield. 生成器的类型判断用 $gen instanceof Generator

生成器的函数

Generator 对象是从 generators返回的.

Generator 对象不能通过 new 实例化.

  • Generator::current — 返回当前产生的值
  • Generator::key — 返回当前产生的键
  • Generator::next — 生成器继续执行
  • Generator::rewind — 重置迭代器
  • Generator::send — 向生成器中传入一个值
  • Generator::throw — 向生成器中抛入一个异常
  • Generator::valid — 检查迭代器是否被关闭
  • Generator::__wakeup — 序列化回调
  • Gengerator::getReturn - Get the return value of a generator

摘自 php.net generator

看着以上方法,是不想起了Iterator, 他们的确很像。同时注意,官网zh语言版本的文档没有索引方法getReturn,访问也是404。文档以en版为准,ch做参考。

以上就是生成器所有的方法,我们一个个来看。

测试方法代码 /php-yield-test/generatorMothod.php, 这里面对每个方法都有使用举例,运行结果如下。

run result 2

run result 3

好接下来对举例做个一一讲解。

Generator::current

  • 返回当前产生的值
<?phpfunction yield_func(){
    yield 12;
    return 'a';}$gen = yield_func();$re = $gen->current();echo 'current return : ' . $re;

输出:

current return : 12

看到 php-yield-test/generatorMothod.php  代码。

通过第一个代码事例,可得,对一个generator调用current方法,才算真正开始执行。执行到yield为止。如果不能命中yield,则执行到函数结束。

非generoator会立马执行并得到结果,而非一个生成器对象。

通过例子2,调用current一次,两次呢,第一次可以看到代码执行日志,第二次,只是把上一次的结果返回给我们而已,并不是让该生成器重新执行。

通过例子1,调用该函数还会获取到返回值,返回的内容就是 yield 表达式左边的内容。如果表达式无内容,则是NULL.

Generator::send

  • 向生成器yield点中传入一个值,并返回下一次current值。
<?phpfunction yield_func(){
    $data = yield 12;
    echo 'get yield data: ' . $data;
    return 'a';}$gen = yield_func();$re = $gen->current();$gen->send(32);

输出:

get yield data: 32

例子3,是一个current,send的常规调用。调用current代码运行yield等到用户send输入参数。接收到输入后,继续运行。current能够接收到yield弹出的值,send返回值为空。

Programming Helper
Programming Helper

AI代码自动生成器,在AI的帮助下更快地编程

下载

例子4,直接调用send,相当于调用current,send。不过current的返回值,并不会通过send传给用户。

例子21中,可以看到直接调用send(1),会运行生成器,并向第一个yield处输入1,继续运行至下一个yield的返回值value。所以,$gen->send(2),和 $gen->current() 结果都是同一个值。

也就是说:跳过current,直接调用send,会丢失第一次yield的弹出值。

Generator::next

  • 跳过中断,并让生成器继续执行
<?phpfunction yield_func(){
    echo 'run to code line: ' . __LINE__ . PHP_EOL;
    yield;
    echo 'run to code line: ' . __LINE__ . PHP_EOL;
    return $result;}$gen = yield_func();$gen->current();echo 'current called' . PHP_EOL;$gen->next();

输出:

run to code line: 4current called
run to code line: 6

例子5,这是一个较为常规的调用,调用current代码运行yield等到用户输入,这是调用next跳过,让代码继续运行。

例子6,直接调用next,相当于调用currentnext。而且通过最后打印$result, 我们发现怎么有点像在调用 $gen->send(NULL);

Generator::rewind

  • 重置迭代器
<?phpfunction yield_func(){
    echo 'run to code line: ' . __LINE__ . PHP_EOL;
    $result = yield 12;
    echo 'run to code line: ' . __LINE__ . PHP_EOL;}$gen = yield_func();echo 'call yield_func rewind ' . PHP_EOL;$gen->rewind();

输出:

call yield_func rewind 
run to code line: 4

例子7,8 中,发现调用该方法,会导致隐式调用current

例子9 中,发现在执行过一个yield代码段后,再次调用该方法,会导致报错(哪怕该 生成器已结束)。

Generator::throw

  • 向生成器中抛入一个异常
<?phpfunction yield_func(){
    try {
        $re = yield 'exception';
    } catch (Exception $e) {
        echo 'catched exception msg: ' .$e->getMessage();
    }}$gen = yield_func();$gen->throw(new \Exception('new yield  exception'));

输出:

catched exception msg: new yield  exception

通过以上简单的例子可得,throw 就是让yield这行代码产生异常,让外面的try catch 捕获我们生成的那个异常。

例子11中,构造生成器,并调用current方法,运行到yield处,再调用throw,就能捕获到异常。

例子12中,当调用send方法,跳过函数内yield代码时,再调用throw传入异常,就没法捕获了。

Generator::valid

  • 检查迭代器是否被关闭
<?phpfunction yield_func(){
    yield 12;
    return 'a';}$gen = yield_func();$gen->send(1);$check = $gen->valid();echo 'the generator valid ? ' . intval($check);

输出:

the generator valid ? 0

例子12中,发现current被隐式调用。

例子13中,可得,当生成器运行到yield代码段时,用valid函数检查,都会返回true

所以,别问我是否已运行,问就是运行。该方法用来获取是否关闭状态,不是 是否运行状态!运行到底,运行到return就是 关闭状态。

Generator::key

  • 返回当前产生的键
<?phpfunction yield_func(){
    yield 1 => 'abc';}$gen = yield_func();echo 'value is :' . $gen->current() . PHP_EOL;echo 'key is: ' . $gen->key() . PHP_EOL;

输出:

value is :abc
key is: 1

从以上例子中,可得yield可显示设置返回的key.

例子15 中,发现key的分发规律和PHP数组键值发放策略是差不多的,默认从0开始,未指定则是以上一个数字key+1作为当前的key.

例子16 中,我们又发现current被隐式调用。

Generator::__wakeup

  • Generator::__wakeup — 序列化回调
<?phpfunction yield_func(){
    yield 1 => 'abc';}$gen = yield_func();try {$ser = serialize($gen);} catch (\Exception $e) {
    print_r($e->getMessage());}

输出:

Serialization of 'Generator' is not allowed

这是一个魔术方法,见 PHP 魔术方法,也就是说 生成器 不能被序列化成一个字符串。

例子17就不用说了,看下例子18,看样子序列化成功了。也就是说一个生成器做为一个方法可以被序列化,当函数变成生成器时,就不能被序列化了。

Generator::getReturn

<?phpfunction yield_func(){
    yield 1 => 'abc';
    return 32;}$gen = yield_func();$gen->send(0);echo 'call yield_func return, and get: ' . $gen->getReturn();

输出:

call yield_func return, and get: 32

该函数就是获取生成器最后的返回值。如果没有return语句,或者没有执行到return语句,调用该函数得到的就是NULL。

例子19 可得,getReturn 能够获取到生成器最后的返回值。

例子19、20 可得,当生成器没有执行到return语句,或者没有执行到最后时,调用getReturn是会导致报错。

综上所述

到这里,我们就发现rewind,next__wakeup 这两个函数感觉没啥叼用呢,为啥还存在呢,因为Generator继承Iterator,自然就有了rewind, next方法,PHP 虽然支持方法覆盖,但子类的访问修饰符 不能缩紧,所以Generator只能重写这两个方法。 __wakeup 继承自 stdClass

状态转换

看图:

PHP yield 生命周期图

画了两个状态转换图,上面的要细致,繁复一点。下面的精简版,便于快速理解。

总结

以上就是关于 PHP 生成器的基础内容,希望你看了后对它有更进一步认识。下一讲,我们手把手一起来做一个任务调度器,实战一下。

有问题欢迎提问,谢谢大家!

没人比我更懂

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

php

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

38

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

83

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

97

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

223

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

458

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

169

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

246

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

34

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 13.4万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号