正确写法是用递归判断每个元素是否为数组,是则继续展开,否则加入结果;避免引用传递、频繁array_merge和array_walk_recursive的键名丢失问题。

PHP 递归函数 flatten 多维数组的正确写法
直接用 array_merge(...$arr) 或 array_values() 无法处理嵌套层级不确定的数组,必须靠递归。核心是判断每个元素是否为数组,是则继续展开,否则收进结果。
常见错误是忘记重置键名、混淆引用传递、或在递归中意外修改原数组。
- 用
is_array()判断子项,不是isset()或!empty() - 避免使用
&$result引用传参——容易在多层调用中污染变量,改用返回值拼接更安全 - 不要在递归体里用
array_merge($result, $sub)频繁重建数组,小数据无感,但千级嵌套时性能明显下降
为什么 array_walk_recursive 不适合“真正”的扁平化
array_walk_recursive() 看似省事,但它会跳过键名为数字的中间数组(比如 [0 => [1,2], 1 => [3,4]] 中的外层数字键),且无法保留原始键名结构(如果需要的话)。它只适用于“提取叶子值”,不等于“扁平化数组结构”。
例如:
$arr = ['a' => [1, 2], 'b' => ['c' => [3]]];
array_walk_recursive($arr, function($v) { echo $v; }); // 输出 123,但丢失了 'a'/'c' 这类非叶子键信息
- 如果你只要所有标量值,
array_walk_recursive足够快且简洁 - 如果你要还原成带顺序索引的一维数组(如
[0=>1, 1=>2, 2=>3]),必须手写递归 - 如果你需保留路径键名(如
['a_0'=>1, 'a_1'=>2, 'b_c_0'=>3]),那得额外加参数控制键生成逻辑
兼容 PHP 5.6+ 的通用 flatten 函数示例
以下函数支持任意深度、混合键名(字符串/数字)、不修改原数组、返回重新索引的数值数组:
function flatten_array($arr) {
$result = [];
foreach ($arr as $item) {
if (is_array($item)) {
$result = array_merge($result, flatten_array($item));
} else {
$result[] = $item;
}
}
return $result;
}
- 输入
[[1,2], [3,[4,5]], 6]→ 输出[1,2,3,4,5,6] - 若需保留键(如
['a'=>[1,2], 'b'=>[3]]→['a_0'=>1, 'a_1'=>2, 'b_0'=>3]),把$result[] = $item换成$result[$key_path] = $item,并递归传入当前路径字符串 - PHP 7.4+ 可用箭头函数简化,但可读性未必更好,不建议在生产环境盲目替换
深层嵌套时的栈溢出和性能提醒
PHP 默认递归限制是 100 层(由 xdebug.max_nesting_level 控制),遇到深度超限会报 Fatal error: Maximum function nesting level of '100' reached。这不是代码错,是配置问题。
- 先确认是否真需要那么深的嵌套——多数业务场景 5~10 层已顶天
- 临时调高限制:在脚本开头加
ini_set('xdebug.max_nesting_level', 500);(仅开发环境) - 生产环境更稳妥的做法是改用栈模拟递归(while + array_pop),但代码复杂度上升,除非明确测出递归成为瓶颈
- 对超大数组(>10k 元素),优先考虑迭代+生成器(
yield)流式处理,避免内存峰值
递归本身没玄学,关键是分清你要的是“值提取”还是“结构展平”,以及是否在意键名、性能边界和错误上下文。这两者选错,后面全白调。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











