PHP 5.3 引入 __invoke() 魔術方法,標誌著 PHP 邁向「可調用對象」編程範式。本文詳細解析 __invoke 的語法、調用規則、is_callable 判斷、實戰場景(策略模式、中間件、閉包替代),並附帶完整魔術方法速查表。
<?php
class Greeter
{
public function __construct(private string $prefix = 'Hello') {}
public function __invoke(string $name): string
{
return "{$this->prefix}, {$name}!";
}
}
$greeter = new Greeter('Hi');
echo $greeter('World'); // Hi, World!
echo $greeter('PHP'); // Hi, PHP!
// 判斷可調用性
var_dump(is_callable($greeter)); // true
var_dump(is_callable([$greeter, '__invoke'])); // true
?>
<?php
class CallableClass
{
public function __invoke($x)
{
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5); // int(5)
var_dump(is_callable($obj)); // bool(true)
?>
$obj($arg) 等價於 $obj->__invoke($arg)。__invoke 時會向父類查找,同 __call。array_map($greeter, ['A','B'])、usort($arr, new Comparator)。<?php
// 策略對象
class JsonEncoder { public function __invoke($data) { return json_encode($data, JSON_UNESCAPED_UNICODE); } }
class XmlEncoder { public function __invoke($data) { /* ... */ } }
// 上下文
class Exporter
{
public function __construct(private $encoder) {}
public function export(array $data): string
{
return ($this->encoder)($data); // 直接調用策略對象
}
}
$exporter = new Exporter(new JsonEncoder());
echo $exporter->export(['status' => 'ok']); // {"status":"ok"}
?>
<?php
interface MiddlewareInterface
{
public function __invoke(Request $request, callable $next): Response;
}
class AuthMiddleware implements MiddlewareInterface
{
public function __invoke(Request $request, callable $next): Response
{
if (!$request->hasHeader('Authorization')) {
return new Response(401, [], 'Unauthorized');
}
return $next($request);
}
}
// 構建鏈
$chain = [
new AuthMiddleware(),
new RateLimitMiddleware(),
fn($req) => new Response(200, [], 'OK'),
];
$dispatch = function (Request $req) use ($chain) {
$next = array_shift($chain);
return $next($req, $dispatch);
};
?>
| 方法 | 觸發時機 | 典型用途 |
|---|---|---|
__construct | 實例化 new Class() | 依賴注入、屬性初始化 |
__destruct | 對象銷毀(GC/腳本結束) | 釋放資源、關閉連接、寫入日誌 |
__call | 調用不可訪問實例方法 | 代理、動態方法、流式接口 |
__callStatic | 調用不可訪問靜態方法 | 靜態工廠、ActiveRecord 查詢 |
__get | 讀取不可訪問屬性 | 虛擬屬性、懶加載、數組訪問 |
__set | 寫入不可訪問屬性 | 屬性驗證、觸發事件、脏標記 |
__isset | isset($obj->prop) | 虛擬屬性存在性判斷 |
__unset | unset($obj->prop) | 清理虛擬屬性 |
__sleep | serialize($obj) | 指定序列化屬性、關閉資源 |
__wakeup | unserialize($str) | 重建連接、初始化資源 |
__serialize | PHP 7.4+ serialize() | 精確控制序列化數據結構 |
__unserialize | PHP 7.4+ unserialize() | 精確控制反序列化邏輯 |
__toString | 對象作字符串使用 echo $obj | 調試輸出、模板渲染、ID 展示 |
__invoke | $obj() | 可調用對象、策略、中間件、閉包替代 |
__set_state | var_export() 導出 | 自定義導出結構 |
__clone | clone $obj | 深拷貝、重置 ID、復制關聯對象 |
__debugInfo | var_dump($obj) (PHP 5.6+) | 隱藏敏感屬性、格式化調試輸出 |
__autoload 已棄用(PHP 7.2 移除),請使用 spl_autoload_register。__wakeup 中拋出的異常 無法被 catch 捕獲,會導致致命錯誤,請內部 try-catch 處理。__toString 拋出異常會觸發 E_RECOVERABLE_ERROR,PHP 7.4+ 允許拋出 Throwable。__invoke 讓 PHP 對象具備了一級函數特性,是函數式編程風格在 PHP 中落地的關鍵基礎設施。