PHP Generator用 yield from 组合迭代器的实现方法
来源:17golang原创
时间:2026-09-15 21:48:35 206浏览 收藏
我在把分页查询结果接到导出流程时,最容易写成一层层手动 foreach:外层读页,内层过滤,最后再把结果塞进数组。PHP Generator 的 yield from 可以把这几个迭代器连成一条消费链,外层只负责编排,真正的值仍然按需产生。
官方地址:https://www.php.net/manual/en/language.generators.syntax.php
yield from的核心是“委托”,不是复制数组。它可以把另一个 Generator、Traversable 对象或数组的值交给调用方;外层在内层结束后继续执行,还可以接住内层return的结果。
yield from适合把分页、过滤和转换拆成独立的惰性迭代器。- 委托会保留键,重复键在
iterator_to_array()中可能覆盖前面的值。 - 内层的
return不会变成遍历值,要通过外层表达式或Generator::getReturn()读取。
先用 yield from 串起分页和过滤
下面的例子把“模拟分页”与“只保留已支付订单”分开。pageRows() 只负责产生记录,paidRows() 只负责过滤,exportRows() 用 yield from 把每一页交出去。
101, 'status' => 'paid'], ['id' => 102, 'status' => 'pending']],
[['id' => 103, 'status' => 'paid']],
];
foreach (exportRows($pages) as $row) {
echo $row['id'] . PHP_EOL;
}
?>
输出顺序是 101、103。调用 exportRows() 时并不会立刻遍历全部页面;只有 foreach 请求下一项,外层才逐层恢复执行。这个特性适合大结果集,但并不意味着底层数据库查询自动变成流式,底层数据源仍要支持逐步读取。

数组和 Iterator 也能接入同一条链
yield from 的右侧不只可以是 Generator。数组和实现 Traversable 的对象也能接入,因此可以把固定前缀、文件迭代器或已有业务集合拼到一个出口中。
组合的价值在于边界清楚:每个内层迭代器拥有自己的读取或过滤职责,外层只表达顺序。若某个来源需要异常转换、重试或释放资源,应在它自己的生成器内部处理,不要把所有控制分支堆到最外层。
组合时最容易忽略的是键和 return 值
委托不会自动重置键。两个内层数组都使用默认键时,遍历本身没有问题,但转换为数组会出现覆盖。需要保留键时默认行为可以接受;只想收集所有值时,应显式关闭键保留。
另一个边界是 return。内层的返回值是控制信息,不会混入 foreach。如果需要统计已处理页数,可以让外层接收委托表达式的返回值:
getReturn() . PHP_EOL; ?>
最终输出的汇总数字是 2。只有生成器完成遍历后才能调用 getReturn();提前读取会得到运行时错误,所以应把它放在消费循环之后。

什么时候应该换回普通 foreach
如果外层需要对每个元素执行不同的分支、合并多个来源的错误,或者必须先知道全部数据才能排序,普通 foreach 往往更直观。yield from 最适合表达“把一个完整迭代协议交给另一个来源”,而不是把复杂业务流程隐藏在多层生成器里。
| 场景 | 建议 | 关键检查 |
|---|---|---|
| 分页逐条导出 | 使用 yield from | 底层读取是否惰性、异常是否在来源层处理 |
| 固定数组拼接 | 可使用 yield from | 是否需要保留键 |
| 依赖全量数据排序 | 先收集再处理 | 内存上限和键重排 |
| 多分支业务编排 | 优先普通 foreach | 控制流是否已经难以追踪 |
常见问题
yield from 会把内层 return 当成一条数据吗?
不会。它只转发内层产生的遍历值,内层 return 通过委托表达式返回给外层,最终结果再由 getReturn() 读取。
为什么 iterator_to_array 后少了一条记录?
通常是重复键导致后来的值覆盖了前面的值。收集全部遍历值时传入第二个参数 false,让函数忽略原键并重新编号。
yield from 能减少数据库内存占用吗?
它只负责生成器之间的委托,不会替底层查询改变取数方式。要降低内存,还需要让数据库驱动或分页读取逻辑一次只取有限数据。
实际使用时可以把判断标准记成一句话:来源负责产生,过滤器负责筛选,外层负责委托;只有确认键策略、return 读取时机和底层数据源边界后,再把多层 foreach 收敛成 yield from。
-
文章 · php教程 | 2小时前 | 协程 · Fiber · PHP · 异步编程 · 生命周期 · suspend · resume · Fiber::suspend Fiber::resume PHP Fiber Fiber::getReturn 协程值传递 Fiber 生命周期366 收藏
-
文章 · php教程 | 3小时前 | HTTP · 文件流 · 元数据 · wrapper · php教程 · PHP Stream · stream_get_meta_data PHP Stream stream_get_wrappers HTTP wrapper 文件流识别217 收藏
-
文章 · php教程 | 5小时前 | tcp连接 · php教程 · stream_socket_client · DNS排查 · 网络故障诊断 · PHP stream_socket_client 超时 PHP DNS 解析失败 PHP TCP 连接拒绝 stream_socket_client errno PHP 网络连接排查395 收藏
-
132 收藏
-
175 收藏
-
162 收藏
-
402 收藏
-
418 收藏
-
229 收藏
-
384 收藏
-
文章 · php教程 | 16小时前 | PHP · 日期处理 · 兼容性 · DatePeriod · 时间区间 · php DateTime DateInterval DatePeriod INCLUDE_END_DATE 日期周期434 收藏
-
496 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习