本文目录导读:

在 PHP 中,克隆对象时,默认情况下属性不会同步修改,但具体行为取决于属性的类型(值类型或引用类型)。
默认浅克隆(clone 关键字)
PHP 的 clone 关键字执行浅克隆(shallow copy):
class User {
public $name;
public $address; // 对象类型
public function __construct($name, $address) {
$this->name = $name;
$this->address = $address;
}
}
$address = new stdClass();
$address->city = 'Beijing';
$user1 = new User('Alice', $address);
$user2 = clone $user1;
// 修改 user2 的 name(值类型)
$user2->name = 'Bob';
echo $user1->name; // 输出 Alice(不受影响 ✅)
// 修改 user2 的 address(对象类型)
$user2->address->city = 'Shanghai';
echo $user1->address->city; // 输出 Shanghai(受影响 ❌)
不同属性类型的行为
| 属性类型 | 克隆后行为 | 示例 |
|---|---|---|
| 基本类型(int, string, bool) | 独立副本,修改互不影响 | |
| 数组 | 独立副本,但数组内的对象仍共享引用 | ⚠️ 需注意 |
| 对象 | 共享引用,修改会同步 | |
| 资源 | 行为依赖于资源类型,通常共享 |
数组的特殊情况
class Team {
public $members = [];
public function addMember($name) {
$this->members[] = new Person($name);
}
}
$team1 = new Team();
$team1->addMember('Alice');
$team2 = clone $team1;
$team2->members[0]->name = 'Bob';
echo $team1->members[0]->name; // 输出 Bob(受影响 ❌)
虽然数组本身是独立副本,但数组中的对象仍然是共享引用。
实现深克隆(__clone() 魔术方法)
如果需要完全独立的副本,必须在 __clone() 方法中手动复制引用类型的属性:
class User {
public $name;
public $address;
public function __clone() {
// 深克隆地址对象
$this->address = clone $this->address;
}
}
$user1 = new User('Alice', $address);
$user2 = clone $user1;
$user2->address->city = 'Shanghai';
echo $user1->address->city; // 输出 Beijing(不受影响 ✅)
序列化实现深克隆(另一种方法)
$user2 = unserialize(serialize($user1));
注意:
- 资源类型(如数据库连接)不能序列化
- 某些对象可能包含不能序列化的属性
- 性能开销较大
使用 clone vs 直接赋值
// 直接赋值 - 共享引用 $user2 = $user1; // 两个变量指向同一个对象 $user2->name = 'Bob'; echo $user1->name; // Bob(同步修改) // clone - 创建新对象 $user2 = clone $user1; // 独立对象 $user2->name = 'Bob'; echo $user1->name; // Alice(不修改)
- 默认
clone:基本类型独立,对象类型共享(浅克隆) - 需要深克隆:在
__clone()中显式克隆所有引用类型的属性 - 数组中的对象:注意它们仍然是共享引用
- 序列化方法:可以实现彻底深克隆,但有额外开销
最佳实践:根据业务需求选择合适的克隆策略,在处理复杂对象图时,考虑实现 __clone() 或使用专门的克隆工具类。