PHP eval and capturing errors (as much as possible) PHP eval and capturing errors (as much as possible) php php

PHP eval and capturing errors (as much as possible)


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

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:

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


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.

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)!


How to test for parse errors inside eval():

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

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