PHP eval и фиксация ошибок (насколько это возможно)
Отказ; Я полностью осведомлен о подводных камнях и "злах" eval, включая, но не ограничиваясь: проблемы производительности, безопасность, переносимость и т.д.
Проблема
Чтение руководства PHP по eval...
eval() возвращает NULL, если возврат не равен вызывается в оцененном коде, в котором в случае, если значение, переданное в вернулся. Если есть ошибка синтаксического анализа в оцененный код, 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-тегов и без конечной точки с запятой
- Новые строки преобразуются в пробелы
- Добавлена переменная, содержащая результат (выражение должно возвращать либо true, либо false, и для того, чтобы не конфликтировать с eval return, используется временная переменная.)
Итак, что бы вы добавили к дальнейшему помощнику пользователя? Существуют ли какие-либо дополнительные функции синтаксического анализа, которые могут лучше определить возможные ошибки/проблемы?
Крис.
Ответы
Ответ 1
Я нашел хорошую альтернативу/ответ на свой вопрос.
Прежде всего, позвольте мне начать с того, что 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;
}
Кажется, он легко делает то, что мне нужно (yay)!
Ответ 2
Я не думаю, что eval бросит вам исключение, оно вызовет обычные ошибки. Чтобы поймать это место,
ob_start();
до eval и после eval:
if ('' !== $error = ob_get_clean()) {
// output the error somehow to the client
}
Я не думаю, что есть другая функция типа eval
с лучшей обработкой ошибок. Существует php_check_syntax
, но он проверяет только файл.
Ответ 3
Как проверить ошибки анализа в eval():
$result = @eval($evalcode . "; return true;");
Если $result == false
, $evalcode
имеет ошибку синтаксического разбора и не выполняет часть 'return true'. Очевидно, $evalcode
не должен возвращать что-то, но с этим трюком вы можете эффективно проверить ошибки синтаксического анализа в выражениях...
Ответ 4
Вы также можете попробовать что-то вроде этого:
$filePath = '/tmp/tmp_eval'.mt_rand();
file_put_contents($filePath, $evalCode);
register_shutdown_function('unlink', $filePath);
require($filePath);
Таким образом, любые ошибки в $evalCode будут обрабатываться обработчиком ошибок.