call_user_func 與 call_user_func_array 是 PHP 動態呼叫函式/方法的核心工具,廣泛用於框架路由、事件分發、插件機制。本文深入解析語法差異、常見錯誤(如 list 關鍵字衝突)、物件方法呼叫技巧與效能考量。
<?php
function add($a, $b) { return $a + $b; }
// call_user_func:參數逐一傳遞
echo call_user_func('add', 3, 5); // 8
// call_user_func_array:參數以陣列傳遞
echo call_user_func_array('add', [3, 5]); // 8
?>
核心差異:call_user_func_array 適合參數數量動態、或參數已在陣列中的場景(如 ...$args 解包前的相容寫法)。
<?php
class Math {
public static function sum($a, $b) { return $a + $b; }
public function mul($a, $b) { return $a * $b; }
}
// 靜態方法:['Class', 'method'] 或 'Class::method'
call_user_func(['Math', 'sum'], 2, 3);
call_user_func('Math::sum', 2, 3);
// 實例方法:[$obj, 'method']
$obj = new Math();
call_user_func([$obj, 'mul'], 4, 5); // 20
?>
⚠️ PHP 7.1+ 支援 Closure::call() 綁定 $this,效能優於 call_user_func。
官方文件明確指出:**不能用於語言結構**,包括 array()、echo()、empty()、eval()、exit()、isset()、list()、print()、unset()。
<?php
class User {
// 錯誤:方法名稱為 list,與語言結構 list() 衝突
public function list() { /* ... */ }
}
// 呼叫時會拋出 Parse error
call_user_func([$user, 'list']); // ❌
// 解決:改用 __call 魔術方法或重新命名
// 或使用 Closure 包裝
call_user_func(fn() => $user->list()); // ✅
?>
...$args 解包運算子,$fn(...$args) 直接呼叫,速度快 2-3 倍。Closure::call($obj, ...$args) 綁定物件上下文,避免建立陣列開銷。<?php
class EventDispatcher {
private $listeners = [];
public function on(string $event, callable $handler) {
$this->listeners[$event][] = $handler;
}
public function emit(string $event, ...$args) {
foreach ($this->listeners[$event] ?? [] as $handler) {
call_user_func_array($handler, $args);
}
}
}
$dispatcher = new EventDispatcher();
$dispatcher->on('order.created', function($orderId) { echo "訂單 $orderId 建立\n"; });
$dispatcher->emit('order.created', 'ORD-2024001');
?>
動態呼叫功能強大但需謹慎使用:**避開關鍵字衝突、優先考慮 ...$args 直呼、型別提示搭配 callable 介面**,才能寫出可維護、高效能的程式碼。