yield是将函数转为生成器的关键字,它暂停执行并逐个产出值,内存占用O(1),适用于大文件或无限序列;生成器不可重复遍历、不支持下标访问,需用foreach消费。

yield 是什么,为什么不用 return
yield 不是函数调用,而是让函数变成生成器(Generator)的关键字。它暂停函数执行、交出一个值,下次迭代时从中断处继续,不重建栈帧、不重复初始化变量。相比 return 一次性返回整个数组,yield 在处理大文件、数据库游标或无限序列时,能把内存占用从 O(n) 降到 O(1)。
常见错误:把生成器当普通数组用,比如直接 count($gen) 或 $gen[0] —— 这会报错,因为生成器不是 ArrayObject,也不支持下标访问。
- 生成器对象只能遍历一次,用完即失效(再次
foreach无输出) - 不能用
array_map、array_filter直接处理生成器,需先转为数组(但会失去内存优势) - 想重用?得重新调用生成器函数,或封装成类并实现
Iterator
读取超大 CSV 文件不爆内存
用 fgetcsv() 配合 yield,逐行解析,每行只保留当前数据,避免把几百万行全 load 到内存。
function readCsvLines(string $path): Generator
{
$handle = fopen($path, 'r');
if (!$handle) {
throw new RuntimeException("Cannot open {$path}");
}
while (($row = fgetcsv($handle)) !== false) {
yield $row;
}
fclose($handle);
}
<p>// 使用
foreach (readCsvLines('/tmp/data.csv') as $line) {
// 每次只有一行在内存里
process($line);
}</p>
注意点:
- 必须手动
fclose(),生成器退出时不会自动关文件句柄 - 如果中途
break或异常退出,fclose()可能被跳过 → 建议加try/finally - PHP 8.1+ 可用
resource类型声明,IDE 更易识别
yield from 转发子生成器,别手写循环
当你有多个生成器要串联,或想把递归逻辑拆开,yield from 是语法糖,它把子生成器的每个产出值“透传”出来,且保持上下文和异常传播链。
function numbers(): Generator { yield 1; yield 2; }
function letters(): Generator { yield 'a'; yield 'b'; }
<p>function combined(): Generator {
yield from numbers();
yield from letters();
}</p><p>// 输出:1, 2, a, b
foreach (combined() as $v) { echo $v . "\n"; }</p>
关键区别:
-
yield from $gen会消耗子生成器,而yield $gen只是产出一个 Generator 对象 - 子生成器抛出的异常会冒泡到外层,无需额外 try/catch 包裹
- 不支持
yield from数组(PHP 7.4+ 才支持),老版本得用foreach ($arr as $v) yield $v
生成器返回值(PHP 7+)和关闭时机
生成器函数可以用 return 设置最终返回值(不是 yield 出的值),这个值可通过 Generator::getReturn() 获取,但**仅在生成器执行完毕后有效** —— 也就是所有 yield 完、自然结束,或被 Generator::close() 强制终止后。
function counter(): Generator
{
yield 1;
yield 2;
return 'done';
}
<p>$gen = counter();
foreach ($gen as $val) {
echo $val . "\n"; // 输出 1, 2
}
echo $gen->getReturn(); // 'done'</p><p>// 如果提前 break:
$gen2 = counter();
foreach ($gen2 as $val) {
if ($val === 1) break;
}
echo $gen2->getReturn(); // Notice: Generator is not closed — 返回 null</p>
所以:依赖 getReturn() 的逻辑,必须确保生成器完整运行,或显式调用 close() 后再取值。这点常被忽略,尤其在异常处理路径中。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











