本文節選自《Core PHP Programming》第 15 節,由 PHP 核心開發者 Zeev Suraski 撰寫,Haohappy 翻譯整理。詳細記錄了 PHP 從純程序語言踏上物件導向之路的關鍵里程碑:PHP 3 的初步嘗試、PHP 4 引入參考機制卻帶來「WTMA 綜合症」、PHP 5 徹底重寫物件模型採用 Object Store 與控制代碼,實現真正的參考語意與高效能。
1997 年夏天開發 PHP 3 時,完全沒有物件導向計畫。1997/08/27 晚間,PHP 3 Alpha 版臨時加入類別支援。當時的物件模型極其簡陋:
$foo->bar 取代 $foo['bar']。$this。程式碼對比:
<?php
// PHP 3 物件寫法
class Example {
var $value = "some value";
function PrintValue() { print $this->value; }
}
$obj = new Example();
$obj->PrintValue();
// PHP 3 結構化寫法 (完全等效)
function PrintValue($arr) { print $arr["value"]; }
function CreateExample() {
return ["value" => "some value", "PrintValue" => "PrintValue"];
}
$arr = CreateExample();
$arr["PrintValue"]($arr); // 間接呼叫
?>
結論:PHP 3 的物件只是「語法糖」,無法支援設計模式與複雜 OOP 架構。
PHP 4 引入參考 概念 (&),允許多個變數指向同一記憶體位址,這是建立物件網絡的基礎。但 PHP 仍將物件視為普通資料類型:指派即複製。
<?php
class MyFoo {
function MyFoo() {
$this->me = &$this; // 建構子內建立自我參考
$this->value = 5;
}
function setValue($val) { $this->value = $val; }
function getValue() { return $this->value; }
function getValueFromMe() { return $this->me->value; }
}
function CreateObject($type) {
switch ($type) {
case "foo": $obj = new MyFoo(); break;
case "bar": $obj = new MyBar(); break;
}
return $obj; // 回傳時複製物件!
}
$global_obj = CreateObject("foo");
$global_obj->setValue(7);
print $global_obj->getValue(); // 輸出 7
print $global_obj->getValueFromMe(); // 輸出 5 (!!)
?>
new MyFoo() 回傳的是建構子內物件的副本,$this->me 仍指向原始物件,導致 $obj 與 $obj->me 脫節。CreateObject 回傳 $obj 時再次複製,$global_obj 與 $global_obj->me 再次脫節。return $obj 切斷了參考鏈結。&$obj =& new MyFoo(); // 28 行
$obj =& new MyBar(); // 31 行
$global_obj =& CreateObject("foo"); // 37 行
function &CreateObject($type) { ... } // 24 行
這就是著名的 WTMA (Way Too Many Ampersands) 綜合症 —— 程式碼滿是 &,極難維護。
PHP 5 是第一個將物件視為獨特資料類型的版本。核心改變:
&。clone 關鍵字)。| PHP 4 及之前 | PHP 5 |
|---|---|
所有值 (含物件) 統一存於 zval 結構 | 標量/陣列存 zval,物件存 Object Store |
| 傳遞/回傳時複製整個 zval | zval 僅存物件指標 (handle),複製 zval 只複製指標 |
| 修改副本不影響原物件 | 所有指標指向同一 Object Store 實體,修改即時生效 |