PHP中的深拷贝数组:神话与现实
\n\n想象一下这个场景:您正在编写一个处理数据库数据的脚本。您获取了一个用户数组,对其进行了一些操作,然后突然发现原始数据发生了变化。熟悉吗?如果您使用PHP,这个问题甚至可能让经验丰富的开发者措手不及。
\n\nStack Overflow上的问题“Is there a function to make a copy of a PHP array to another?” 获得了数万次浏览。这并非偶然。乍一看,在PHP中复制数组似乎是一项简单的任务。但魔鬼,一如既往,隐藏在细节之中。
\n\n在本文中,我们将探讨PHP中复制数组的所有可能方法:从简单的赋值到处理多维结构。您将了解PHP何时创建真正的副本,何时仅创建数据的引用。我们开始吧!
\n\n直接赋值:第一个惊喜
\n\n最直观的方法就是直接将数组赋值给另一个变量:
\n\n$original = ['apple', 'banana', 'cherry'];\n$copy = $original;\n$copy[0] = 'mango';\n\nprint_r($original);\n// Array ( [0] => apple [1] => banana [2] => cherry )\nprint_r($copy);\n// Array ( [0] => mango [1] => banana [2] => cherry )\n\n令人惊讶的是,这居然有效!在PHP中,数组是值类型,而不是引用类型。当您将一个数组赋值给另一个变量时,PHP会创建一个带有写时复制机制的浅拷贝。
\n\n简单来说:只要您不修改任何一个数组,它们就指向内存中的同一份数据。一旦您尝试修改其中一个变量中的元素,PHP就会分配新的内存并复制数据。这非常高效,无需额外操作。
\n\n结论:对于简单的一维数组,直接赋值就是完整的复制。
\n\n陷阱:数组中的对象
\n\n现在想象一下,数组中包含对象:
\n\nclass User {\n public $name;\n public function __construct($name) {\n $this->name = $name;\n }\n}\n\n$original = [new User('Alice'), new User('Bob')];\n$copy = $original;\n$copy[0]->name = 'Charlie';\n\nprint_r($original[0]->name); // Charlie\n\n惊喜来了!浅拷贝复制的是对象的引用,而不是对象本身。修改副本中的对象,也会改变原始对象。对于对象,需要采用不同的方法。
\n\narray_merge()函数:不明显的细微差别
\n\n经常有人建议使用array_merge()来复制数组:
$original = [1, 2, 3];\n$copy = array_merge([], $original);\n$copy[0] = 100;\n\nprint_r($original); // [1, 2, 3]\n\n是的,这有效。但有一个重要的细微差别:array_merge()会重置数字键并从零开始重新编号。如果您的键是字符串,则没有问题。但如果是数字键,请准备好迎接惊喜:
$original = ['a' => 1, 2 => 'two', 5 => 'five'];\n$copy = array_merge([], $original);\n\nprint_r($copy);\n// Array ( [a] => 1 [0] => two [1] => five )\n\n看到了吗?键2和5变成了0和1。如果键的顺序很重要,这可能会破坏逻辑。
\n\n结论:array_merge()仅适用于复制具有字符串键的数组,或者当重新索引不会造成问题时。
使用循环复制:手动控制
\n\n最可靠但也是最冗长的方法是遍历数组:
\n\n$original = ['x' => 10, 'y' => 20];\n$copy = [];\n\nforeach ($original as $key => $value) {\n $copy[$key] = $value;\n}\n\n$copy['x'] = 100;\nprint_r($original['x']); // 10\n\n对于简单数组来说,这有些冗余。但如果您需要进行深拷贝,那么没有递