PHP 5 引入 __clone() 魔術方法,在 clone $obj 時自動調用。克隆預設為淺拷貝(引用型屬性共享同一對象)。若需在克隆後修改副本屬性,必須在 __clone() 中重新賦值。此時有兩個特殊指標:
$this:指向新建立的副本(克隆結果)。$that:指向原始對象(被克隆者)。<?php
class Person {
public $name;
public $sex;
public $age;
public function __construct($name, $sex, $age) {
$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}
public function say() {
echo "我叫:{$this->name},性別:{$this->sex},年齡:{$this->age}
";
}
public function __clone() {
// $this 是副本,$that 是原本
$this->name = "我是複製的 " . $that->name;
// $this->age = 30; // 也可修改其他屬性
}
}
$p1 = new Person("張三", "男", 20);
$p2 = clone $p1;
$p1->say(); // 原本
$p2->say(); // 副本(已在 __clone 中改名)
我叫:張三,性別:男,年齡:20
我叫:我是複製的 張三,性別:男,年齡:20
| 指標 | 指向 | 可修改 | 用途 |
|---|---|---|---|
$this | 副本(新對象) | 是 | 初始化副本專屬狀態(如重設 ID、時間戳、關聯對象) |
$that | 原本(舊對象) | 否(僅讀) | 讀取原本屬性以計算副本初始值 |
$this->id = null; 讓 ORM 視為新實體插入。$this->address = clone $that->address;$this->created_at = new DateTime();$this->version = $that->version + 1;$that 是標準變數:它只在 __clone() 內部可用,且不可重新賦值($that = ... 會報錯)。__clone():繼承鏈中需手動 parent::__clone();。__clone() 執行深拷貝。序列化/反序列化同樣會建立新對象,但不會觸發 __clone()。若需統一邏輯,可提取共用方法:
private function initCopy($source) {
$this->id = null;
$this->created_at = new DateTime();
$this->address = clone $source->address;
}
public function __clone() { $this->initCopy($that); }
public function __wakeup() { $this->initCopy($this); /* 此處 $this 已是反序列化產生的新對象 */ }
__clone() 中的 $this/$that 雙指標設計極具匠心:$this 面向未來(副本),$that 回顧過去(原本)。掌握此機制,即可精確控制對象複製行為,避免共享狀態導致的難以排查 Bug。