PHP 常用魔術方法速查表:觸發條件、返回值規範與實戰代碼片段
魔術方法以 __ 開頭,在特定操作時自動觸發。本文整理 13 個核心魔術方法的觸發條件、參數、返回值規範,提供最小可用代碼片段,作為開發時的快速參考卡片。
生命周期類
| 方法 | 觸發條件 | 參數 | 返回值 | 代碼片段 |
__construct | new Class() | 任意 | void | public function __construct(private Db $db) {} |
__destruct | 對象銷毀 (引用歸零/腳本結束) | 無 | void | public function __destruct() { $this->db->close(); } |
__clone | clone $obj 後 | 無 | void | public function __clone() { $this->id = Uuid::v4(); } |
屬性重載類
| 方法 | 觸發條件 | 參數 | 返回值 | 代碼片段 |
__get | 讀取不可訪問屬性 $obj->prop | string $name | mixed | public function __get($n) { return $this->data[$n] ?? null; } |
__set | 寫入不可訪問屬性 $obj->prop = $v | string $name, mixed $value | void | public function __set($n, $v) { $this->data[$n] = $v; } |
__isset | isset($obj->prop) / empty() | string $name | bool | public function __isset($n) { return isset($this->data[$n]); } |
__unset | unset($obj->prop) | string $name | void | public function __unset($n) { unset($this->data[$n]); } |
方法重載類
| 方法 | 觸發條件 | 參數 | 返回值 | 代碼片段 |
__call | 調用不可訪問實例方法 $obj->method() | string $name, array $args | mixed | public function __call($m, $a) { return $this->proxy->$m(...$a); } |
__callStatic | 調用不可訪問靜態方法 Class::method() | string $name, array $args | mixed | public static function __callStatic($m, $a) { return self::$factory->$m(...$a); } |
序列化類
| 方法 | 觸發條件 | 參數 | 返回值 | 代碼片段 |
__sleep | serialize($obj) 前 | 無 | array (屬性名) | public function __sleep() { return ['data', 'config']; } |
__wakeup | unserialize($str) 後 | 無 | void | public function __wakeup() { $this->reconnect(); } |
__serialize | serialize($obj) (7.4+) | 無 | array (鍵值對) | public function __serialize() { return ['v'=>2, 'data'=>$this->data]; } |
__unserialize | unserialize($str) (7.4+) | array $data | void | public function __unserialize($d) { $this->data=$d['data']; } |
轉換與調試類
| 方法 | 觸發條件 | 參數 | 返回值 | 代碼片段 |
__toString | 對象作字符串 echo $obj | 無 | string | public function __toString() { return $this->name; } |
__invoke | 對象作函數 $obj() | 任意 | mixed | public function __invoke($x) { return $this->fn($x); } |
__set_state | var_export($obj) (靜態) | array $props | object | public static function __set_state($p) { return new self($p); } |
__debugInfo | var_dump($obj) (5.6+) | 無 | array | public function __debugInfo() { return ['id'=>$this->id, 'secret'=>'***']; } |
完整演示類
class MagicDemo {
private $data = [];
private $resource;
// 構造/析構
public function __construct() {
$this->resource = fopen('php://memory', 'r+');
echo "__construct\n";
}
public function __destruct() {
fclose($this->resource);
echo "__destruct\n";
}
// 屬性重載
public function __get(string $name) {
echo "__get($name)\n";
return $this->data[$name] ?? null;
}
public function __set(string $name, $value) {
echo "__set($name, $value)\n";
$this->data[$name] = $value;
}
public function __isset(string $name): bool {
echo "__isset($name)\n";
return isset($this->data[$name]);
}
public function __unset(string $name) {
echo "__unset($name)\n";
unset($this->data[$name]);
}
// 方法重載
public function __call(string $name, array $args) {
echo "__call($name, " . json_encode($args) . ")\n";
return "called $name";
}
public static function __callStatic(string $name, array $args) {
echo "__callStatic($name, " . json_encode($args) . ")\n";
return "static $name";
}
// 序列化 (新協議)
public function __serialize(): array {
echo "__serialize\n";
return ['data' => $this->data, 'version' => 1];
}
public function __unserialize(array $data): void {
echo "__unserialize\n";
$this->data = $data['data'] ?? [];
}
// 舊協議 (兼容)
public function __sleep(): array {
echo "__sleep\n";
return ['data'];
}
public function __wakeup(): void {
echo "__wakeup\n";
$this->resource = fopen('php://memory', 'r+');
}
// 轉換/調試
public function __toString(): string {
return "MagicDemo(data=" . count($this->data) . ")";
}
public function __invoke(string $action) {
return "invoked: $action";
}
public static function __set_state(array $props): self {
$obj = new self();
$obj->data = $props['data'] ?? [];
return $obj;
}
public function __debugInfo(): array {
return ['data_keys' => array_keys($this->data), 'resource' => '***'];
}
public function __clone() {
echo "__clone\n";
$this->resource = fopen('php://memory', 'r+');
}
}
// 測試腳本
$obj = new MagicDemo();
$obj->name = 'test'; // __set
echo $obj->name; // __get
isset($obj->name); // __isset
unset($obj->name); // __unset
$obj->unknownMethod(1, 2); // __call
MagicDemo::staticMethod(); // __callStatic
$serialized = serialize($obj); // __serialize -> __sleep
unserialize($serialized); // __unserialize -> __wakeup
echo $obj; // __toString
$obj('action'); // __invoke
eval('$obj2 = ' . var_export($obj, true) . ';'); // __set_state
var_dump($obj); // __debugInfo
$cloned = clone $obj; // __clone
關鍵規範總結
- 不可
static 聲明:__get/__set/__isset/__unset/__call 必須為實例方法,PHP 5.3+ 靜態聲明拋出 E_DEPRECATED。
__toString 必須返回字符串:拋出異常或返回非字符串觸發 E_RECOVERABLE_ERROR 並終止。
__sleep 返回非數組:導致 serialize() 返回 null 並 E_NOTICE。
__wakeup 安全風險:反序列化可實例化任意類,必須配合 allowed_classes 白名單。
- 遞歸陷阱:
__get 中訪問 $this->prop 會再次觸發 __get,內部用 $this->data['prop']。
- 繼承調用:子類覆蓋時通常需
parent::__construct()、parent::__destruct()、parent::__clone()。
魔術常量速查
__LINE__ // 當前行號 (int)
__FILE__ // 文件完整路徑 (string)
__DIR__ // 文件目錄 (string, 等同 dirname(__FILE__))
__FUNCTION__ // 函數名 (string)
__CLASS__ // 類名含命名空間 (string)
__TRAIT__ // Trait 名 (string)
__METHOD__ // 類方法名 (string)
__NAMESPACE__// 當前命名空間 (string)
將此頁面加入書籤,開發遇到魔術方法即查即用,避免踩坑。