foreach 是首选,因其语法简洁、自动处理指针与键类型、引用安全;for 需手动索引易错,while+each() 已废弃且行为不可靠;遍历时修改数组应先收集键再统一操作,嵌套遍历依深度与需求选递归或迭代器。
PHP 遍历数组时,foreach 不仅语法简洁,还自动处理内部指针、键类型和引用安全问题。用 for 循环需手动维护索引,对关联数组或键不连续的数组极易出错;while + each() 已在 PHP 7.2+ 被废弃,且每次调用会移动数组指针,导致不可预测行为。
常见错误现象:for ($i = 0; $i 在循环体中修改 $arr(如 unset())会导致跳过元素或 Notice;while (list($k, $v) = each($arr)) 第二次遍历时直接返回 false,因为指针已到末尾且未重置。
foreach 每次迭代都从原始数组副本取值,不依赖内部指针foreach 比 for 略快(尤其大数组),因避免了重复调用 count() 和边界检查关联数组不能靠数字下标假设顺序或存在性。foreach ($arr as $k => $v) 是唯一可靠方式;若只写 foreach ($arr as $v),会丢失键名,后续无法做字段映射或条件过滤。
使用场景举例:处理 HTTP 请求参数($_GET)、数据库查询结果(PDO::FETCH_ASSOC)、JSON 解码后的对象(json_decode($str, true))——这些结构天然无序且键名语义关键。
for ($i = 0; $i → 若键为 'user_id' 或 2001,$arr[0] 不存在
foreach ($arr as $key => $value) { if ($key === 'email') { /* 处理邮箱 */ } }
foreach ($arr as &$v) 启用引用后,必须在循环结束后 unset($v),否则下次循环可能污染数据PHP 的 foreach 默认按「快照」语义遍历——即开始时复制一份键列表,后续对原数组的 unset()、array_push() 不影响当前循环次数,但会影响后续迭代的值可见性。
性能 / 兼容性影响:PHP 8.0+ 对「遍历中追加元素」的行为做了明确定义(新增元素会被遍历到),而 PHP 7.x 中该行为未定义,不同 SAPI(CLI / FPM)可能表现不一致。
$toRemove = [];
foreach ($items as $k => $v) {
if ($v['status'] === 'deleted') {
$toRemove[] = $k;
}
}
foreach ($toRemove as $k) {
unset($items[$k]);
}foreach 中直接 unset($arr[$k]) 并期望它跳过下一个元素——这不会生效,仍会继续迭代原始键列表array_filter() 或 array_map() 更清晰遇到多维数组(如树形菜单、API 响应嵌套结构),是否用递归取决于层级是否固定、是否需中断或收集路径信息。盲目用 foreach 套 foreach 仅适用于已知深度(如二维),三层以上立刻变得难维护。
容易踩的坑:is_array($v) 判断不够,需排除对象(stdClass)或资源;递归函数未设最大深度限制,在恶意构造的超深数组上会爆栈。
array_walk_recursive(),但它跳过键名,只提供值和“扁平后”的键(丢失层级上下文)['users', '0', 'profile']),并在每层检查 max_depth
RecursiveArrayIterator + RecursiveIteratorIterato
r,支持 BOTTOM_UP/TOP_DOWN 遍历模式,适合复杂处理逻辑最常被忽略的一点:嵌套遍历时,foreach 的引用赋值(&$v)只作用于当前层级,子数组仍是值拷贝——若需深层修改,必须显式使用引用传递或返回新数组。