eval() 將字串作為 PHP 代碼執行,屬於語言結構而非函數。它賦予了極大的動態性,卻也是安全漏洞的溫床。
void eval ( string $code_str )
$code_str 必須是**合法的 PHP 代碼**,以分號結尾。<?php
$string = '杯子';
$name = '咖啡';
$str = '這個 $string 中裝有 $name.
';
echo $str; // 輸出:這個 $string 中裝有 $name.
eval("\$str = \"$str\";"); // 雙引號:變量先被解析
echo $str; // 輸出:這個 杯子 中裝有 咖啡.
/*
解析過程:
1. 雙引號字串 "\$str = \"$str\";" 先解析變量:
$str 變為 "這個 杯子 中裝有 咖啡.
"
2. eval 實際執行的代碼:
$str = "這個 杯子 中裝有 咖啡.
";
*/
?>
<?php
// 錯誤:雙引號導致 $a 被預解析為空/舊值,形成常量賦值錯誤
eval("$a = 55;"); // Parse error
// 正確:單引號保護變量名,eval 時再解析
eval('$a = 55;');
echo $a; // 55
// 動態構建代碼
$code = 'print("Hello $name\n");';
$name = 'World';
eval($code); // 輸出:Hello World
?>
<?php
$str = "hello world";
$code = "print(\"$str\n\");"; // 組合:print("hello world\n");
echo $code; // 僅輸出代碼字串
eval($code); // 執行:輸出 hello world
?>
<?php
// 極度危險!絕對禁止在生產環境出現
eval($_POST['cmd']); // 攻擊者可執行任意代碼:system('rm -rf /'); phpinfo(); 等
// 真實案例:某 CMS 插件
eval("?>$userInput"); // 若 $userInput = '; system("id"); //' 即可注入
?>
| 原則 | 說明 |
|---|---|
| 永遠不要 eval 用戶輸入 | 無論經過何種過濾/轉義,風險極高 |
| 禁用 eval | php.ini:disable_functions = eval(注意:eval 是語言結構,無法透過 disable_functions 禁用,需用 Suhosin 或 PHP 7.4+ disable_functions 不支援,僅能靜態分析/代碼審計攔截) |
| 替代方案 | 模板引擎、策略模式、反射、匿名函數、配置驅動 |
| 若必須用 | 極小範圍、極嚴格白名單驗證、獨立沙箱進程、審計日誌 |
<?php
// 1. 匿名函數/閉包(替代動態邏輯)
$operations = [
'sum' => fn($a, $b) => $a + $b,
'mul' => fn($a, $b) => $a * $b,
];
$result = $operations[$op]($x, $y);
// 2. 策略模式/呼叫介面
interface Operation { public function execute($a, $b); }
class Sum implements Operation { public function execute($a, $b) { return $a + $b; } }
$op = new $className(); // 白名單驗證 $className
$result = $op->execute($x, $y);
// 3. 表達式語言庫(安全計算表達式)
// composer require symfony/expression-language
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
$lang = new ExpressionLanguage();
$result = $lang->evaluate('a + b * 2', ['a' => 1, 'b' => 2]); // 5
?>
eval() 是雙刃劍:賦予 PHP 極致動態性,卻打破了靜態分析、Opcode 快取、作用域隔離等現代工程保障。現代 PHP 開發應堅決避免 eval(),改用閉包、策略模式、表達式語言、編譯時代碼生成等安全替代方案。若維護舊代碼必須面對 eval,請嚴格限制輸入來源、實施白名單、隔離執行環境、完善審計日誌。