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...