生成器是一次性不可重用的迭代器,调用后返回新实例,耗尽后无法重置;yield混用键名会导致索引不连续;send()需先next()再调用;feof()+fgets()有边界风险,应改用while(($line=fgets($f))!==false)。

生成器不是“懒加载数组”,它是一次性、不可重用的迭代器对象,用错场景或误调方法会直接报错或静默失效。
Generator对象不能重复遍历
生成器函数每次调用都返回一个全新的Generator实例,但同一个实例只能遍历一次。一旦valid()返回false,它就彻底耗尽,再次foreach或rewind()会抛出Exception: Cannot rewind a generator that was already run。
- 错误写法:
$gen = readLogFile("big.log"); foreach($gen as $l) {...} foreach($gen as $l) {...}—— 第二个foreach什么也不执行 - 正确做法:需要多次遍历时,每次都重新调用生成器函数:
foreach(readLogFile("big.log") as $l) {...},或显式封装为可复用逻辑(如闭包+参数化) - 调试技巧:用
$gen->valid()检查是否还有值,比empty($gen)更可靠(empty对Generator始终返回true)
yield带键和不带键混用时索引自动递增
PHP按“最后使用的数字键 + 1”规则分配隐式索引,不是从0开始重排。这容易导致数组键错乱,尤其在拼接多个生成器结果时。
- 示例:
function gen() { yield 'a'; yield 4 => 'b'; yield 'c'; }→iterator_to_array(gen())得到[0 => 'a', 4 => 'b', 5 => 'c'],不是[0=>'a',1=>'b',2=>'c'] - 若需严格连续索引,统一用无键
yield,或手动yield $i => $value控制 - 注意:
iterator_to_array($gen, false)第二个参数设为false可强制丢弃键、只取值,避免键冲突
send()必须配合yield接收,且首次调用next()前不能send
send()是协程通信核心,但极易因调用时机错误导致Fatal error: Uncaught Exception: Cannot send to a generator that has not yet been resumed。
- 必须先调用
next()或进入foreach触发第一次执行,让生成器运行到第一个yield处暂停,此时才能send() -
yield本身要写成$received = yield $value;形式,否则发送的值会被丢弃 - 常见陷阱:在
foreach中混用send()——foreach内部自动调用next(),你再send()会跳过下一个yield,逻辑错位
大文件读取时feof() + fgets()组合有边界风险
用while(!feof($file)) { yield fgets($file); }看似合理,但feof()只有在尝试读取失败后才返回true,最后一行可能被重复yield或遗漏。
- 更健壮写法:
while (($line = fgets($file)) !== false) { yield $line; } - 注意
fgets()返回false表示EOF或错误,用!==严格比较,避免空行("")被误判为结束 - 如果文件含非UTF-8编码或二进制内容,
fgets()可能截断,应改用fread($file, $chunkSize)分块yield,并自行处理行边界
生成器真正的复杂点不在语法,而在于状态机本质——它把函数执行流拆成多个暂停/恢复切片,任何破坏这个切片连续性的操作(比如试图重用实例、跳过yield、忽略返回值语义)都会让行为脱离预期。写完记得用valid()和current()手动探针验证状态,别全靠foreach掩盖问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











