会,但仅在foreach中直接修改数组结构(如unset、array_push)时发生;默认遍历副本,值修改不影响循环,而增删元素会导致指针错位、跳过或重复访问。

会,但只在特定方式下发生——直接在 foreach 循环体内修改数组结构(如 unset()、array_push()、$arr[] = 等),会导致遍历行为异常,甚至跳过元素或重复访问。
默认遍历的是数组副本,值修改不影响循环过程
PHP 的 foreach 在开始时会隐式复制一份数组的内部状态(包括指针位置和当前快照),因此:
- 仅修改
$value(不带&):只改副本,不影响原数组,也不干扰后续遍历; - 用引用
foreach ($arr as &$v)修改元素值:原数组被更新,但遍历顺序和元素数量不变,不影响循环流程; - 在循环中增删元素(如
unset($arr[$key])或$arr['new'] = 1):数组内部结构改变,而 foreach 仍按初始快照推进指针,极易出错。
修改结构导致遍历异常的典型表现
以下代码演示风险:
$arr = [0 => 'a', 1 => 'b', 2 => 'c'];
foreach ($arr as $k => $v) {
echo "$k:$v ";
if ($k === 1) unset($arr[0]); // 删除前面的键
}
输出可能是:0:a 1:b 2:c,看似正常;但若删除的是当前或后续键(如 unset($arr[2])),可能因内部指针偏移造成遗漏或重复。PHP 官方明确警告:不要在 foreach 中改变数组大小或键名分布。
安全修改原数组的推荐做法
- 先收集要修改的键/索引,循环结束后统一操作;
- 用
for或while配合key()/next()手动控制,便于动态调整; - 对大数组,优先用
array_filter()、array_map()等函数生成新数组,再遍历结果; - 必须边遍历边删时,改用反向
for循环(从count()-1递减),避免索引错位。
对象属性遍历不受此限制,但逻辑需自控
当 foreach 遍历对象(如 foreach ($obj as $prop => $val))时,修改对象属性本身不会中断遍历(因为对象是引用传递),但若在循环中动态添加/删除 public 属性,实际效果取决于类是否实现 Iterator 接口——未实现时,PHP 仍按初始可见属性列表遍历,新增属性不会被本轮循环捕获。

















