yield不是语法糖而是内存开关,将全量数组转为单条迭代流,内存从mb级降至kb级;必须声明generator返回类型,禁用iterator_to_array等全量消费操作,关闭pdo缓冲才真正生效。

直接说结论:用 yield 不是为了让代码“看起来更现代”,而是把本该一次性塞进内存的百万级数组,变成每次只 hold 一条数据的迭代流。不改逻辑、不换框架,仅替换 return $array 为 yield $item,内存就能从几十 MB 压到几 KB。
生成器函数必须声明 Generator 返回类型
PHP 7.1+ 强制要求,否则运行时报 TypeError;PHP 8.2+ 默认拒绝执行。这不是可选项,是硬性前提。
- ✅ 正确写法:
function getUsers(): Generator { yield ['id' => 1]; } - ❌ 错误写法:
function getUsers() { yield ['id' => 1]; }(PHP 8.2+ 直接报错) - ⚠️ 模糊写法:
function getUsers(): iterable(IDE 无法提示,静态分析失效)
别把生成器转成数组——iterator_to_array() 是最常见 OOM 原因
生成器只能单向遍历一次,且设计初衷就是“不落地”。一旦调用 iterator_to_array()、json_encode() 或 count(),PHP 就会试图把全部值收集成数组,瞬间触发 Fatal error: Allowed memory size exhausted。
- ❌ 千万别这么干:
$all = iterator_to_array(getUsers()); - ✅ 正确做法:直接
foreach (getUsers() as $user) { ... } - 需要总数?单独跑
SELECT COUNT(*),别指望生成器里“数出来”
数据库查询必须关掉 PDO 缓冲,否则 yield 白写
ThinkPHP、Laravel 等 ORM 默认开启 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,这意味着 MySQL 客户端早已把整张结果集拉进 PHP 内存——yield 只是在已加载的数据上“假装逐行吐”,毫无意义。
- ✅ 正确配置(TP6.1+):
'params' => [PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false] - ✅ 手动 PDO 查询时,必须加
$stmt->closeCursor(),否则连接被占着不放 - ❌ 别在生成器里混用
toArray()、paginate()、json()—— 全部触发全量加载
yield from 合并多个数据源时要注意键名覆盖
用 yield from 委托子生成器时,PHP 会原样保留子生成器的键名。如果多个子生成器都 yield 0 => 'a',后 yield 的会覆盖前面的——这不是 bug,是迭代器规范行为,但容易被当成“丢数据”。
- ✅ 安全做法:统一用字符串键,或显式重编号:
yield $i++ => $value - ✅ 多文件处理时,优先用
yield from readLogFile($path)而非嵌套foreach+yield - ⚠️ PHP 7.0+ 才支持
yield from,老版本得手动循环 +yield
最容易被忽略的一点:生成器对象不可序列化,也不能被 json_encode() 直接处理;它不是容器,是管道。你得接受“不能随机访问、不能反复遍历、不能中途计数”这个事实——省下的内存,代价就是放弃这些便利。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











