PHP 支援以變數名稱動態呼叫函式($func())或實例化物件(new $class()),解析器會依變數值尋找對應函式或類別。這特性常用於路由分派、外掛架構、策略模式等情境。
<?php
// controller.php
$fun = (isset($_GET['fun']) && $_GET['fun'] !== '') ? $_GET['fun'] : 'def';
controller($fun);
function controller($fun) {
if (function_exists($fun)) {
$fun(); // 動態呼叫
} else {
echo "函式 {$fun} 未定義";
}
}
function def() { echo "預設函式 def()"; }
function a() { echo "函式 a() 被呼叫"; }
function b() { echo "函式 b() 被呼叫"; }
?>
請求 controller.php?fun=a 將輸出「函式 a() 被呼叫」;無參數則執行 def()。
<?php
require_once 'showErrMsg.php';
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
if ($action !== '' && $action !== null) {
if (function_exists($action)) {
eval("{$action}();"); // 極度危險!
} else {
die(showErrMsg("方法 [{$action}()] 不存在"));
}
}
?>
安全警示:eval() 會直接執行字串程式碼,若輸入未嚴格驗證,極易導致遠端程式碼執行(RCE)。生產環境嚴禁使用,請改用 call_user_func() 或白名單機制。
<?php
class A {
public function foo() {
echo isset($this) ? "\$this defined (" . get_class($this) . ")" : "\$this not defined";
}
}
class B {
public function bar() { A::foo(); } // 靜態呼叫
}
$a = new A(); $a->foo(); // 動態呼叫(有實例)
A::foo(); // 靜態呼叫(無實例)
$b = new B(); $b->bar(); // 物件內靜態呼叫
B::bar(); // 靜態呼叫
?>
| 類型 | 呼叫方式 | 有無實例 | $this 狀態 |
|---|---|---|---|
| 動態 | $obj->method() | new 實例後 | 指向實例 |
| 靜態 | Class::method() | 無實例 | 未定義 |
call_user_func([$obj, 'method']) 或 call_user_func('func') 替代直接變數呼叫,可搭配 is_callable() 驗證。$allowed = ['a','b','c'],僅允許名單內函式被動態呼叫。in_array($class, $allowedClasses) 防禦注入。eval()、create_function() 等高風險函式。善用動態呼叫特性可大幅簡化路由與擴充架構,但務必嚴守「輸入驗證」與「最小權限」原則。