魔術方法是 PHP 保留的特殊方法名稱(雙底線開頭),在特定生命週期或事件觸發時自動被呼叫,無需手動調用。它們賦予物件「生命」,讓開發者能介入物件的建立、銷毀、存取、轉換等關鍵時刻。
當物件被當作字串使用時(echo $obj、字串串接、print 等)自動觸發。必須 return 字串,不能直接 echo。
<?php
class User {
public function __construct(public string $name, public string $email) {}
public function __toString(): string {
return "User: {$this->name} <{$this->email}>";
}
}
$user = new User("張三", "zhang@example.com");
echo $user; // 自動呼叫 __toString()
// 輸出:User: 張三 <zhang@example.com>
return json_encode($this)呼叫不存在或不可存取的實例方法時觸發。適用於:動態代理、錯誤處理、流暢介面、魔術 getter/setter。
class APIClient {
private $endpoint = 'https://api.example.com';
public function __call(string $name, array $arguments): mixed {
// 動態映射方法名到 API 端點
$endpoint = "/{$name}";
$params = $arguments[0] ?? [];
return $this->request('POST', $endpoint, $params);
}
public static function __callStatic(string $name, array $arguments): mixed {
$instance = new self();
return $instance->$name(...$arguments);
}
private function request(string $method, string $endpoint, array $data): array {
// 實際 HTTP 請求邏輯...
return ['status' => 'ok', 'endpoint' => $endpoint];
}
}
$client = new APIClient();
$result = $client->getUsers(['limit' => 10]); // 不存在 getUsers(),觸發 __call()
// 實際呼叫 $client->request('POST', '/getUsers', ['limit' => 10])
存取不存在或不可見的屬性時觸發。常用於:資料映射、延遲載入、權限控制。
class Entity {
private array $data = [];
private array $loaded = [];
public function __set(string $name, mixed $value): void {
$this->data[$name] = $value;
}
public function __get(string $name): mixed {
if (!isset($this->data[$name]) && !$this->loadRelation($name)) {
throw new Exception("屬性 {$name} 不存在");
}
return $this->data[$name];
}
public function __isset(string $name): bool {
return isset($this->data[$name]) || $this->loadRelation($name);
}
private function loadRelation(string $name): bool {
if (isset($this->loaded[$name])) return false;
// 模擬從資料庫載入關聯資料
$this->data[$name] = "載入的 {$name} 資料";
$this->loaded[$name] = true;
return true;
}
}
$entity = new Entity();
$entity->title = "新文章"; // 觸發 __set()
echo $entity->title; // 觸發 __get()
echo isset($entity->content) ? "存在" : "不存在"; // 觸發 __isset() -> loadRelation() -> true
clone 關鍵字建立物件副本時觸發。預設為淺層複製(引用型屬性仍指向同一物件)。__clone() 可自訂深層複製邏輯。
class Document {
public function __construct(
public string $title,
public array $tags = [],
public ?Author $author = null
) {}
public function __clone(): void {
// 深層複製作者物件(避免副本修改影響原物件)
if ($this->author instanceof Author) {
$this->author = clone $this->author;
}
// 複製標籤陣列(預設淺層複製陣列值已足夠,但這裡演示明確複製)
$this->tags = array_values($this->tags);
}
}
class Author {
public function __construct(public string $name) {}
}
$original = new Document("原文件", ['php', 'oop'], new Author("張三"));
$copy = clone $original;
$copy->author->name = "李四"; // 修改副本作者
echo "原作者:{$original->author->name}"; // 張三(未受影響)
echo "副本作者:{$copy->author->name}"; // 李四
注意:__autoload() 已在 PHP 7.2 棄用,PHP 8.0 移除。現代專案請使用 spl_autoload_register() 或 Composer PSR-4 自動載入。
<?php
// 專案啟動檔(如 bootstrap.php)
spl_autoload_register(function (string $class): void {
// PSR-4 對應:App\Controller\UserController -> src/Controller/UserController.php
$prefix = 'App\\';
$baseDir = __DIR__ . '/src/';
if (str_starts_with($class, $prefix)) {
$relativeClass = substr($class, strlen($prefix));
$file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
if (file_exists($file)) {
require $file;
}
}
});
// 或直接使用 Composer 自動載入(推薦)
// require __DIR__ . '/vendor/autoload.php';
| 方法 | 觸發時機 | 典型用途 |
|---|---|---|
__construct() | new 實例化時 | 初始化屬性、注入依賴 |
__destruct() | 物件銷毀/腳本結束 | 關閉連線、釋放資源、記錄日誌 |
__invoke() | 物件當函式呼叫 $obj() | 建立可呼叫物件、策略模式、中介軟體 |
__sleep() | serialize($obj) 前 | 指定序列化哪些屬性、關閉資源 |
__wakeup() | unserialize($data) 後 | 重建連線、初始化資源 |
__serialize() | PHP 7.4+ 序列化新機制 | 自訂序列化資料結構(取代 __sleep) |
__unserialize() | PHP 7.4+ 反序列化新機制 | 自訂反序列化邏輯(取代 __wakeup) |
__set_state() | var_export() 匯出類別時 | 自訂靜態工廠方法重建物件 |
__debugInfo() | var_dump($obj) 時 | 控制除錯輸出內容、隱藏敏感資料 |
__call、__get 等攔截器有額外開銷,高頻呼叫路徑應避免。@method、@property 註解。__serialize/__unserialize 時注意資料驗證,防止物件注入攻擊。@method、@property、@property-read 註解聲明動態成員,支援 IDE 提示與靜態分析。PHP 魔術方法是強大的元編程工具:__toString 讓物件可直接輸出、__call/__get 實現動態行為、__clone 控制複製語意、__serialize 管理持久化。善用它們能寫出優雅的 API、ORM、代理模式等,但需謹記「明確優於隱性」,在團隊專案中建立使用規範,避免濫用導致維護困難。