PHP 提供 16 個雙底線開頭的魔術方法,自動在特定生命週期或事件觸發,掌握它們能實現強大的元編程能力。
| 方法 | 觸發時機 | 典型用途 |
|---|---|---|
__construct() | 物件實例化 | 初始化屬性、注入依賴 |
__destruct() | 物件銷毀 (GC/腳本結束) | 釋放資源、關閉連線、寫入日誌 |
__call() | 呼叫不可存取的實例方法 | 動態代理、方法遺漏處理、流暢介面 |
__callStatic() | 呼叫不可存取的靜態方法 | 靜態工廠、動態查找 |
__get() | 讀取不可存取的屬性 | 虛擬屬性、延遲載入、資料轉換 |
__set() | 寫入不可存取的屬性 | 屬性驗證、自動轉型、觸發事件 |
__isset() | isset() / empty() 檢查不可存取屬性 | 虛擬屬性存在性判斷 |
__unset() | unset() 不可存取屬性 | 清理虛擬屬性、快取失效 |
__sleep() | serialize() 序列化前 | 指定序列化屬性、關閉資源句柄 |
__wakeup() | unserialize() 反序列化後 | 重新建立連線、初始化運行時狀態 |
__toString() | 物件作為字串使用 (echo/print) | 友善輸出、除錯資訊 |
__invoke() | 物件被當作函數呼叫 $obj() | 可呼叫物件、策略模式、閉包替代 |
__set_state() | var_export() 匯出物件 | 自定義匯出格式、單例還原 |
__clone() | clone 複製物件 | 深層複製、重置識別碼、複製相關資源 |
__debugInfo() | var_dump() 輸出物件 | 控制除錯輸出、隱藏敏感資料 |
<?php
class DynamicEntity
{
private $data = [];
private $relations = [];
// 動態屬性讀寫
public function __get($name) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
// 延遲載入關聯
if (isset($this->relations[$name])) {
return $this->relations[$name] = $this->relations[$name]();
}
return null; // 或拋出異常
}
public function __set($name, $value) {
$this->data[$name] = $value;
}
public function __isset($name) {
return isset($this->data[$name]) || isset($this->relations[$name]);
}
public function __unset($name) {
unset($this->data[$name], $this->relations[$name]);
}
// 動態方法代理 (如 $user->getPosts())
public function __call($method, $args) {
if (preg_match('/^get(.+)/', $method, $m)) {
$relation = lcfirst($m[1]);
return $this->$relation ?? null;
}
throw new BadMethodCallException("Method $method not found");
}
// 序列化控制
public function __sleep() {
return ['data']; // 僅序列化資料,不序列化關聯物件
}
public function __wakeup() {
$this->relations = []; // 重置關聯容器
}
// 友善輸出
public function __toString() {
return get_class($this) . '[' . json_encode($this->data) . ']';
}
// 可呼叫物件
public function __invoke($key) {
return $this->$key;
}
}
// 使用示範
$user = new DynamicEntity();
$user->name = 'John';
$user->relations['posts'] = fn() => ['Post 1', 'Post 2'];
echo $user->name; // John (觸發 __get)
echo $user->getPosts(); // ['Post 1', 'Post 2'] (觸發 __call)
echo $user; // DynamicEntity[{"name":"John"}] (觸發 __toString)
echo $user('name'); // John (觸發 __invoke)
?>
__wakeup 可能被惡意利用,敏感類別應實作 __sleep 限制屬性parent::__construct() 等__debugInfo() 過濾密碼、Token 等敏感欄位<?php
// PSR-4 自動載入 (推薦使用 Composer)
spl_autoload_register(function ($class) {
$prefix = 'App\\';
$baseDir = __DIR__ . '/src/';
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) return;
$relativeClass = substr($class, $len);
$file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
if (file_exists($file)) require $file;
});
// 或使用 Composer 自動產生的 autoload.php
require __DIR__ . '/vendor/autoload.php';
?>
魔術方法是 PHP 物件模型的進階特性,善用能簡化程式碼,濫用則增加複雜度,請在明確需要動態行為時採用。