最近在做一个电商项目,需要把商品属性合并到一起。本来以为array_merge就能搞定,结果发现这货会把数字key重排,字符串key倒是保留。当时我就懵逼了——老子精心设计的数组下标就这么被糟蹋了?
先来看个简单例子:
$arr1 = ['color' => 'red', 100 => 'test'];
$arr2 = ['size' => 'XL', 100 => 'override'];
print_r(array_merge($arr1, $arr2));
输出结果会让你怀疑人生:
Array
(
[color] => red
[0] => test
[size] => XL
[1] => override
)
看到没?数字key 100被无情地重置成了0和1,而字符串key完好无损。这特么是什么鬼设计?后来查文档才发现,array_merge这货对数字键名的处理就是这么不讲武德。
要解决这个问题,我试了几种方案:
方案一:+ 运算符
简单粗暴的$arr1 + $arr2确实能保留key,但有个致命问题——它不会覆盖重复key的值。也就是说如果两个数组有相同的key,前面数组的值会保留,后面的直接被忽略。这显然不符合大多数合并场景的需求。
方案二:array_replace
这个函数终于靠谱点了:
$result = array_replace($arr1, $arr2);
print_r($result);
输出:
Array
(
[100] => override
)
完美!数字key保留了,后面的值也覆盖了前面的。但是...等等,这函数是PHP5.3才引入的,要是遇到老系统怎么办?
方案三:手动循环
只能自己撸代码了:
function mergeKeepKeys(array $arr1, array $arr2) {
foreach($arr2 as $key => $value) {
$arr1[$key] = $value;
}
return $arr1;
}
这个土办法虽然丑,但兼容性好。不过要注意几个坑:
1. 如果数组很大,性能会受影响
2. 多维数组需要递归处理
3. 要小心引用传递的问题
说到多维数组,事情就复杂了。比如这样的数据结构:
$products = [
'p001' => [
'name' => '手机',
'attrs' => ['color' => 'black']
]
];
$updates = [
'price' => 3999,
'attrs' => ['memory' => '128G']
这时候简单的合并就不够用了。我写了个递归合并函数:
function deepMerge(array $arr1, array $arr2) {
$merged = $arr1;
foreach ($arr2 as $key => &$value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = deepMerge($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
这个版本处理了多维数组,但还有改进空间:
1. 没有处理对象类型的值
2. 循环引用会导致无限递归
3. 对大数组内存占用较高
性能优化方面,我做过测试:在PHP7.4下,处理两个各有10万元素的数组:
array_merge: 0.012秒
array_replace: 0.015秒
手动循环: 0.025秒
递归合并: 0.12秒
所以要根据实际场景选择方案。如果是配置合并这种低频操作,用递归没问题;要是高频调用的核心逻辑,就得考虑更高效的实现。
说到配置合并,这可能是最常用的场景了。比如:
$defaultConfig = [
'debug' => false,
'db' => [
'host' => 'localhost',
'port' => 3306
]
];
$userConfig = [
'host' => '127.0.0.1',
'password' => '123456'
这时候用array_replace_recursive最合适:
$finalConfig = array_replace_recursive($defaultConfig, $userConfig);
但现实往往更复杂。有次我遇到个坑:两个配置数组都有SplFileInfo对象作为值,合并后对象引用出了问题,导致后续操作把文件锁死了。所以对于对象合并要特别小心,最好先序列化再合并。
还有个冷门技巧:用array_merge_recursive处理特定场景。这个函数会把相同key的值合并成数组:
$arr1 = ['key' => 'value1'];
print_r(array_merge_recursive($arr1, $arr2));
输出:
Array
(
[key] => Array
(
[0] => value1
)
这在处理日志合并时很有用,但大多数时候反而会造成数据混乱。
最后分享一个真实案例:有次线上故障,就是因为数组合并时key处理不当。两个系统对接,A系统发来的数据是['100'=>'订单1'],B系统处理后变成[0=>'订单1'],结果ID丢失导致后续流程全部出错。后来用array_replace修复,但已经造成了损失。
总结几个要点:
1. 数字key要用array_replace
2. 多维数组用array_replace_recursive
3. 超大数组考虑分批处理
4. 重要数据合并前先备份
5. 写单元测试验证合并逻辑
记住,在PHP的世界里,数组合并不是简单的1+1=2,而是要看清楚每个key的宿命。就像我那个电商项目,最后用了三层合并策略:先用array_replace处理基础属性,再用递归合并处理规格参数,最后用特殊逻辑处理SKU组合。代码虽然丑,但至少不会半夜被运维打电话叫起来修bug。
最后的最后,如果你非要问我PHP数组合并的终极解决方案是什么?我的答案是:改用Python。