PHP 5 採用 Zend Engine II,物件儲存於 Object Store,變數僅持有指標。當你寫 $tv2 = $tv1 時,兩個變數指向同一個實例。修改其中一個屬性,另一個也會跟著變——這不是複製,而是參考傳遞。要得到獨立副本,必須使用 clone 關鍵字。
class Television {
protected $_color = 'black';
public function getColor() { return $this->_color; }
public function setColor($color) { $this->_color = (string)$color; return $this; }
}
$tv1 = new Television();
$tv2 = clone $tv1; // 真正複製
$tv2->setColor('white');
echo $tv1->getColor(); // black(未受影響)
echo $tv2->getColor(); // white
當物件被 clone 時,會自動呼叫 __clone()。常見用途:重設唯一識別碼、重新建立關聯物件。
class Television {
protected $_identity = 0;
protected $_color = 'black';
protected $_control = null;
public function __construct() { $this->_control = new Telecontrol(); }
public function getControl() { return $this->_control; }
public function setIdentity($id) { $this->_identity = (int)$id; return $this; }
public function getIdentity() { return $this->_identity; }
public function __clone() {
$this->setIdentity(0); // 重設編號
$this->_control = new Telecontrol(); // 重新建立遙控器(關鍵!)
}
}
class Telecontrol {}
預設 clone 為淺複製:基本型別屬性複製值,但物件屬性仍指向同一個實例。上例若不在 __clone 中 new Telecontrol(),兩台電視會共用同一支遙控器,違反現實邏輯。
優點:可控性高;缺點:類別屬性多、層級深時維護困難,且無法修改第三方類別。
利用 serialize() + unserialize() 遞迴複製整個物件圖,包含所有巢狀物件。
$tv1 = new Television();
$tv2 = unserialize(serialize($tv1)); // 深複製
// 驗證
echo spl_object_id($tv1->getControl()); // 不同 ID
echo spl_object_id($tv2->getControl()); // 不同 ID
注意:此法不會觸發 __clone,但會觸發 __sleep/__wakeup。若需兩者兼顧,可深複製後再 clone 一次(效能略有影響)。
__sleep 排除、__wakeup 重建。| 方式 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| 參考指派(=) | 需共享同一實例 | 效能最好 | 非獨立副本 |
| clone(淺複製) | 無巢狀物件、或僅需複製基本屬性 | 語法簡單、觸發 __clone | 關聯物件共享 |
| __clone 手動重建 | 關聯物件少、需客製化邏輯 | 完全可控 | 維護成本高 |
| 序列化深複製 | 複雜物件圖、第三方類別、需完整隔離 | 一次複製所有層級 | 效能較差、資源型別需特殊處理 |
PHP 5 的物件模型與 Java 相似,可參考 Java 的 Cloneable、序列化、複製建構子等最佳實踐。實務上建議:優先考慮不可變物件設計,減少複製需求;必須複製時,根據物件複雜度選擇淺/深複製,並撰寫單元測試驗證獨立性。