Это то, что я получил сейчас:
/{% if(.+?) %}(.*?){% endif %}/gusi
Он ловит несколько операторов if и т. Д.
IMG: http://img.ruphp.com/php/2015-02-07_23-22-11.png
Но когда я делаю вложенные, так что если в if, то останавливается при первом появлении {% endif%}
IMG: http://img.ruphp.com/php/2015-02-08_09-29-43.png
Есть ли способ уловить как можно больше {% endif%} операторов, чем есть {% if …%}, и если да, то как?
Не используйте regexen, используйте существующий анализатор Twig. Вот пример экстрактора, который я написал, который анализирует пользовательские теги и извлекает их: https://github.com/deceze/Twig-extensions/tree/master/lib/Twig/Extensions/Extension/Gettext
Задача лексера – превратить исходный код Twig в объекты; вы можете расширить его, если вам нужно подключиться к этому процессу:
class My_Twig_Lexer extends Twig_Lexer { ... /** * Overrides lexComment by saving comment tokens into $this->commentTokens * instead of just ignoring them. */ protected function lexComment() { if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { throw new Twig_Error_Syntax('Unclosed comment', $this->lineno, $this->filename); } $value = substr($this->code, $this->cursor, $match[0][1] - $this->cursor); $token = new Twig_Extensions_Extension_Gettext_Token(Twig_Extensions_Extension_Gettext_Token::COMMENT, $value, $this->lineno); $this->commentTokens[] = $token; $this->moveCursor($value . $match[0][0]); } ... }
Как правило, узлы комментариев Twig отбрасываются Twig, этот лексер сохраняет их.
Однако ваша главная задача будет заключаться в работе с парсером:
$twig = new Twig_Environment(new Twig_Loader_String); $lexer = new My_Twig_Lexer($twig); $parser = new Twig_Parser($twig); $source = file_get_contents($file); $tokens = $lexer->tokenize($source); $node = $parser->parse($tokens); processNode($node);
$node
– это корневой узел дерева узлов, которые представляют источник Twig объектно-ориентированным способом, все правильно проанализированы. Вам просто нужно обработать это дерево, не беспокоясь о точном синтаксисе, который использовался для его создания:
processNode(Twig_NodeInterface $node) { switch (true) { case $node instanceof Twig_Node_Expression_Function : processFunctionNode($node); break; case $node instanceof Twig_Node_Expression_Filter : processFilterNode($node); break; } foreach ($node as $child) { if ($child instanceof Twig_NodeInterface) { processNode($child); } } }
Просто переходите его, пока не найдете тот узел, который вы ищете, и получите его информацию. Поиграйте с ним немного. Этот примерный код может быть или не быть устаревшим, вам придется вникнуть в исходный код парсера Twig, чтобы понять его.
Практически тривиально изменить шаблон в рекурсивный шаблон :
{% if(.+?) %}((?>(?R)|.)*?){% endif %}
Рабочий пример: https://regex101.com/r/gX8rM0/1
Однако это было бы плохой идеей: в шаблоне отсутствует много случаев, которые на самом деле являются ошибками в вашем парсере. Всего несколько примеров:
Комментарии :
{% if aaa %} 123 <!-- {% endif %} --> {% endif %}
Строковые литералы :
{% if aaa %}a = "{% endif %}"{% endif %} {% if $x == "{% %}" %}...{% endif %}
Экранированные символы (вам нужны экранированные символы, не так ли?):
<p>To start a condition, use <code>\{% if aaa %}</code></p>
Недопустимый ввод :
Было бы неплохо, если бы парсер мог неплохо работать на недопустимом вводе и указывать на правильное положение ошибки.