PHP foreach 引用坑,数组末尾项莫名变了
数组末尾项被改掉,很多时候不是数据源错了,而是上一次 foreach 引用遍历后,循环变量还绑在末尾元素上。
foreach ($items as &$item) → $item 引用末尾元素 → 后续 foreach 复用 $item → 末尾项被覆盖 → unset($item) 断开引用
这个坑不算高级,但很隐蔽。你看代码时会觉得两段 foreach 都很正常,数组却在第二次遍历后出现“末尾项复制上一项”的怪现象。php 官方文档也专门提醒过:引用遍历结束后,要用 unset() 销毁循环变量引用。
场景问答:为什么只坏末尾元素

问题通常出现在这种代码里:前面为了批量修改数组元素,用了 foreach ($items as &$item)。循环跑完后,$item 并不会自动变回普通变量,它仍然引用着数组末尾元素。
• 第一次遍历用了 &$item,说明变量和数组元素绑在一起。
• 第二次遍历如果复用 $item,每次赋值都会写回末尾元素。
• 表面看是第二段代码出错,根因在第一段引用没有断开。
末尾项出问题,是因为引用还停在那里。
这类 bug 在商品列表、订单明细、表格数据格式化里很常见,尤其是先改字段、再组装展示数据的场景。
错误复现:&$item 会留下引用关系

可以把 $item 想成一根线。引用遍历时,这根线会依次连到每个元素;循环结束后,它没有自动剪断,而是继续连在末尾元素上。
foreach ($items as &$item) {
$item['price'] *= 100;
}
foreach ($items as $item) {
// 这里复用了 $item
}
• 变量名相同不是语法错误,但会触发这个坑。
• 即使用不同循环结构,只要复用了引用变量,也可能污染数据。
• 打印数组时,通常会看到末尾元素变得和前一个元素很像。
引用变量不会因为循环结束就自动断开。
排查时可以搜 foreach 里的 &,再看后面是否有同名变量继续参与遍历。
修复习惯:引用遍历后立刻 unset($item)

修复很简单:引用遍历结束后立刻写 unset($item)。这行代码不是释放数组元素,而是断开循环变量和末尾元素之间的引用关系。
foreach ($items as &$item) {
$item['price'] *= 100;
}
unset($item);
• 用完引用变量就 unset(),不要等后面想起来再补。
• 后续遍历换一个变量名,比如 $row,可读性也更清楚。
• 如果只是读取数据,不要加 &。
这不是洁癖,是避免数组被悄悄写回。
团队里可以把它当成代码习惯:看到引用遍历,就顺手检查后面有没有 unset()。
评审清单:看到 foreach 里的 & 就多看两眼

引用遍历不是不能用,但要有明确理由。很多时候你只是想生成一个新数组,那就没必要改原数组;很多时候你只是读取元素,那就更不该用引用。
• 批量修改原数组时,引用遍历可以用,但要紧跟 unset()。
• 只是映射数据时,可以新建数组,不要原地改。
• 代码评审里看到 &$item,顺手检查变量复用。
引用遍历越少,数组状态越容易看明白。
foreach 引用坑麻烦的地方,是它不会每次都爆。数据量小、遍历顺序巧的时候可能看不出来。一旦出现在价格、库存、状态这种字段上,排查成本就会被放大。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











