PHP eval и захват ошибок (насколько это возможно) - PullRequest
23 голосов
/ 11 июля 2010

Ответственность ; Я полностью осведомлен о подводных камнях и «бедах» eval, включая, помимо прочего: проблемы производительности, безопасности, переносимости и т. Д.

Проблема

Чтение руководства PHP по eval ...

eval () возвращает NULL, если return не вызывается в оцененном коде, в котором если значение, переданное для возврата, вернулся. Если есть ошибка разбора в оцененный код, eval () возвращает ЛОЖЬ и выполнение следующего код продолжается нормально. Это не можно поймать ошибку разбора в 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, используется временная переменная.)

Итак, что бы вы добавили для дальнейшей помощи пользователю? Существуют ли какие-либо дополнительные функции синтаксического анализа, которые могли бы лучше определить возможные ошибки / проблемы?

Крис.

Ответы [ 5 ]

14 голосов
/ 11 июля 2010

Поскольку PHP 7 eval () сгенерирует исключение ParseError для синтаксических ошибок:

try {
    $result = eval($code);
} catch (ParseError $e) {
    // Report error somehow
}

В PHP 5 eval () сгенерирует ошибку разбора, которая является особенной-cказано не прерывать выполнение (как обычно бывает при разборе ошибок).Тем не менее, он также не может быть перехвачен через обработчик ошибок.Возможно перехватить напечатанное сообщение об ошибке, предполагая, что display_errors=1:

ob_start();
$result = eval($code);
if ('' !== $error = ob_get_clean()) {
    // Report error somehow
}
12 голосов
/ 12 июля 2010

Я нашел хорошую альтернативу / ответ на мой вопрос.

Прежде всего, позвольте мне начать с того, что предложение 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;
}

Кажется, он легко делает именно то, что янужно (ууу)!

7 голосов
/ 05 ноября 2015

Как проверить ошибки синтаксического анализа внутри eval ():

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

Если $result == false, $evalcode имеет ошибку синтаксического анализа и не выполняет часть 'true true'.Очевидно, $evalcode не должен возвращать сам по себе что-то, но с помощью этого трюка вы можете эффективно проверять ошибки синтаксического анализа в выражениях ...

2 голосов
/ 31 октября 2018

Хорошие новости: Начиная с PHP 7 , eval() now * выдает исключение ParseError, если оцененный код недействителен:

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

* Ну, тогда какое-то время ...;)

2 голосов
/ 10 августа 2013

Вы также можете попробовать что-то вроде этого:

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

Таким образом, любые ошибки в $ evalCode будут обрабатываться обработчиком ошибок.

...