PHP递归遍历数组需手动实现,核心是is_array()判断后递归调用自身;必须显式处理键名、注意引用传递、避免array_walk_recursive的局限性,深度过大时宜用栈模拟。

PHP递归遍历数组时,foreach嵌套调用自己最直接
PHP本身没有内置“递归遍历数组”的单一函数,得靠自己写逻辑。核心就是:遇到子数组就再调一次当前函数,其他类型值直接处理。别想用array_walk_recursive替代——它跳过键名、不支持自定义结构展开,很多场景下根本不够用。
常见错误是漏判is_array(),导致foreach对字符串或数字报Invalid argument supplied for foreach();或者忘了传引用/返回值,结果修改没生效、数据丢了。
- 必须用
is_array($item)判断,不能只靠!is_scalar()(对象、resource也会过) - 如果要修改原数组,参数得加
&$arr;如果只是读取或生成新结构,按需返回即可 - 深度很大时注意
max_execution_time和xdebug.max_nesting_level限制(默认100),调试时容易卡死
带键名的递归遍历,foreach ($arr as $key => $value)是基础模板
多数真实需求要保留键名(比如处理API返回的嵌套JSON、配置数组、树形菜单)。这时候不能偷懒只遍历值,必须显式拆出$key和$value,再决定是否递进。
示例:把所有字符串值转为大写,同时保持层级和键名不变:
立即学习“PHP免费学习笔记(深入)”;
function uppercaseRecursive(&$arr) {
foreach ($arr as $key => &$value) {
if (is_array($value)) {
uppercaseRecursive($value);
} elseif (is_string($value)) {
$arr[$key] = strtoupper($value);
}
}
}
注意&$value和$arr[$key] = ...这两处——前者保证子数组内修改能回溯到父级,后者避免对非引用标量赋值失效。
用array_walk_recursive前先确认它真能满足需求
array_walk_recursive确实省事,但它会“扁平化”所有嵌套:自动跳过键名、不暴露层级信息、遇到对象直接跳过、无法中断或控制遍历顺序。一旦你需要做这些事,它立刻掉链子。
典型不适用场景:
- 要把
['user' => ['name' => 'alice']]转成['user_name' => 'alice'](需要拼接键路径) - 遇到
'status' => 'error'就立刻退出整个遍历(它不支持break) - 子数组里混着对象,想统一序列化(它会跳过对象)
真要用,记住它只接受三参数回调:function ($value, $key, $userdata),且$key是最终叶子节点的键,不是完整路径。
递归深度大时,改用栈模拟更稳
PHP默认递归深度上限低,xdebug开启时尤其敏感。如果确定数组可能超200层(比如解析超深XML转数组、日志嵌套结构),硬递归容易Fatal error: Maximum function nesting level。这时改用显式栈(stack)更可控。
思路:把待处理的数组和当前路径压入栈,循环出栈、逐层展开,用array_unshift()或array_push()维护顺序:
$stack = [[$data, []]]; // [$current_array, $path]
while (!empty($stack)) {
[$arr, $path] = array_pop($stack);
foreach ($arr as $k => $v) {
$newPath = array_merge($path, [$k]);
if (is_array($v)) {
$stack[] = [$v, $newPath];
} else {
// 处理 $v,路径在 $newPath 里
}
}
}
栈方式不依赖函数调用栈,内存占用可预估,也方便加深度限制(比如count($newPath) > 50就跳过)。但代价是代码略长,调试路径变量比递归直观性差一点。
真正麻烦的不是写法,而是分不清“要不要保留键”“改原数组还是建新数组”“深度会不会爆”,这几个点没提前想清楚,后面全得重来。



















