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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3223899/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 09:03:25  来源:igfitidea点击:

PHP eval and capturing errors (as much as possible)

phpexceptionparsingeval

提问by Christian

Disclaimer; I'm fully aware of the pitfalls and "evils" of eval, including but not limited to: performance issues, security, portability etc.

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

The problem

问题

Reading the PHP manual on eval...

阅读有关 eval 的 PHP 手册...

eval() returns NULL unless return is called in the evaluated code, in which case the value passed to return is returned. If there is a parse error in the evaluated code, eval() returns FALSE and execution of the following code continues normally. It is not possible to catch a parse error in eval() using set_error_handler().

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

In short, no error capture except returning false which is very helpful, but I'm sur eI could do way better!

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

The reason

原因

A part of the site's functionality I'm working on relies on executing expressions. I'd like not to pass through the path of sandbox or execution modules, so I've ended using eval. Before you shout "what if the client turned bad?!" know that the client is pretty much trusted; he wouldn't want to break his own site, and anyone getting access to this functionality pretty much owns the server, regardless of eval.

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

The client knows about expressions like in Excel, and it isn't a problem explaining the little differences, however, having some form of warning is pretty much standard functionality.

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

This is what I have so far:

这是我到目前为止:

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().'.';
    }
}

Notes

笔记

  • The function returns a message string in any event
  • The code expression should be a single-line piece of PHP, without PHP tags and without an ending semicolon
  • New lines are converted to spaces
  • A variable is added to contain the result (expression should return either true or false, and in order not to conflict with eval's return, a temp variable is used.)
  • 该函数在任何事件中都返回一个消息字符串
  • 代码表达式应该是一行 PHP 代码,没有 PHP 标签,也没有结束分号
  • 新行转换为空格
  • 添加一个变量以包含结果(表达式应返回 true 或 false,为了不与 eval 的返回冲突,使用临时变量。)

So, what would you add to further aide the user? Is there any further parsing functions which might better pinpoint possible errors/issues?

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

Chris.

克里斯。

采纳答案by Christian

I've found a good alternative/answer to my question.

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

First of, let me start by saying that nikic's suggestion works when I set error_reporting(E_ALL); notices are shown in PHP output, and thanks to OB, they can be captured.

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

Next, I've found this very useful code:

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

/**
 * 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;
}

Seems it easily does exactly what I need (yay)!

似乎它很容易完成我所需要的(耶)!

回答by NikiC

Since PHP 7 eval() will generate a ParseErrorexception for syntax errors:

由于 PHP 7 eval() 会产生ParseError语法错误异常:

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

In PHP 5 eval() will generate a parse error, which is special-cased to not abort execution (as parse errors would usually do). However, it also cannot be caught through an error handler. A possibility is to catch the printed error message, assuming that display_errors=1:

在 PHP 5 eval() 会产生解析错误,这是特殊情况,不会中止执行(因为解析错误通常会这样做)。但是,它也不能通过错误处理程序捕获。一种可能性是捕获打印的错误消息,假设display_errors=1

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

回答by Davide Moretti

How to test for parse errors inside eval():

如何在 eval() 中测试解析错误:

$result = @eval($evalcode . "; return true;");

If $result == false, $evalcodehas a parse error and does not execute the 'return true' part. Obviously $evalcodemust not returnitself something, but with this trick you can test for parse errors in expressions effectively...

如果$result == false,$evalcode有解析错误并且不执行“返回真”部分。显然$evalcode不能返回自己的东西,但通过这个技巧,你可以有效地测试表达式中的解析错误......

回答by Sz.

Good news:As of PHP 7, eval()now* throws a ParseErrorexception if the evaluated code is invalid:

好消息:从 PHP 7 开始,如果评估的代码无效,eval()now* 会抛出ParseError异常:

try
{
    eval("Oops :-o");
}
catch (ParseError $err)
{
    echo "YAY! ERROR CAPTURED: $err";
}


* Well, for quite a while then... ;)

*嗯,然后很长一段时间...... ;)

回答by barbushin

You can also try something like this:

你也可以尝试这样的事情:

$filePath = '/tmp/tmp_eval'.mt_rand();
file_put_contents($filePath, $evalCode);
register_shutdown_function('unlink', $filePath);
require($filePath);

So any errors in $evalCode will be handled by errors handler.

所以 $evalCode 中的任何错误都将由错误处理程序处理。