foreach 默认按值遍历,修改循环变量不影响原数组;需用 &$item 引用或通过键名索引(如 $arr[$key] = ...)才能生效,且循环中增删元素会导致遍历异常。

foreach 循环中直接赋值修改原数组元素无效
PHP 的 foreach 默认是按值遍历,每次迭代拿到的是数组元素的副本,对循环变量(如 $item)赋值不会影响原数组。这是最常见也最容易被忽略的原因。
常见错误现象:
写成这样,以为能改数组第 0 个元素的值:
$arr = ['a', 'b', 'c'];
foreach ($arr as $item) {
if ($item === 'a') $item = 'A'; // ❌ 没用
}
var_dump($arr); // 还是 ['a', 'b', 'c']- 使用
&$item引用赋值才能生效(注意:必须在循环开始时声明引用,中途加&无效) - 如果数组本身是嵌套结构(比如多维数组),只加一层引用不够,需确保目标层级也被引用
- 循环中对
$item使用unset()或array_push()等操作,也不会改变原数组长度或键顺序
修改数组键值必须用明确的键名索引
即使你用了引用,如果想改的是某个特定键对应的值,更稳妥的方式是通过键来操作——尤其当数组有非连续数字键、字符串键,或你在循环中动态增删元素时。
示例场景:把所有字符串值转大写,但保留原始键和结构:
立即学习“PHP免费学习笔记(深入)”;
$arr = ['name' => 'alice', 'age' => 25];
foreach ($arr as $key => $value) {
$arr[$key] = strtoupper($value); // ✅ 安全、清晰、不依赖引用
}- 这种写法不依赖引用机制,避免因引用丢失导致的静默失败
- 适用于 PHP 所有版本,包括早期不支持引用赋值的环境
- 如果循环中同时要读旧值、写新值,且逻辑复杂,显式索引比引用更易追踪和调试
foreach 中修改数组结构(增删元素)行为不可靠
在 foreach 过程中用 unset()、array_shift() 或 $arr[] = ... 修改数组本身,会导致内部指针错乱,PHP 可能跳过某些元素,或重复处理,结果不可预测。
错误示例:
$arr = [1, 2, 3, 4];
foreach ($arr as $k => $v) {
if ($v % 2 === 0) unset($arr[$k]); // ⚠️ 危险!可能导致遍历不完整
}- PHP 文档明确说明:「对数组进行修改可能影响遍历行为」
- 需要增删元素时,应先收集要操作的键,循环结束后再批量处理;或改用
for+count()(仅限数字索引且不稀疏) - 若必须边遍历边过滤,优先考虑
array_filter()或array_map()等函数式方法
foreach 引用失效的隐藏陷阱
即使写了 foreach ($arr as &$item),也可能“看似引用实则断开”,尤其在循环后继续使用该变量,或在函数作用域中混用引用与返回值。
典型问题:
$arr = [1, 2, 3];
foreach ($arr as &$item) { /* do something */ }
// 忘记 unset($item),下一次 foreach 或变量复用会污染数据
foreach ($arr as $item) { /* 此处 $item 是引用,可能意外改写 $arr 最后一个元素 */ }- 每次用完引用变量后,务必
unset($item),否则它仍指向原数组最后一个元素 - 函数内返回引用数组再用
foreach遍历时,若函数返回的是临时数组而非变量引用,&$item会报Cannot create references to elements of a temporary array -
foreach对对象属性数组、ArrayObject等容器的行为与普通数组不同,不能一概而论
实际改数组,别赌 foreach 的引用是否还连着,老实用键索引;要动结构,就别在循环里动手。引用不是银弹,是双刃剑。



















