Подтвердите введенный пользователем код PHP, прежде чем передавать его в eval ()

Перед передачей строки в eval () я хотел бы убедиться, что синтаксис верен и позволяет:

  1. Две функции: a () и b ()
  2. Четыре оператора: / * – +
  3. Кронштейны: ()
  4. Числа: 1,2, -1, 1

Как я могу это сделать, может быть, это что-то связано с PHP Tokenizer?

Я на самом деле пытаюсь сделать простой интерпретатор формулы, поэтому a () и b () будут заменены на ln () и exp (). Я не хочу писать токенизатор и парсер с нуля.

Что касается валидации, то действуют следующие символьные токены:

operator: [/*+-] funcs: (a\(|b\() brackets: [()] numbers: \d+(\.\d+)? space: [ ] 

Простая проверка может затем проверить, соответствует ли входная строка любой комбинации этих шаблонов. Поскольку токен funcs довольно точен, и он не сталкивается с другими токенами, эта проверка должна быть довольно стабильной без необходимости внедрения любого синтаксиса / грамматики:

 $tokens = array( 'operator' => '[/*+-]', 'funcs' => '(a\(|b\()', 'brackets' => '[()]', 'numbers' => '\d+(\.\d+)?', 'space' => '[ ]', ); $pattern = ''; foreach($tokens as $token) { $pattern .= sprintf('|(?:%s)', $token); } $pattern = sprintf('~^(%s)*$~', ltrim($pattern, '|')); echo $pattern; 

Только если вся строка ввода соответствует шаблону, основанному на токенах, он проверяет. Это все еще может быть синтаксически неправильным PHP, и вы можете убедиться, что он основан только на указанных токенах:

 ~^((?:[/*+-])|(?:(a\(|b\())|(?:[()])|(?:\d+(\.\d+)?)|(?:[ ]))*$~ 

Если вы построите шаблон динамически – как в примере, вы можете более легко изменить свои токены языка.

Кроме того, это может быть первым шагом к вашему собственному токенизатору / лексеру. Затем токен-поток может передаваться в синтаксический анализатор, который может синтаксически проверять и интерпретировать его. Об этом написала пользователь user187291 .

В качестве альтернативы написанию полного синтаксиса lexer +, и вам нужно проверить синтаксис, вы также можете сформулировать свою грамматику на основе токенов, а затем выполнить грамматику маркера на основе регулярных выражений в представлении маркера ввода.

Знаки – это слова, которые вы используете в своей грамматике. Вам нужно будет более точно описать скобки и определение функции в токенах, а токенизатор должен следовать более четким правилам, которые токен заменяет другой токен. Концепция изложена в моем другом вопросе . Он также использует регулярное выражение для формулировки грамматики и проверки синтаксиса, но он все еще не анализирует. В вашем случае eval будет парсером, который вы используете.

Генераторы Parser уже были написаны для PHP, и в частности LIME поставляется с типичным примером «калькулятора», который станет очевидной отправной точкой для вашего «мини-языка»: http://sourceforge.net/projects/lime -PHP /

Прошло много лет с тех пор, как я в последний раз играл с LIME, но он был уже зрелым и стабильным.

Заметки:

1) Использование полноразмерного генератора парсеров дает вам преимущество избежать PHP eval () полностью, если вы хотите – вы можете заставить LIME испускать парсер, который эффективно предоставляет функцию «eval» для выражений, написанных на вашем мини-языке (с проверкой испеченных в). Это дает вам дополнительное преимущество, позволяющее вам добавлять поддержку новых функций по мере необходимости.

2) Поначалу может показаться излишним использовать генератор синтаксического анализатора для такой, казалось бы, небольшой задачи, но как только вы получите примеры работы, вы будете впечатлены тем, насколько легко их модифицировать и расширять. И очень легко недооценить трудность написания парсера без ошибок (даже «тривиального») с нуля.

да, вам нужен Tokenizer или что-то подобное, но это только часть истории. Токенизатор (обычно называемый «lexer») может читать и анализировать только элементы выражения, но не имеет возможности обнаружить что-то вроде «foo () + * bar)» является недопустимым. Вам понадобится вторая часть, называемая парсером, которая сможет организовать токены в виде дерева (называемого «AST») или предоставить сообщение об ошибке, если этого не сделать. По иронии судьбы, как только у вас есть дерево, «eval» больше не требуется, вы можете оценить свое выражение непосредственно из дерева.

Я бы рекомендовал вам написать парсер вручную, потому что это очень полезное упражнение и очень весело. Регуляторы рекурсивного спуска довольно просты в программировании.

Вы можете использовать token_get_all() , проверить каждый токен и прервать первый недопустимый токен.

Ответ hakre, использование регулярных выражений – хорошее решение, но это немного сложно. Также обработка белого списка функций становится довольно запутанной. И если это пойдет не так, это может сильно повлиять на вашу систему.

Есть ли причина, по которой вы не используете javascript 'eval'?