Как поймать вложенные {% if …%} {% endif%} с регулярным выражением

Вот что я получил сейчас:

/{% if(.+?) %}(.*?){% endif %}/gusi

Ловит несколько операторов if и т. Д.

IMG: http://image.xesau.eu/2015-02-07_23-22-11.png

Но когда я делаю вложенные, то if в if, он останавливается при первом появлении {% endif%}

IMG: http://image.xesau.eu/2015-02-08_09-29-43.png

Есть ли способ поймать столько операторов {% endif%}, сколько было операторов {% if …%}, и если да, то как?

-2

Решение

Не используйте 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, чтобы понять его.

5

Другие решения

Это почти тривиально, чтобы изменить ваш шаблон в рекурсивный паттерн:

{% 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>
    
  • Неправильный ввод:
    Было бы неплохо, если бы синтаксический анализатор мог относительно хорошо работать с неверным вводом и указывать на правильную позицию ошибки.

3

По вопросам рекламы [email protected]