PHP保留雙底線__開頭的方法名為魔術方法,在特定生命週期或操作觸發時自動被呼叫。掌握這11個核心魔術方法,能讓類別設計更靈活、API更直觀。
class PropertyProxy {
private $data = [];
public function __get($name) {
return $this->data[$name] ?? null; // 讀取未宣告/不可見屬性時觸發
}
public function __set($name, $value) {
$this->data[$name] = $value; // 寫入未宣告/不可見屬性時觸發
}
public function __isset($name) {
return isset($this->data[$name]); // isset()檢查未宣告屬性時觸發
}
public function __unset($name) {
unset($this->data[$name]); // unset()操作未宣告屬性時觸發
}
}
關鍵點:這四個方法處理的是「未宣告」或「不可見」(private/protected)屬性。可用於實作動態屬性、資料代理、驗證邏輯集中化。
class MethodProxy {
public function __call($method, $args) {
// 實例方法不存在時觸發,$args為參數陣列
echo "呼叫實例方法: $method(" . implode(', ', $args) . ")";
}
public static function __callStatic($method, $args) {
// 靜態方法不存在時觸發(PHP 5.3+)
echo "呼叫靜態方法: $method(" . implode(', ', $args) . ")";
}
}
應用場景:動態API代理、流暢介面建構器、方法委派模式。
class ResourceManager {
private $handle;
public function __construct($config) {
$this->handle = fopen($config['path'], 'w'); // 物件建立時自動取得資源
}
public function __destruct() {
if (is_resource($this->handle)) {
fclose($this->handle); // 物件銷毀前釋放資源
}
}
}
銷毀時機:函數內區域變數在return時;全域變數在腳本結束時;手動unset($obj)或賦值null時。
class DeepCloneable {
private $resource;
public function __clone() {
// 淺層複製後執行深層複製邏輯
$this->resource = clone $this->resource; // 避免共用同一資源導致雙重釋放
}
}
PHP 5採用參考賦值,$b = $a不複製物件,需用clone關鍵字觸發__clone。
class User {
public $name;
public function __toString() {
return "User: {$this->name}"; // echo $user 直接輸出
}
}
PHP 5.2+支援所有字串語境(printf("%s", $obj)),未實作時拋出E_RECOVERABLE_ERROR。
class DBConnection {
private $pdo;
private $config;
public function __sleep() {
return ['config']; // 僅序列化配置,排除PDO連線資源
}
public function __wakeup() {
$this->pdo = new PDO(...$this->config); // 反序列化時重建連線
}
}
應用:關閉資料庫連線、提交待寫入資料、排除大型暫存資料、反序列化重建資源。
class Config {
public static function __set_state($array) {
$obj = new self();
foreach ($array as $k => $v) $obj->$k = $v;
return $obj;
}
}
// var_export($config) 會產生可執行的PHP程式碼並呼叫 __set_state
class CallableClass {
public function __invoke($x, $y) {
return $x + $y;
}
}
$fn = new CallableClass();
echo $fn(2, 3); // 輸出 5,物件可直接當函數呼叫
配合閉包與函數式編程模式極具表達力。
// 現代寫法
spl_autoload_register(function($class) {
$file = __DIR__ . "/src/{$class}.php";
if (file_exists($file)) require $file;
});
// 類別式載入器
class Loader {
public static function autoload($class) { /* ... */ }
}
spl_autoload_register(['Loader', 'autoload']);
// new Test() 將自動觸發載入
注意:__autoload中拋出的例外無法被catch捕獲,會導致致命錯誤,務必內部處理。
__call、__callStatic、__get、__set、__isset、__unset、__invoke 必須宣告為public__callStatic 必須宣告為static善用魔術方法能打造極具表達力的DSL風格API,但濫用會降低程式碼可讀性與除錯難度,建議在框架核心、庫開發中謹慎應用。