必须用foreach($arr as &$value)修改原数组元素,仅在需批量更新、格式化或标记状态时启用引用;值遍历只操作副本,$arr[$key]=...在稀疏或非整数键时不可靠。

修改原数组元素时必须用 foreach ($arr as &$value)
只有明确需要在遍历过程中直接改写原数组的某个值,才该启用引用。比如批量更新价格、统一格式化字段、标记已处理状态等场景。值遍历(foreach ($arr as $value))永远只操作副本,改了也白改。
常见错误是以为写了 $arr[$key] = ... 就能改原数组——这在关联数组里可能奏效,但面对稀疏索引或非整数键时极易出错;而引用遍历绕过键名,直抵内存地址,更可靠。
- 适用:
$users中每个['status']都要设为'processed' - 不适用:只是打印、校验、统计——用值遍历更安全、开销更低
- 注意:
unset($arr)或array_push($arr, ...)在引用遍历中会引发未定义行为,禁止混用
嵌套数组里改深层值必须逐层加引用
二维及以上结构中,仅外层用引用不够。例如要修改 $data[0]['profile']['email'],光写 foreach ($data as &$row) 只让 $row 引用第一层,$row['profile'] 仍是副本。必须内层也加 &$sub 才能穿透。
典型陷阱是嵌套后忘记解除引用,导致下一轮外层循环的 $row 仍绑着上一轮的 $sub,结果写错位置。这不是 PHP Bug,是引用变量生命周期没管好。
立即学习“PHP免费学习笔记(深入)”;
- 正确写法:
foreach ($data as &$row) { foreach ($row as &$field) { $field = trim($field); } unset($field); } unset($row); - 漏掉任一
unset(),后续对$row或$field的赋值都可能污染原数组 - 如果只读不写,全部用值遍历,彻底规避风险
性能敏感场景下引用可省掉复制开销
当数组元素本身是大字符串、长数组或对象时,值遍历会触发完整拷贝,GC 压力明显上升。引用遍历跳过复制,只传指针,实测在万级元素+平均 2KB 字符串的场景下,内存占用能降 30% 以上。
但这不是银弹。PHP 对小变量(如整数、短字符串)做了 copy-on-write 优化,引用反而多一次符号表查找;且引用会阻止引擎做某些底层优化。所以“大才引,小则避”。
- 适合引:元素含
base64图片字符串、JSON 片段、子数组 >5 项 - 不适合引:纯数字 ID 列表、布尔标记数组、单字符状态码
- 验证方法:
memory_get_usage(true)对比两版循环前后的差值
引用遍历后不 unset() 是最常被忽略的硬伤
几乎所有线上环境出现的“数组值莫名变相同”“某次循环覆盖了前面所有数据”,根源都是这个。PHP 不会在循环结束自动销毁引用变量,$value 会一直挂着最后一项的地址,直到所在作用域退出。
哪怕你后面只写 foreach ($arr as $v),只要 $v 还被旧引用占着,赋值就会写进原数组——而你根本没意识到自己在用引用。
- 必须跟在每轮引用遍历末尾:
unset($value),不能靠作用域自动清理 - 函数内遍历?同样要
unset,局部变量不等于安全 - 用 IDE 搜索项目里的
foreach.*&,挨个检查是否配对unset



















