ConFoo 2025

eval

(PHP 4, PHP 5, PHP 7, PHP 8)

eval把字符串作为PHP代码执行

说明

eval(string $code): mixed

把指定 code 作为 PHP 代码执行。

正在执行的代码继承调用 eval() 所在行的变量作用域。该行中任何有效变量都可在执行的代码中读取和修改。但定义的所有函数和类都将在全局命名空间中定义。换句话说,编译器将执行的代码视为单独 included 后的文件。

警告

eval() 语言构造非常危险,因为它允许执行任意 PHP 代码。因此不鼓励使用它。如果已经仔细验证过除了使用此构造以外别无他法, 请多加注意不要在未事先正确验证的情况下将任何用户提供的数据传递到其中。

参数

code

需要被执行的字符串

代码不能包含打开/关闭 PHP tags。比如, 'echo "Hi!";' 不能这样传入: '<?php echo "Hi!"; ?>'。但仍然可以用合适的 PHP tag 来离开、重新进入 PHP 模式。比如 'echo "In PHP mode!"; ?>In HTML mode!<?php echo "Back in PHP mode!";'

除此之外,传入的必须是有效的 PHP 代码。所有的语句必须以分号结尾。比如 'echo "Hi!"' 会导致一个 parse error,而 'echo "Hi!";' 则会正常运行。

return 语句会立即中止当前字符串的执行。

代码执行的作用域是调用 eval() 处的作用域。因此,eval() 里任何的变量定义、修改,都会在函数结束后被保留。

返回值

eval() 返回 null,除非在执行的代码中 return 了一个值,函数返回传递给 return 的值。 PHP 7 开始,执行的代码里如果有一个 parse error,eval() 会抛出 ParseError 异常。在 PHP 7 之前, 如果在执行的代码中有 parse error,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.

注释

注意: 因为是语言构造器而不是函数,不能被 可变函数 或者 命名参数 调用。

小技巧

和直接将结果输出到浏览器一样,可使用输出控制函数来捕获当前函数的输出,然后(例如)保存到一个 string 中。

注意:

如果在执行的代码中产生了一个致命的错误(fatal error),整个脚本会退出。

参见

添加备注

用户贡献的备注 7 notes

up
474
Anonymous
20 years ago
Kepp the following Quote in mind:

If eval() is the answer, you're almost certainly asking the
wrong question. -- Rasmus Lerdorf, BDFL of PHP
up
43
lord dot dracon at gmail dot com
9 years ago
Inception with eval()

<pre>
Inception Start:
<?php
eval("echo 'Inception lvl 1...\n'; eval('echo \"Inception lvl 2...\n\"; eval(\"echo \'Inception lvl 3...\n\'; eval(\'echo \\\"Limbo!\\\";\');\");');");
?>
up
21
Jeremie LEGRAND
7 years ago
At least in PHP 7.1+, eval() terminates the script if the evaluated code generate a fatal error. For example:
<?php
@eval('$content = (100 - );');
?>

(Even if it is in the man, I'm note sure it acted like this in 5.6, but whatever)
To catch it, I had to do:
<?php
try {
eval(
'$content = (100 - );');
} catch (
Throwable $t) {
$content = null;
}
?>

This is the only way I found to catch the error and hide the fact there was one.
up
27
bohwaz
12 years ago
If you want to allow math input and make sure that the input is proper mathematics and not some hacking code, you can try this:

<?php

$test
= '2+3*pi';

// Remove whitespaces
$test = preg_replace('/\s+/', '', $test);

$number = '(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number
$functions = '(?:sinh?|cosh?|tanh?|abs|acosh?|asinh?|atanh?|exp|log10|deg2rad|rad2deg|sqrt|ceil|floor|round)'; // Allowed PHP functions
$operators = '[+\/*\^%-]'; // Allowed math operators
$regexp = '/^(('.$number.'|'.$functions.'\s*\((?1)+\)|\((?1)+\))(?:'.$operators.'(?2))?)+$/'; // Final regexp, heavily using recursive patterns

if (preg_match($regexp, $q))
{
$test = preg_replace('!pi|π!', 'pi()', $test); // Replace pi with pi function
eval('$result = '.$test.';');
}
else
{
$result = false;
}

?>

I can't guarantee you absolutely that this will block every possible malicious code nor that it will block malformed code, but that's better than the matheval function below which will allow malformed code like '2+2+' which will throw an error.
up
8
catgirl at charuru dot moe
6 years ago
It should be noted that imported namespaces are not available in eval.
up
5
divinity76 at gmail dot com
7 years ago
imo, this is a better eval replacement:

<?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;
}
?>

- why? betterEval follows normal php opening and closing tag conventions, there's no need to strip `<?php?>` from the source. and it always throws a ParseError if there was a parse error, instead of returning false (note: this was fixed for normal eval() in php 7.0). - and there's also something about exception backtraces
up
6
darkhogg (foo) gmail (bar) com
14 years ago
The following code

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

does not throw any error, but prints the opening tag.
Adding a space after the open tag fixes it:

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