Hi again, here is my last contribution to the subject : this php_syntax_error() function returns false if there is no syntax error in $code, or an array($message, $line) if there is one (idea borrowed from kevin's code) .
For exemple, php_syntax_error(' DELIBERTE PHP ERROR; ') returns array('unexpected T_STRING', 1) ;)
Please note that the dead code sandbox IS important. A "return" at the beginning of the evaluated string can easily be broken: try eval('return; function strlen(){}') versus eval('if(0){function strlen(){}}').
<?php
function php_syntax_error($code)
{
$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 '"': --$inString; break;
}
}
else
{
switch ($token)
{
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();
$braces || $code = "if(0){{$code}\n}";
if (false === eval($code))
{
if ($braces) $braces = PHP_INT_MAX;
else
{
false !== strpos($code, "\r") && $code = strtr(str_replace("\r\n", "\n", $code), "\r", "\n");
$braces = substr_count($code, "\n");
}
$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;
}
?>