yield 是 php 中将内存占用从 o(n) 降至 o(1) 的核心机制,通过返回 generator 对象按需生成值,避免一次性加载全部数据。

直接说结论:yield 不是用来“让代码更酷”的语法糖,它是 PHP 里最有效的内存开关——只要把大数组遍历改成生成器,内存占用就能从 O(n) 压到接近 O(1)。
为什么 foreach 大数组会崩溃,而 yield 不会?
因为普通函数返回数组时,PHP 必须一次性把全部数据载入内存;yield 函数返回的是 Generator 对象,它不存数据,只存执行状态。每次 foreach 取一个值,函数才运行到下一个 yield 并暂停。
- 常见错误现象:
Fatal error: Allowed memory size exhausted,但代码里根本没显式定义大数组——问题往往出在某个封装函数悄悄return range(1, 1000000)或array_merge(...) - 真实场景:读取 50 万行 CSV、遍历深层目录树、拉取分页 API 全量数据
- 性能影响:100 万条数据,普通数组常占 80–120 MB;生成器稳定在 2–4 MB(仅含栈帧和当前值)
yield 函数怎么写才安全?关键三步
不是加个 yield 就万事大吉。下面这些点漏掉一个,就可能白忙活:
- 函数体里别用
$data = []收集结果再return $data——这是最常见返祖写法 - 循环中直接
yield $value,或yield $key => $value;键名类型要一致(整型键重复会覆盖,字符串键不会) - PHP 7.0+ 才支持
yield from anotherGenerator();老版本必须手动foreach (anotherGenerator() as $v) yield $v;
千万别对生成器调用 iterator_to_array()
这是导致 OOM 的高频操作。生成器设计就是单向、一次性的流,强行转数组等于把整个流“解压”回内存。
- ❌ 错误示范:
$all = iterator_to_array(getIds());、json_encode($gen)、array_values($gen) - ✅ 正确做法:保持
foreach ($gen as $v)直接消费;如需缓存小部分,用array_slice(iterator_to_array($gen, false), 0, 100)并确认数据量可控 - 兼容性注意:PHP 7.4+ 支持
iterator_to_array($gen, false)的第二个参数跳过键名转换,但依然不解决内存问题
return 和 yield 混用的坑在哪?
return 在生成器函数里不参与迭代,它的值只能通过 Generator::getReturn() 单独获取。很多人以为 return 'done' 会作为最后一项输出,结果遍历时完全看不到。
- 典型误用:
foreach (countdown(3) as $v) { echo $v; }输出321,但不会输出done - 正确用法:
echo $gen->getReturn();——适合传摘要、总条数、状态码等元信息 - 类型陷阱:函数声明了
function foo(): Generator,return的值类型不影响该声明,但 IDE 和静态分析无法推断getReturn()类型
最易被忽略的一点:生成器对象不可重用。一旦 foreach 遍历完或中途 break,这个 Generator 就关闭了,再调用 rewind() 会报错。需要重复使用?只能重新调用生成器函数再拿一个新实例。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











