foreach遍历时追加元素不会被本轮循环遍历到,因其基于数组初始快照;修改现有元素可用引用或下标,但需注意引用残留问题;动态扩展需求应改用while或分步处理。

foreach 里直接往数组尾部 array_push() 或 [] 追加,新元素不会被遍历到
PHP 的 foreach 在开始执行时会「快照」当前数组的内部结构(包括键、值、内部指针位置),后续对原数组的追加操作(如 array_push($arr, $x) 或 $arr[] = $x)不会影响本轮循环的迭代范围。
也就是说,你可以在循环体里追加,但新增的元素绝不会出现在这次 foreach 的后续迭代中——它只属于下一次遍历。
- 这是 PHP 语言设计决定的,不是 bug,也和是否引用无关
- 常见错误现象:
foreach ($list as $item) { array_push($list, 'new'); }看似无限循环,实际只执行原始长度次 - 若误以为能“边遍历边扩展”,逻辑可能漏处理数据,尤其在构建树形结构或递归扁平化时容易出错
想在 foreach 中修改/添加字段,必须用引用 &$v 或显式下标 $arr[$k]
给每个元素增加新键(比如 ['status'] 或 ['created_at']),本质是修改已有元素,不是追加新元素。这时有两种安全写法:
- 用引用:
foreach ($arr as $k => &$v) { $v['name'] = 'xxx'; }—— 注意结尾要unset($v)避免后续意外污染(尤其在多次循环同一数组时) - 不用引用:
foreach ($arr as $k => $v) { $arr[$k]['name'] = 'xxx'; }—— 更直观,无引用残留风险,性能差异可忽略 - 二维以上嵌套时,
&$v只解一层引用,$v['child']['field'] = ...不会生效,必须确保$v本身是引用,或改用$arr[$k]['child']['field']
用 foreach 实现“真正边遍历边追加”需要手动控制指针
如果业务确实要求遍历过程中动态加入新项并让它们也被处理(比如 BFS 层序展开、任务队列扩散),foreach 不适合,得换方式:
立即学习“PHP免费学习笔记(深入)”;
- 用
while+each()/current()+next()手动推进,自己维护指针 - 更推荐:先收集待追加项到临时数组,循环结束后
array_merge()或批量array_push(),再重新遍历 - 避免在循环中反复调用
count()判断长度变化——PHP 数组长度缓存不实时更新,可能引发逻辑偏差
容易被忽略的引用残留问题
写 foreach ($arr as &$v) 后,即使循环结束,$v 仍指向数组最后一个元素的内存地址。如果紧接着又写 foreach ($arr as $v)(没加 &),第二次循环的第一个赋值就会覆盖原数组最后一项。
这个坑不报错,但数据静默损坏。最稳妥的习惯是:
- 每次用完引用循环后,立刻
unset($v) - 或者干脆统一用下标写法
$arr[$k]['field'] = ...,省心且可读性更好



















