copist
@copist
Empower people to give

Как узнать текст выражения, на котором eval() закончился с фатальной ошибкой?

Есть такая вот проблемка:


В коде используем eval для вычисления значения формул. В случае злостной ошибки в тексте выражения PHP фатально ошибается и всё, что остаётся в логах:

PHP Fatal Error: syntax error, unexpected ')' in Form/Validate/DinamicFormulaValues.php(149) : eval()'d code on line 1



Как вывести в консоль текст выражения, которое не удалось проевалить?


У меня в голове такой вариант:

$expression = "1 + 2)"; // тут явно похожая синтаксическая ошибка - незакрытая скобка
$file = tempnam('/tmp', 'eval')
file_put_contents($file, "<?php return {$expression};");
$value = include($file);



После этого возникает ошибка
Parse error:  syntax error, unexpected ')' in /tmp/eval1kGtG7 on line 1



(при должной сноровке и чуточке разума это сообщение уходит почтой админу или разработчикам)


Открываю файл /tmp/eval1kGtG7 и вижу синтаксическую ошибку


Ещё вариант:
$result = exec('php -l "'.$file.'"', $output); // это консольная команда проверки синтаксиса файла PHP
echo $result; // -> Errors parsing /tmp/eval1kGtG7
echo implode("\n", $output);  // -> PHP Parse error:  syntax error, unexpected ')' in /tmp/eval1kGtG7 on line 1



Мне не нравится то, что файлов будет не просто много, а очень очень много, потому что через эту конструкцию проходит сотни проверок.


Ещё вариант
if (false === @eval($expression)) {
    var_dump(error_get_last()); // выводится текст сообщения об ошибке
}


Недостаток этого варианта: в error_get_last() остаётся результат от интерпретации предыдущего неудачного выражения, а проверок будет сотни — получается, что будет много ложных срабатываний на рекорректность.


Есть ли другие варианты?
  • Вопрос задан
  • 4447 просмотров
Решения вопроса 1
@m-haritonov
Можете попробовать использовать PECL расширение runkit, если есть возможность его установки на сервер. В частности, runkit предоставляет класс Runkit_Sandbox, предоставляющий возможность запуска кода в изолированных условиях (в отдельно выделенной области памяти). См. метод Runkit_Sandbox::eval() в примерах в документации к классу. Также в runkit есть метод runkit_lint — аналог системного вызова «php -l file», но принимающий в качестве аргумента строку с кодом, а не путь к файлу.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 5
Для проверки на синтаксические ошибки в PHP можно использовать специальные парсеры, например этот: github.com/nikic/PHP-Parser, код будет выглядеть примерно так:

$code = '1 + 2)';

$parser = new PHPParser_Parser(new PHPParser_Lexer);

try {
    $stmts = $parser->parse($code);
} catch (PHPParser_Error $e) {
    echo 'Parse Error: ', $e->getMessage();
}


Средствами языка только через shutdown_handler + error_get_last, других способов нет.
Ответ написан
Комментировать
EugeneOZ
@EugeneOZ
Недостаток этого варианта: в error_get_last() остаётся результат от интерпретации предыдущего неудачного выражения, а проверок будет сотни

Скрипт в любом случае умрёт при fatal error, и error_get_last поймает лишь его последний вздох, а не продолжит выполнение.
Только тут надо свой shutdown_handler регистрировать, чтобы поймать fatal error — смотрите register_shutdown_function().
Ответ написан
Комментировать
EugeneOZ
@EugeneOZ
так вы удаляйте файл после eval — так останутся только те, что не прошли eval.
и писать можно не только в файлы — уже изобретены базы данных :) запишите в БД значение expression до eval вашего жуткого и удалите после него.
Ответ написан
RUVATA
@RUVATA
Разработчик, гик, меломан, разгильдяй
Какая-то надуманная проблема…
Оберните eval в try-Catch блок, и по срабатыванию Catch выводите что угодно.
Ответ написан
copist
@copist Автор вопроса
Empower people to give
Вот умные товарищи подсказали следующее:

$evalResult = @eval('$result = (bool)(1<2);');

$evalResult = null если выражение выполнилось
$evalResult = false если не выполнилось


Только почему-то на моей машине отрабатывает, а на промышленном стенде валится в Fatal Error и опять текст выражения остаётся неизвестным.

У меня php 5.3.15, на проме 5.3.23
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы