在PHP开发中,处理多维数组的合并是常见需求,当两个数组合并时需要保留深层结构,按照键名递归合并对应的值,普通的array_merge函数无法满足这种场景,需要自定义递归逻辑来实现。

普通数组合并的局限性
PHP内置的array_merge函数处理多维数组时,只会合并第一层的键,如果键名相同且对应的值都是数组,只会保留后面数组的数组值,不会继续合并数组内部的键。比如下面这个例子:
<?php
$arr1 = [
'user' => [
'name' => '张三',
'age' => 20,
'hobby' => ['篮球', '足球']
],
'config' => ['debug' => true]
];
$arr2 = [
'user' => [
'age' => 22,
'hobby' => ['羽毛球'],
'gender' => '男'
],
'config' => ['cache' => false]
];
$result = array_merge($arr1, $arr2);
print_r($result);
运行上面的代码,输出的结果中user.hobby会被后面的数组直接覆盖,不会保留原有的篮球和足球,config数组也只会保留后面的cache键,丢失了debug键,不符合递归合并的预期。
递归合并函数的实现思路
要实现按结构键递归合并两个同构多维数组,核心逻辑是遍历两个数组的所有键,对每个键对应的值做如下判断:
- 如果两个数组的当前键对应的值都是数组,就递归调用合并函数处理这两个子数组
- 如果只有一个数组的当前键对应的值是数组,或者两个都不是数组,就按照需求选择保留哪个值,通常选择保留第二个数组的值,或者根据业务需求自定义规则
- 遍历完所有键之后,返回合并后的新数组,避免修改原数组
完整的递归合并代码实现
下面是通用的递归合并函数,支持两个同构多维数组的深层键合并:
<?php
/**
* 递归合并两个同构多维数组
* @param array $arr1 第一个数组
* @param array $arr2 第二个数组
* @return array 合并后的数组
*/
function recursive_array_merge(array $arr1, array $arr2): array {
// 遍历第二个数组的所有键
foreach ($arr2 as $key => $value) {
// 如果第一个数组存在该键,且两个值都是数组,递归合并
if (isset($arr1[$key]) && is_array($arr1[$key]) && is_array($value)) {
$arr1[$key] = recursive_array_merge($arr1[$key], $value);
} else {
// 否则直接用第二个数组的值覆盖或新增
$arr1[$key] = $value;
}
}
return $arr1;
}
// 测试数据
$arr1 = [
'user' => [
'name' => '张三',
'age' => 20,
'hobby' => ['篮球', '足球']
],
'config' => ['debug' => true]
];
$arr2 = [
'user' => [
'age' => 22,
'hobby' => ['羽毛球'],
'gender' => '男'
],
'config' => ['cache' => false]
];
$merged = recursive_array_merge($arr1, $arr2);
print_r($merged);
运行上面的测试代码,输出的合并结果如下:
Array
(
[user] => Array
(
[name] => 张三
[age] => 22
[hobby] => Array
(
[0] => 篮球
[1] => 足球
[2] => 羽毛球
)
[gender] => 男
)
[config] => Array
(
[debug] => 1
[cache] =>
)
)
自定义合并规则扩展
如果业务场景需要自定义合并规则,比如数组类型的值不合并而是直接覆盖,或者数字索引的数组需要追加而不是替换,可以修改递归函数中的判断逻辑。比如要保留数字索引数组的原有值,不合并索引,可以调整判断条件:
<?php
function recursive_array_merge_custom(array $arr1, array $arr2): array {
foreach ($arr2 as $key => $value) {
// 字符串键且两个都是数组才递归合并,数字键直接追加
if (is_string($key) && isset($arr1[$key]) && is_array($arr1[$key]) && is_array($value)) {
$arr1[$key] = recursive_array_merge_custom($arr1[$key], $value);
} else {
// 数字键追加,字符串键覆盖
if (is_int($key)) {
$arr1[] = $value;
} else {
$arr1[$key] = $value;
}
}
}
return $arr1;
}
注意事项
使用递归合并函数时需要注意几个问题:
- 该函数假设两个数组是同构的,也就是对应键的值类型一致,如果结构不同可能会出现预期外的结果
- 递归深度过深的多维数组可能会导致栈溢出,PHP默认递归深度限制可以通过
ini_set调整,但建议尽量避免过深的数组结构 - 如果需要合并多个数组,可以循环调用该函数,比如
$result = recursive_array_merge($arr1, $arr2); $result = recursive_array_merge($result, $arr3);