array_merge_recursive不能扁平化数组,仅递归合并;真正降维需用array_walk_recursive提取叶子值,或自定义递归函数拼接键路径保留键名。

array_merge_recursive 会保留多维结构,不是真正“扁平化”
很多人看到 array_merge_recursive 就以为能“把二维变一维”,结果发现键名冲突时自动嵌套、关联键没消失、数值键被重排——它只是递归合并,不是降维。比如:
$arr = ['a' => ['x' => 1], 'b' => ['y' => 2]]; var_dump(array_merge_recursive($arr)); // 还是二维,且 'a' 和 'b' 键还在
真正要的是“所有叶子值提上来,丢掉中间层级”,得自己遍历或用更精准的工具。
用 array_walk_recursive 最简洁可靠
array_walk_recursive 天然跳过中间数组,只对最终标量值回调,适合提取所有叶子节点:
- 它自动忽略非标量(如子数组),不报错也不中断
-
回调函数里直接
array_push或赋值到新数组即可 - 不关心原数组是数值索引还是关联键,只取值
示例:
立即学习“PHP免费学习笔记(深入)”;
$data = ['user' => ['name' => 'Alice', 'age' => 30], 'status' => 'active'];
$result = [];
array_walk_recursive($data, function($val) use (&$result) {
$result[] = $val;
});
// $result === ['Alice', 30, 'active']
注意:array_walk_recursive 不保留原始键名,如果需要键值对映射,得换方式。
要保留原始键名?得手动递归 + 拼接键路径
关联数组降维后还想区分 'user.name' 和 'user.age',就不能靠内置函数了。必须自己写递归,拼出点号分隔的键名:
- 每层递归传入当前键路径(初始为空字符串)
- 遇到子数组就继续递归,路径追加
"{$prefix}.{$key}" - 遇到标量值就写入结果:
$flat[$new_key] = $val - 注意空字符串开头的键要裁掉开头的点(
ltrim($key, '.'))
示例片段:
function flatten_assoc($arr, $prefix = '') {
$result = [];
foreach ($arr as $k => $v) {
$new_key = $prefix === '' ? $k : $prefix . '.' . $k;
if (is_array($v)) {
$result += flatten_assoc($v, $new_key);
} else {
$result[$new_key] = $v;
}
}
return $result;
}
调用 flatten_assoc($data) 得到 ['user.name' => 'Alice', 'user.age' => 30, 'status' => 'active']。
性能和兼容性:别在大数组上用 array_walk_recursive + 引用闭包
PHP 7.4+ 对 use (&$ref) 闭包优化较好,但若数组嵌套深、元素超万级,递归函数比 array_walk_recursive 更可控(可加深度限制、提前退出)。另外:
-
array_walk_recursive在 PHP 5.2.1+ 都支持,无兼容问题 - 自定义递归函数要注意栈溢出风险,深层嵌套建议改用栈模拟(while + array_pop)
- 如果原数组含对象或资源,
array_walk_recursive会跳过它们,而手动递归可以加类型判断处理
真正麻烦的从来不是“怎么写出来”,而是“原数组里混着 null、对象、闭包、循环引用”——这些情况,任何扁平化逻辑都得先做类型过滤或引用检测,否则直接崩。











