PHP Conference Japan 2024

eval

(PHP 4、PHP 5、PHP 7、PHP 8)

eval將字串評估為 PHP 程式碼

描述

eval(string $code): mixed

將給定的 code 評估為 PHP。

正在評估的程式碼繼承了 eval() 呼叫發生的行上的 變數範圍。該行中可用的任何變數都可以在已評估的程式碼中讀取和修改。但是,所有定義的函式和類別都將在全域命名空間中定義。換句話說,編譯器會將已評估的程式碼視為獨立的 包含檔案。

注意

eval() 語言結構非常危險,因為它允許執行任意的 PHP 程式碼。因此不建議使用它。 如果您已仔細驗證沒有其他選項可以選擇,只能使用此結構,請特別注意不要在事先未正確驗證的情況下傳遞任何使用者提供的資料到其中。

參數

code

要評估的有效 PHP 程式碼。

程式碼不能包裹在開頭和結尾的 PHP 標籤中,例如必須傳遞 'echo "Hi!";' 而不是 '<?php echo "Hi!"; ?>'。但仍可以使用適當的 PHP 標籤離開並重新進入 PHP 模式,例如 'echo "In PHP mode!"; ?>In HTML mode!<?php echo "Back in PHP mode!";'

除此之外,傳遞的程式碼必須是有效的 PHP。這包括所有陳述式都必須使用分號正確終止。例如,'echo "Hi!"' 將導致解析錯誤,而 'echo "Hi!";' 將會運作。

return 陳述式將立即終止程式碼的評估。

程式碼將在呼叫 eval() 的程式碼範圍中執行。因此,在 eval() 呼叫中定義或變更的任何變數在終止後仍可見。

回傳值

除非在已評估的程式碼中呼叫 return,否則 eval() 會傳回 null,在這種情況下,會傳回傳遞給 return 的值。從 PHP 7 開始,如果已評估的程式碼中存在解析錯誤,則 eval() 會擲出 ParseError 例外。在 PHP 7 之前,在這種情況下,eval() 會傳回 false,而後續程式碼的執行會正常繼續。無法使用 set_error_handler() 捕捉 eval() 中的解析錯誤。

範例

範例 1 eval() 範例 - 簡單的文字合併

<?php
$string
= 'cup';
$name = 'coffee';
$str = 'This is a $string with my $name in it.';
echo
$str. "\n";
eval(
"\$str = \"$str\";");
echo
$str. "\n";
?>

上面的範例將輸出

This is a $string with my $name in it.
This is a cup with my coffee in it.

注意

注意因為這是語言結構而不是函式,所以無法使用 變數函式具名引數呼叫它。

提示

如同任何將結果直接輸出到瀏覽器的內容一樣,可以使用 輸出控制函式來捕捉此函式的輸出,並將其儲存為 字串(例如)。

注意:

如果已評估的程式碼中發生嚴重錯誤,則整個腳本將會結束。

參見

新增註解

使用者貢獻的註解 7 則註解

474
匿名
20 年前
請記住以下引言

如果 eval() 是答案,那麼您幾乎肯定是在問
錯誤的問題。-- Rasmus Lerdorf,PHP 的 BDFL
43
lord dot dracon at gmail dot com
9 年前
使用 eval() 的啟動

<pre>
啟動開始
<?php
eval("echo 'Inception lvl 1...\n'; eval('echo \"Inception lvl 2...\n\"; eval(\"echo \'Inception lvl 3...\n\'; eval(\'echo \\\"Limbo!\\\";\');\");');");
?>
21
Jeremie LEGRAND
7 年前
至少在 PHP 7.1+ 中,如果已評估的程式碼產生嚴重錯誤,eval() 會終止腳本。例如
<?php
@eval('$content = (100 - );');
?>

(即使它在手冊中,我也不確定它在 5.6 中是否像這樣運作,但無論如何)
為了捕捉它,我必須執行
<?php
try {
eval(
'$content = (100 - );');
} catch (
Throwable $t) {
$content = null;
}
?>

這是我找到的唯一一種捕捉錯誤並隱藏存在錯誤的方式。
27
bohwaz
12 年前
如果您想要允許數學輸入,並確保輸入是正確的數學運算,而不是一些駭客程式碼,您可以嘗試此方法

<?php

$test
= '2+3*pi';

// 移除空白字元
$test = preg_replace('/\s+/', '', $test);

$number = '(?:\d+(?:[,.]\d+)?|pi|π)'; // 數字的定義
$functions = '(?:sinh?|cosh?|tanh?|abs|acosh?|asinh?|atanh?|exp|log10|deg2rad|rad2deg|sqrt|ceil|floor|round)'; // 允許的 PHP 函數
$operators = '[+\/*\^%-]'; // 允許的數學運算符
$regexp = '/^(('.$number.'|'.$functions.'\s*\((?1)+\)|\((?1)+\))(?:'.$operators.'(?2))?)+$/'; // 最終的正規表達式,大量使用遞迴模式

if (preg_match($regexp, $q))
{
$test = preg_replace('!pi|π!', 'pi()', $test); // 將 pi 取代為 pi 函數
eval('$result = '.$test.';');
}
else
{
$result = false;
}

?>

我無法絕對保證這能阻擋所有可能的惡意程式碼,也無法保證能阻擋格式錯誤的程式碼,但這比下面 matheval 函數好,matheval 允許像 '2+2+' 這種格式錯誤的程式碼,會拋出錯誤。
8
catgirl at charuru dot moe
6 年前
應該要注意的是,匯入的命名空間在 eval 中是不可用的。
5
divinity76 at gmail dot com
7 年前
我認為,這是一個更好的 eval 替代方案

<?php
function betterEval($code) {
$tmp = tmpfile ();
$tmpf = stream_get_meta_data ( $tmp );
$tmpf = $tmpf ['uri'];
fwrite ( $tmp, $code );
$ret = include ($tmpf);
fclose ( $tmp );
return
$ret;
}
?>

- 為什麼?betterEval 遵循正常的 php 開啟和關閉標籤慣例,不需要從原始碼中移除 `<?php?>`。如果發生解析錯誤,它總是會拋出 ParseError,而不是返回 false(注意:這在 php 7.0 中已修復為正常的 eval())。- 還有一些關於例外回溯的內容
6
darkhogg (foo) gmail (bar) com
14 年前
以下程式碼

<?php
eval( '?> foo <?php' );
?>

不會拋出任何錯誤,但會印出開啟標籤。
在開啟標籤後加入一個空格即可修復此問題

<?php
eval( '?> foo <?php ' );
?>
To Top