PHP 评估和捕获错误(尽可能多)

2022-08-30 13:56:55

免责声明;我完全了解eval的陷阱和“邪恶”,包括但不限于:性能问题,安全性,可移植性等。

问题

阅读 PHP 手册上的评估...

eval() 返回 NULL,除非在计算的代码中调用 return,在这种情况下,将返回传递给 return 的值。如果计算的代码中存在解析错误,eval() 将返回 FALSE,并且以下代码的执行将继续正常进行。使用set_error_handler() 无法在 eval() 中捕获解析错误。

简而言之,除了返回false之外,没有错误捕获,这是非常有帮助的,但我是sur eI可以做得更好!

原因

我正在处理的站点功能的一部分依赖于执行表达式。我不想通过沙盒或执行模块的路径,所以我已经结束了使用eval。在你大喊“如果客户变坏怎么办?”之前,要知道客户几乎是值得信赖的;他不想破坏自己的网站,任何能够访问此功能的人都几乎拥有服务器,无论评估如何。

客户端知道Excel中的表达式,并且解释微小的差异不是问题,但是,具有某种形式的警告几乎是标准功能。

这是我到目前为止所拥有的:

define('CR',chr(13));
define('LF',chr(10));

function test($cond=''){
    $cond=trim($cond);
    if($cond=='')return 'Success (condition was empty).'; $result=false;
    $cond='$result = '.str_replace(array(CR,LF),' ',$cond).';';
    try {
        $success=eval($cond);
        if($success===false)return 'Error: could not run expression.';
        return 'Success (condition return '.($result?'true':'false').').';
    }catch(Exception $e){
        return 'Error: exception '.get_class($e).', '.$e->getMessage().'.';
    }
}

笔记

  • 该函数在任何事件中返回消息字符串
  • 代码表达式应该是一行 PHP,没有 PHP 标记,也没有结束分号
  • 新行将转换为空格
  • 添加一个变量以包含结果(表达式应返回 true 或 false,并且为了不与 eval 的返回冲突,使用 temp 变量。

那么,你会添加什么来进一步帮助用户?是否有任何进一步的解析函数可以更好地查明可能的错误/问题?

克里斯。


答案 1

由于 PHP 7 eval() 将为语法错误生成 ParseError 异常:

try {
    $result = eval($code);
} catch (ParseError $e) {
    // Report error somehow
}

在 PHP 5 中,eval() 将生成一个解析错误,该错误的特殊情况是不中止执行(就像解析错误通常所做的那样)。但是,也无法通过错误处理程序捕获它。一种可能性是捕获打印的错误消息,假设:display_errors=1

ob_start();
$result = eval($code);
if ('' !== $error = ob_get_clean()) {
    // Report error somehow
}

答案 2

我为我的问题找到了一个很好的替代方案/答案。

首先,让我首先说,当我设置error_reporting(E_ALL)时,nikic的建议是有效的;通知显示在PHP输出中,并且由于OB,它们可以被捕获。

接下来,我发现了这个非常有用的代码:

/**
 * Check the syntax of some PHP code.
 * @param string $code PHP code to check.
 * @return boolean|array If false, then check was successful, otherwise an array(message,line) of errors is returned.
 */
function php_syntax_error($code){
    if(!defined("CR"))
        define("CR","\r");
    if(!defined("LF"))
        define("LF","\n") ;
    if(!defined("CRLF"))
        define("CRLF","\r\n") ;
    $braces=0;
    $inString=0;
    foreach (token_get_all('<?php ' . $code) as $token) {
        if (is_array($token)) {
            switch ($token[0]) {
                case T_CURLY_OPEN:
                case T_DOLLAR_OPEN_CURLY_BRACES:
                case T_START_HEREDOC: ++$inString; break;
                case T_END_HEREDOC:   --$inString; break;
            }
        } else if ($inString & 1) {
            switch ($token) {
                case '`': case '\'':
                case '"': --$inString; break;
            }
        } else {
            switch ($token) {
                case '`': case '\'':
                case '"': ++$inString; break;
                case '{': ++$braces; break;
                case '}':
                    if ($inString) {
                        --$inString;
                    } else {
                        --$braces;
                        if ($braces < 0) break 2;
                    }
                    break;
            }
        }
    }
    $inString = @ini_set('log_errors', false);
    $token = @ini_set('display_errors', true);
    ob_start();
    $code = substr($code, strlen('<?php '));
    $braces || $code = "if(0){{$code}\n}";
    if (eval($code) === false) {
        if ($braces) {
            $braces = PHP_INT_MAX;
        } else {
            false !== strpos($code,CR) && $code = strtr(str_replace(CRLF,LF,$code),CR,LF);
            $braces = substr_count($code,LF);
        }
        $code = ob_get_clean();
        $code = strip_tags($code);
        if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) {
            $code[2] = (int) $code[2];
            $code = $code[2] <= $braces
                ? array($code[1], $code[2])
                : array('unexpected $end' . substr($code[1], 14), $braces);
        } else $code = array('syntax error', 0);
    } else {
        ob_end_clean();
        $code = false;
    }
    @ini_set('display_errors', $token);
    @ini_set('log_errors', $inString);
    return $code;
}

似乎它很容易做到我需要的(耶)!


推荐