手写PHP递归深度合并数组的核心是:同键且均为数组时递归合并,否则右值覆盖左值;支持多参数、保留键名、自动跳过非数组值。

PHP 手写递归深度合并数组,核心是判断值是否为数组:是则递归合并,否则用右侧值覆盖左侧值(或按需保留左侧)。下面是一个简洁、可靠、符合 PHP 原生 array_merge_recursive 行为但支持“覆盖式”深度合并的实现。
基础版:覆盖式深度合并(推荐)
该版本模仿 Laravel 的 Arr::mergeRecursive 逻辑,对相同键的数组进行递归合并,非数组值则以右数组为准:
function array_merge_deep($left, $right) {
$result = $left;
foreach ($right as $key => $value) {
if (isset($left[$key]) && is_array($left[$key]) && is_array($value)) {
$result[$key] = array_merge_deep($left[$key], $value);
} else {
$result[$key] = $value;
}
}
return $result;
}
- 支持关联键和数字键(数字键不会被重索引,保留原 key)
- 遇到同名且均为数组时递归调用自身
- 左有右无的键保留;右有左无的键直接添加;同名但类型不同时,右值完全覆盖左值
增强版:支持多数组参数与空值保护
支持传入任意数量数组,并跳过 null / 非数组参数:
function array_merge_deep(...$arrays) {
if (empty($arrays)) return [];
$result = [];
foreach ($arrays as $array) {
if (!is_array($array)) continue;
$result = _merge_two_deep($result, $array);
}
return $result;
}
function _merge_two_deep($left, $right) {
$result = $left;
foreach ($right as $key => $value) {
if (array_key_exists($key, $left) && is_array($left[$key]) && is_array($value)) {
$result[$key] = _merge_two_deep($left[$key], $value);
} else {
$result[$key] = $value;
}
}
return $result;
}
- 使用可变参数
...接收多个数组,更接近原生array_merge用法 - 内部用私有函数
_merge_two_deep封装两数组合并逻辑,避免递归污染公共接口 - 自动忽略非数组参数(如
null、string),提升鲁棒性
注意事项与常见坑
手写时容易忽略的细节:
-
数字键处理:PHP 原生
array_merge会重置数字键,但深度合并通常应保留结构。上述实现保持 key 不变,符合配置合并等常见场景 - 引用与拷贝:函数中所有操作都是值拷贝,不会修改原始数组;如需节省内存且确定源数据不再使用,可考虑引用传参 + unset 优化(一般不必要)
- 循环引用:纯数组不含引用时无需担心;若输入可能含对象或引用数组,需额外加 visited 标记防止无限递归(普通业务数组极少需要)
-
类型安全:建议在关键项目中加上
is_array()双重判断(如isset($left[$key]) && is_array(...)),避免 notice
简单测试示例
验证行为是否符合预期:
$a = ['user' => ['name' => 'Tom', 'roles' => ['admin']], 'status' => 'active']; $b = ['user' => ['email' => 't@x.com', 'roles' => ['editor']], 'meta' => ['v' => 1]]; print_r(array_merge_deep($a, $b)); // 输出: // [ // 'user' => ['name'=>'Tom', 'email'=>'t@x.com', 'roles'=>['admin','editor']], // 'status' => 'active', // 'meta' => ['v'=>1] // ]
-
user.roles是两个数组 → 合并为['admin', 'editor'](注意:这是追加而非去重) -
user.name左有右无 → 保留'Tom';user.email右有左无 → 添加进去 - 如需“右值优先覆盖,数组也覆盖而非追加”,把递归分支改为
$result[$key] = $value;即可(即彻底覆盖)
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











