PHP 的物件導向機制中,最強大也最具彈性的特性莫過於「魔術方法」。這些以雙底線(__)開頭的特殊方法,能讓我們在不修改外部呼叫代碼的情況下,攔截並重新定義類別的預設行為,極大地減少了冗餘代碼,同時賦予了語法極高的擴展性。
PHP 保留所有以 __ 開頭的方法名稱作為魔術方法,常見的包括:
__construct()、__destruct():建構與解構__call()、__callStatic():動態方法呼叫攔截__get()、__set()、__isset()、__unset():屬性存取攔截__sleep()、__wakeup():序列化與反序列化控制__toString()、__invoke()、__set_state()、__clone():型別轉換、可呼叫物件、匯出與複製注意: 除非刻意使用魔術功能,否則自訂方法應避免以 __ 開頭,以免產生衝突。
__get、__set、__isset、__unset當嘗試存取「未宣告」或「不可見」(private/protected)的屬性時,這四個方法會被自動觸發:
public function __get(string $name) // 讀取未定義屬性
public function __set(string $name, mixed $value) // 寫入未定義屬性
public function __isset(string $name) // isset() 檢查未定義屬性
public function __unset(string $name) // unset() 刪除未定義屬性
這讓我們能實作「虛擬屬性」、資料驗證、或是將屬性存取導向資料庫/快取等儲存層。
__call 與 __callStatic呼叫不存在或不可見的方法時,__call() 會介入處理;同理,__callStatic() 處理靜態方法呼叫(PHP 5.3.0+)。兩者皆必須宣告為 public,後者還需加上 static 關鍵字。這是實作 Fluent Interface、動態代理或 RPC 客戶端的關鍵。
__construct、__destruct__construct 後,更改類別名稱不再需同步修改建構函式。__clonePHP 5 採用參考賦值,若需深層複製物件,必須使用 clone 關鍵字,此時 __clone() 會被自動觸發。常用於複製時重新初始化資源(如重新建立資料庫連線、重設唯一識別碼)。
__toString
當物件被當作字串使用(如 echo $obj、printf("%s", $obj))時自動呼叫。必須回傳字串,否則拋出 E_RECOVERABLE_ERROR。PHP 5.2.0 後支援所有字串語境,但不支援非字串格式(如 %d)。方法內不可拋出例外。
__sleep、__wakeup__sleep():serialize() 前呼叫,回傳需序列化的屬性名稱陣列。可用於清理資源、排除大型不需儲存的屬性。不可回傳父類私有屬性,否則產生 E_NOTICE。__wakeup():unserialize() 後呼叫,常用於重建資料庫連線、重新初始化資源。__invoke當物件被當作函數呼叫($obj())時觸發,讓物件擁有「可呼叫」特性,配合閉包可實現策略模式、中介軟體鏈等進階模式。
__set_statevar_export() 匯出類別時觸發(PHP 5.1.0+),接收屬性陣列並回傳物件實例,常用於程式碼生成、快取匯出等場景。
__get/__set 實作資料模型的「虛擬欄位」與驗證邏輯。__call 建立動態 API 客戶端,減少重複的方法定義。__sleep/__wakeup 處理資源密集型物件的序列化,避免儲存不必要的連線資源。__toString 讓領域物件在日誌、除錯輸出中更具可讀性。魔術方法是 PHP 物件導向的「隱形引擎」,掌握它們能讓你寫出更優雅、可維護且極具擴展性的代碼。