【问题标题】:PHP eval and capturing errors (as much as possible)PHP eval 和捕获错误(尽可能)
【发布时间】:2011-03-14 12:38:13
【问题描述】:

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

问题

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

eval() 返回 NULL,除非 return 是 在评估代码中调用,其中 如果传递给返回的值是 回来。如果出现解析错误 评估的代码, eval() 返回 FALSE 并执行以下操作 代码正常继续。它不是 可能会捕获解析错误 eval() 使用 set_error_handler()。

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

原因

我正在处理的网站功能的一部分依赖于执行表达式。我不想通过沙箱或执行模块的路径,所以我已经结束了使用eval。在你大喊“如果客户变坏怎么办?!”之前知道客户非常信任;他不想破坏自己的网站,而且任何可以访问此功能的人几乎都拥有服务器,无论 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 标记,也没有结束分号
  • 新行转换为空格
  • 添加了一个变量来包含结果(表达式应该返回真或假,为了不与 eval 的返回冲突,使用了一个临时变量。)

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

克里斯。

【问题讨论】:

  • 如果您可以就您将使用的“表达式”提供更多反馈,也许我们可以提供更多帮助。我可以想到一些不错的 token_get_all 东西来验证用户输入;)
  • 普通 PHP 代码?我计划允许对 PHP 的完全访问,但定义函数和类可能除外,这不是必需的。
  • @Sz。话虽如此,我现在建议使用symfony/expression-language(它在 10 年不可用)。

标签: php exception parsing eval


【解决方案1】:

我认为最好的解决方案是

try {
    eval(/* ... */);
} catch (Throwable $t) {
    //...
}

它捕获每个错误和异常,包括Call to undefined function

【讨论】:

    【解决方案2】:

    由于 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
    }
    

    【讨论】:

    • 是的,我确实定义了它们,抱歉没有提及。修复了上面的代码。至于异常,我很确定两者都不是,但是,在可能向其添加异常之后,此代码将来可能会失败,因此无论如何处理它们都没有坏处。
    • 我找不到check_syntax
    • php_check_syntax 已在 5.0.5 中删除,他们希望您执行类似 exec("echo '<?php $code' | /usr/local/bin/php -l 2>/dev/null", $out, $ret); 的操作,这所以要方便得多。
    • !== 运算符的优先级高于 =。需要加括号:('' !== ($error = ob_get_clean()))
    • @François 如果作业在 RHS 上,则不是。这就是这里使用尤达条件的原因。
    【解决方案3】:

    好消息: As of PHP 7, eval() now* 如果评估代码无效,则抛出 ParseError 异常:

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

    * 好吧,有一段时间了...... ;)

    【讨论】:

      【解决方案4】:

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

      首先,让我先说 nikic 的建议在我设置 error_reporting(E_ALL); 时有效通知显示在 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;
      }
      

      似乎它很容易满足我的需要(耶)!

      【讨论】:

      • 哇,这看起来不错。我又学到了一些新东西:break 可以争论!稍后我将查看这段代码以真正理解它的作用。只有一点:如果代码中存在严重的解析错误,token_get_all 可以自行输出错误,例如如果您在源代码中使用 `\`(在 PHP
      • 非常好。遗憾的是,它仍然不能 - 不能 - 提供针对错误函数调用的保护,例如函数名称中的简单拼写错误。 PHP 应该真正考虑在 eval() 中捕获致命错误。
      【解决方案5】:

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

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

      如果$result == false$evalcode 有解析错误,不执行'return true'部分。显然$evalcode 不能返回 本身的东西,但是通过这个技巧,您可以有效地测试表达式中的解析错误...

      【讨论】:

      • 非常聪明的主意!我还没有测试过,但我会的。
      • 您无法在eval'd 代码中发现解析错误!它只会停止执行您的脚本。
      • 这对我很有用,即使我的 eval() 返回了一个值。我有$value=eval("return ".$evalcode."; return false;"); 然后if ($value===false) echo ("Error with: ".$evalcode); 当然这意味着evalcode 在正常情况下永远不会返回false。但这适合我的使用。
      【解决方案6】:

      你也可以试试这样的:

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

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

      【讨论】:

        猜你喜欢
        • 2011-07-28
        • 1970-01-01
        • 2021-12-30
        • 1970-01-01
        • 2015-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多