PHP 允許將函數名作為字符串存儲在變量中,**直接在變量後加括號即可調用** (Variable Functions)。這是動態調用的基礎。
| 方式 | 語法 | 支援對象 | 性能 | 適用場景 |
|---|---|---|---|---|
| 變量函數 | $func = 'strlen'; $func($str); | 函數、靜態方法、實例方法 (PHP 7+) | ⭐⭐⭐⭐⭐ (最快) | 簡單回調、路由分發 |
| call_user_func | call_user_func('strlen', $str); | 同上 + 可調用對象 | ⭐⭐⭐⭐ (微弱開銷) | 參數數組動態構建 |
| call_user_func_array | call_user_func_array('func', [$a, $b]); | 同上,參數為數組 | ⭐⭐⭐⭐ | 參數數量不固定 |
| Closure::call / bindTo | $fn = fn() => ...; $fn->call($obj, $arg); | 閉包、綁定作用域 | ⭐⭐⭐⭐ | 依賴注入、中間件、異步 |
| ReflectionMethod::invoke | (new ReflectionMethod($cls, $m))->invoke($obj, $arg); | 類方法、可訪問 private/protected | ⭐⭐ (反射開銷大) | 框架內核、註解掃描、測試 |
class Router {
private $routes = [];
public function add(string $pattern, callable $handler) {
$this->routes[$pattern] = $handler;
}
public function dispatch(string $uri) {
foreach ($this->routes as $pattern => $handler) {
$regex = '#^' . str_replace('/', '\/', $pattern) . '$#';
if (preg_match($regex, $uri, $matches)) {
array_shift($matches); // 移除整體匹配
return call_user_func_array($handler, array_values($matches));
}
}
throw new Exception('404 Not Found');
}
}
// 使用
$router = new Router();
$router->add('user/(\d+)', function($id) { echo "User $id"; });
$router->add('post/(\w+)', 'handlePost'); // 字符串函數名
$router->dispatch('user/123'); // 輸出 User 123
function handlePost($slug) { echo "Post $slug"; }
class Service {
public function process(string $type, array $data) {
$method = 'handle' . ucfirst($type);
if (method_exists($this, $method)) {
return $this->$method($data); // 變量方法調用
}
throw new Exception("Unsupported type: $type");
}
protected function handleOrder(array $data) { return 'Order processed'; }
protected function handlePayment(array $data) { return 'Payment processed'; }
}
$func = 'strlen'; $str = 'test';
// 1. 變量函數 0.42s
// 2. call_user_func 0.48s
// 3. call_user_func_array 0.55s
// 4. Closure::call 0.50s
// 5. Reflection 3.20s
**結論**:熱路徑優先用 **變量函數** 或 **直接調用**;需動態參數數組用 call_user_func_array;反射僅用於初始化/掃描階段。
echo, print, isset, empty, include, require, exit, die, list, eval 不是函數,無法動態調用。需包裝:function myEcho($s){ echo $s; }。'App\Helper\format' 或在當前命名空間下。call_user_func 同理。['ClassName', 'method'] 或 'ClassName::method' (PHP 7+)。動態函數調用是 PHP 靈活性的核心體現。**掌握五種方式的差異、性能、適用場景**,並避開命名空間、語言構造、可見性等陷阱,即可編寫優雅的路由、插件系統、事件分發器、中間件管道等核心基礎設施。