array_diff_assoc 不适用于多维数组,因其仅逐层比较顶层键名及值(数组值用 === 判断是否同一变量),不递归比对子数组内容。

array_diff_assoc 在 PHP 7.2 中只支持一维数组的键值对比,不递归处理多维结构。要实现“同时比对键和值”的**深度差异检测**,必须手动递归遍历,或借助自定义函数模拟 array_diff_assoc 的语义并扩展到多维。
为什么 array_diff_assoc 不适用于多维数组?
该函数仅逐层比较顶层键名与对应值(值为数组时,直接用 === 判断是否完全相同)。只要某个键对应的值是数组,哪怕内容一致,只要不是同一个数组变量,就会判定为不同——它不做递归值比较。
例如:
$a = ['user' => ['id' => 1, 'name' => 'A']]; $b = ['user' => ['id' => 1, 'name' => 'A']]; var_dump(array_diff_assoc($a, $b)); // 输出 [](巧合相等,因两个子数组字面量相同且 PHP 7.2+ 对类似字面量做了优化) // 但若 $b['user'] 是通过 new 数组或多次赋值生成,结果就可能非空,不可靠
实现多维键值深度对比的实用方法
核心思路:递归进入每个层级,对键名存在性、键名顺序、对应值类型及内容分别判断。推荐封装为函数 array_diff_assoc_deep:
立即学习“PHP免费学习笔记(深入)”;
- 先用
array_keys获取双方键列表,用array_diff找出 $array1 有而 $array2 没有的键 → 直接纳入差异结果 - 对共有的键,检查值是否均为数组:
- 都不是数组 → 用
==(松散)或===(严格)判断是否相等;建议用===保持与原函数一致语义 - 都是数组 → 递归调用自身比对,仅当子数组完全一致才跳过,否则将差异合并进结果
- 类型不一致(如一边是数组一边是字符串)→ 视为差异,保留该键值对
- 都不是数组 → 用
- 注意:PHP 数组键顺序敏感,
array_diff_assoc本身依赖顺序,递归实现也需保持键遍历顺序一致(使用原生foreach即可)
一个轻量可用的参考实现
以下函数兼容 PHP 7.2,支持任意嵌套深度,行为尽量贴近 array_diff_assoc 原意(键名 + 值全等):
function array_diff_assoc_deep($array1, $array2) {
$diff = [];
foreach ($array1 as $key => $value) {
if (!array_key_exists($key, $array2)) {
$diff[$key] = $value;
continue;
}
$other = $array2[$key];
if (is_array($value) && is_array($other)) {
$sub_diff = array_diff_assoc_deep($value, $other);
if (!empty($sub_diff)) {
$diff[$key] = $sub_diff;
}
} elseif ($value !== $other) {
$diff[$key] = $value;
}
}
return $diff;
}
用法示例:
$a = ['a' => 1, 'b' => ['x' => 10, 'y' => 20]]; $b = ['a' => 1, 'b' => ['x' => 10, 'y' => 25], 'c' => 99]; print_r(array_diff_assoc_deep($a, $b)); // 输出:Array ( [b] => Array ( [y] => 20 ) )
注意事项与替代建议
- 性能:深度递归在超大嵌套数组上可能影响性能,生产环境建议加递归深度限制(如传入 $depth 参数并递增校验)
- 循环引用:此实现不检测数组循环引用,若存在会导致无限递归甚至崩溃,业务中需预判或改用
spl_object_hash辅助标记 - 浮点数/NaN:PHP 中
NaN !== NaN,若数组含 NaN 值,会被当作差异;如有需要,可在比较前用is_nan()特殊处理 - 更健壮方案:如项目已用 Composer,可考虑
symfony/var-exporter或phpunit/phpunit的assertEquals差异输出逻辑,但属重型依赖,轻量场景不推荐



















