作者 Johan Persson(JpGraph 圖表庫開發者)總結 PHP 4 進行完整 OO 開發時的語義陷阱。針對有 C++/Java/Eiffel/C# 經驗的開發者,PHP 4 的「拷貝語義」與主流語言的「參考語義」差異極大,極易產生難以發現的 Bug。
PHP 4 中類別變數不是指標,而是物件本身。賦值操作 = 執行的是淺拷貝(建立新物件副本),而非建立別名。
function SomeFunction($aObj) { $aObj->Set(10); }
$myObj = new MyClass();
SomeFunction($myObj); // 預期修改 $myObj,實際修改副本!
修正:函數簽名加 & 強制參考傳遞:
function SomeFunction(&$aObj)
$myObject = new SomeClass(); $myRefToObject = &$myObject; // 正確建立參考 $myCopyRefToObject = $myRefToObject; // 錯誤!這會拷貝物件,而非拷貝參考 $myCopyRefToObject = &$myRefToObject; // 正確:拷貝參考本身
C++ 中參考的參考仍是原物件參考,PHP 4 卻會拷貝物件。
永遠使用參考傳遞物件或物件賦值:
✅ 效能更好(避免拷貝)
✅ 語義可預測(符合 C++/Java 直覺)
✅ 減少隱性 Bug
$this 參考問題在建構子中將 $this 註冊為觀察者(Observer 模式)時:
class Display {
function Display(&$batt) {
$batt->AddObserver("Notify", $this); // $this 此處是副本!
}
}
$myBattery = new Battery();
$myDisplay = new Display($myBattery); // 註冊的是 Display 副本
後續 Notify() 更新的是副本屬性,原 $myDisplay 不變。
&:$myDisplay = &new Display($myBattery);(呼叫端需知實作細節)$myDisplay = new Display();
$myDisplay->Init($myBattery);Init() 內安全使用 &$thisforeach 迴圈的隱性拷貝// 版本 1:foreach 隱性拷貝
foreach ($this->plots as $p) { $p->Update(); } // 修改副本,原陣列物件不變
// 版本 2:索引迴圈直接操作原物件
for ($i=0; $i
結論:foreach 遍歷物件陣列時會建立副本,需用索引迴圈或 PHP 5 的 foreach ($arr as &$v) 參考模式。
譯者註記
- [1] OO: Object-Oriented
- [2] 原文無 C#,譯者個人偏好補充
- [3] Semantic 譯為「語義」
- [4] 參考《C++ Gotchas》
- [5] 此處「類」應指 Instance(實例)
- [6] 參考 GoF《Design Patterns》Observer 模式
- [7] 經典買賣馬算術題:兩筆交易各賺 10,共賺 20(選項 C)