在PHP开发中,把多个数组变量合并成一个数组是极其常见的操作。无论是合并配置项、整合表单数据,还是组装接口返回结构,我们都会面对“如何合并”的选择。PHP提供了多种数组合并手段,其中最容易被混淆的就是array_merge函数与加号(+)运算符。它们表面上都能把数组连起来,但在键名处理逻辑上有本质不同。
一、array_merge函数的基本用法
array_merge是PHP内置的数组函数,用来将一个或多个数组的单元合并起来。它接收一个或多个数组参数,返回合并后的新数组。对于字符串键名(关联数组),如果后面的数组中有相同键名,那么后面的值会覆盖前面的;对于数字键名(索引数组),所有元素会被按顺序追加,并且重新从0开始建立连续的数字索引。
下面是一段基础示例,展示关联数组与索引数组在array_merge下的表现:
<?php
$configA = array(
'host' => '127.0.0.1',
'port' => 3306,
'debug' => false
);
$configB = array(
'port' => 3307,
'user' => 'root'
);
$merged = array_merge($configA, $configB);
print_r($merged);
// 输出:
// Array (
// [host] => 127.0.0.1
// [port] => 3307 // 后面的覆盖前面的
// [debug] => false
// [user] => root
// )
$listA = array('a', 'b');
$listB = array('c', 'd');
$listMerged = array_merge($listA, $listB);
print_r($listMerged);
// 输出:Array ( [0] => a [1] => b [2] => c [3] => d )
?>
从上面可以看出,array_merge对字符串键采取“后者覆盖前者”的策略,对数字键则重新编号。这种特性在合并用户配置与默认配置时非常实用,因为通常希望用户传进来的配置项生效。
不过要注意,如果传入的数组本身不是数组类型(比如null),array_merge会把null当作空数组处理,不会报错;但如果传入的是标量值则会产生警告。因此在使用前最好用is_array做一下校验,或者确保变量一定是数组。
二、加号运算符的合并逻辑
PHP中的加号(+)也可以用于数组合并,但它的语义和array_merge完全不同。加号运算的规则是:以左侧数组为基础,右侧数组中的元素只有在左侧数组中不存在对应键名时才会被加入。也就是说,左侧数组的键值具有更高优先级,不会被右侧覆盖。
我们用和前面一样的关联数组来做对比:
<?php
$configA = array(
'host' => '127.0.0.1',
'port' => 3306,
'debug' => false
);
$configB = array(
'port' => 3307,
'user' => 'root'
);
$added = $configA + $configB;
print_r($added);
// 输出:
// Array (
// [host] => 127.0.0.1
// [port] => 3306 // 保留左侧的,未被覆盖
// [debug] => false
// [user] => root // 左侧没有的键才加进来
// )
$listA = array('a', 'b');
$listB = array('c', 'd');
$listAdded = $listA + $listB;
print_r($listAdded);
// 输出:Array ( [0] => a [1] => b )
// 数字键0和1已存在于左侧,因此右侧的不会被添加
?>
可以看到,加号运算符对数字索引数组几乎“无合并效果”,因为索引0、1在左侧已存在,右侧的值全部被忽略。这一点经常让初学者感到意外,以为加号也能像array_merge那样拼接列表,其实并不能。
加号更适合用来设置“默认值”场景:把用户选项放在左边,默认选项放在右边,用户没填的才用默认的。但如果你需要把两个索引列表拼起来,加号是错误选择。
三、两者核心差异对比
为了更直观地理解,我们把两种方式的差异整理成表格:
| 对比维度 | array_merge | 加号运算符 |
|---|---|---|
| 字符串键冲突 | 后面覆盖前面 | 前面保留,后面忽略 |
| 数字键处理 | 重新索引,顺序追加 | 键已存在则忽略右侧 |
| 典型用途 | 用户配置覆盖默认配置 | 默认配置补充用户缺失项 |
| 多维数组 | 一维合并,不递归 | 一维合并,不递归 |
从表中能清楚看到,两者不是简单的“语法糖替换”关系,而是语义不同的操作。在代码评审中,如果看到有人用加号去合并两个索引数组,基本可以判定这是一个bug。
此外,当数组合并涉及多维结构时,无论是array_merge还是加号,都只会合并第一层。子数组会被整体覆盖或保留,而不会逐字段合并。这时就需要递归合并函数。
四、多维数组的递归合并
如果数组是多维的,比如默认配置里包含数据库子配置,用户配置只改了其中一项,我们希望子配置也能按字段合并,那么应该使用array_merge_recursive,或者自己写递归逻辑。array_merge_recursive会把同名字符串键下的值合并成数组,但有时会产生意料之外的嵌套,因此更可控的做法是手动实现递归覆盖。
示例:使用自定义函数实现“后者覆盖前者”的递归合并
<?php
function deep_merge($base, $override) {
if (!is_array($base)) {
return $override;
}
foreach ($override as $key => $value) {
if (is_array($value) && isset($base[$key]) && is_array($base[$key])) {
$base[$key] = deep_merge($base[$key], $value);
} else {
$base[$key] = $value;
}
}
return $base;
}
$default = array(
'db' => array('host' => '127.0.0.1', 'port' => 3306),
'cache' => true
);
$user = array(
'db' => array('port' => 3307)
);
$result = deep_merge($default, $user);
print_r($result);
// 输出:db.host保留127.0.0.1,db.port变成3307,cache为true
?>
这种写法比array_merge_recursive更安全,因为它不会把同键下的标量值变成数组,而是直接覆盖。在框架配置加载、环境变量合并等场景中非常实用。
总结来说,PHP数组合并没有唯一正确答案,关键看业务语义:要覆盖就用array_merge或递归覆盖,要保底就用加号。理解键名处理规则,才能写出符合预期的数据整合代码。
PHP数组合并array_merge加号运算符修改时间:2026-08-11 13:39:46