php数组赋值默认浅拷贝,$new = $old 不产生独立副本;标量值互不影响,对象和引用仍共享;array_merge([], $arr)仅对纯标量一维数组有效;真正深拷贝需serialize/unserialize或手动递归处理。

PHP数组赋值默认是浅拷贝
直接用 $new = $old 得到的不是独立副本,而是对原数组的引用(对于非引用类型元素)或指针共享(对内部对象、引用变量)。改 $new['user'] 可能悄悄改掉 $old['user'],尤其当数组里存了对象或用了 & 引用时。
- 标量值(
int、string、bool)复制后互不影响 —— 这是“看起来像深拷贝”的假象 - 对象不复制实例,只复制对象句柄;改
$new['obj']->name会同步反映在$old['obj']上 - 含
&$ref的数组,两边共享同一内存地址,任一端修改都会穿透 - 嵌套数组本身是值复制,但嵌套里的对象/引用仍共享
用 array_merge([], $arr) 实现简单深拷贝(限一维+标量)
这个技巧常被误认为“通用深拷贝”,其实它只对纯标量一维数组可靠。本质是重建键值对,断开顶层关联,但不递归处理子数组或对象。
-
$copy = array_merge([], $original)对['a' => 1, 'b' => 'x']有效,新旧数组完全隔离 - 遇到
['data' => ['inner' => 99]]:外层数组分离了,但$copy['data']和$original['data']仍是同一个数组引用 - 含对象时无效:
['obj' => new StdClass()]中的StdClass实例仍被共用 - 性能比直接赋值略低,但可读性强,适合配置数组、表单数据等轻量场景
真正深拷贝必须递归 + 类型判断(对象要 clone)
PHP没有内置通用深拷贝函数,serialize/unserialize 是最常用兜底方案,但它有兼容性陷阱;手动递归则可控但易漏类型。
-
$deep = unserialize(serialize($arr))能处理嵌套数组、对象、资源外的绝大多数情况 - 失败场景:含不可序列化对象(如
Closure、部分资源句柄)、__sleep()报错、或启用了open_basedir限制 - 手动递归写法需区分:
is_array()→ 递归复制;is_object()→ 先clone再递归处理属性;is_resource()通常不能深拷,得特殊处理 - 第三方库如
symfony/var-exporter或spatie/array-to-xml的辅助工具类更健壮,但引入依赖需权衡
什么时候根本不需要深拷贝?
很多场景所谓“要复制”,其实是误解了PHP数组的值语义。过度深拷贝反而拖慢性能、掩盖设计问题。
立即学习“PHP免费学习笔记(深入)”;
- 函数参数传数组默认按值传递(PHP 7+),函数内修改不会影响调用方 —— 不用提前拷贝
- 循环中临时修改数组(如
foreach ($arr as &$v) { $v *= 2; }),改完再用原变量即可,无需备份 - 配置合并常用
array_replace_recursive(),它生成新数组且逻辑清晰,比盲目深拷贝更贴近意图 - 真要隔离状态,优先考虑封装成对象 + 明确的
copy()方法,而不是到处serialize
array_merge([], $arr) 或干脆不用拷贝。真正需要 serialize/unserialize 的,往往已经暴露了数据结构耦合过紧的问题。










