PHP 5 引入魔術方法,以雙底線 __ 開頭命名,在特定生命週期或事件自動觸發。善用它們可實現屬性存取攔截、自動載入、物件序列化、函式式呼叫等進階功能。本文系統整理 11 個核心魔術方法的觸發時機、參數與實戰範例。
protected/private 不可見屬性)
class Entity {
private $data = [];
public function __get($name) {
return isset($this->data[$name]) ? $this->data[$name] : null;
}
public function __set($name, $value) {
$this->data[$name] = $value;
}
}
$e = new Entity();
$e->title = 'Hello'; // 觸發 __set
echo $e->title; // 觸發 __get
public function __isset($name) {
return isset($this->data[$name]);
}
public function __unset($name) {
unset($this->data[$name]);
}
isset($obj->prop)、unset($obj->prop) 觸發,同樣適用於不可見屬性。
public function __call($method, $args) {
if ($method === 'sayHello') {
return "Hello, {$args[0]}!";
}
throw new BadMethodCallException("Method $method not found");
}
$obj->sayHello('World'); // "Hello, World!"
public static function __callStatic($method, $args) {
// 靜態代理、工廠模式常用
}
ClassName::staticMethod(); // 觸發 __callStatic
注意:兩者必須宣告為 public,__callStatic 另需 static 關鍵字。
function __autoload($class) {
include "classes/$class.php";
}
缺點:只能定義一個、例外無法被 catch 捕獲(會致命錯誤)。
class Loader {
public static function autoload($class) {
$file = __DIR__ . "/src/$class.php";
if (file_exists($file)) require $file;
}
}
spl_autoload_register(['Loader', 'autoload']);
// 亦可用匿名函數
spl_autoload_register(function($class) {
// PSR-4 對應邏輯
});
| 方法 | 觸發時機 | 常見用途 |
|---|---|---|
__construct | 物件實例化時 | 初始化屬性、注入依賴、建立連線 |
__destruct | 物件銷毀前(記憶體釋放前) | 關閉連線、釋放資源、寫入日誌 |
class Database {
private $pdo;
public function __construct($dsn) {
$this->pdo = new PDO($dsn);
}
public function __destruct() {
$this->pdo = null; // 關閉連線
}
}
銷毀時機:函數內區域變數在 return 時;全域變數在腳本結束時;手動 unset($obj) 或賦值 null 時。
PHP 5 物件預設為參考指派,clone 關鍵字建立淺拷貝,觸發 __clone 可執行深拷貝邏輯。
class User {
public $name;
public $address; // 物件
public function __clone() {
$this->address = clone $this->address; // 深拷貝關聯物件
}
}
$u2 = clone $u1;
public function __toString() {
return "User: {$this->name}";
}
echo $user; // 自動觸發
string,否則拋出 E_RECOVERABLE_ERROR(PHP 5.2+)echo/print 觸發;5.2+ 所有字串語境(如 printf('%s', $obj))均觸發
public function __sleep() {
$this->db->close(); // 關閉連線
return ['name', 'email']; // 僅序列化指定屬性
}
public function __wakeup() {
$this->db = new Database(); // 重建連線
}
__sleep:serialize() 前調用,回傳需序列化屬性名陣列__wakeup:unserialize() 後調用,重建資源
public static function __set_state(array $props) {
$obj = new self();
foreach ($props as $k => $v) $obj->$k = $v;
return $obj;
}
var_export($obj, true) 產生的程式碼可直接 eval() 還原物件。
class CallableClass {
public function __invoke($x, $y) {
return $x + $y;
}
}
$fn = new CallableClass();
echo $fn(2, 3); // 5,如同呼叫函數
使物件可當作 callable 傳遞,配合 array_map、Closure::fromCallable 等極為強大。
詳見第 3 節,常用於靜態工廠、ActiveRecord 風格查詢:
User::findByEmail('test@example.com'); // 觸發 __callStatic
__callStatic 必須加 static__construct/__destruct/__clone 等不可被繼承覆寫為非 public魔術方法是 PHP 物件模型的擴展點,核心場景包括:
合理使用可大幅提升程式碼彈性,濫用則會降低可讀性,建議在框架核心、ORM、代理模式等場景採用。