Php 如何用正则表达式捕获嵌套的{%if…%}{%endif%}语句

Php 如何用正则表达式捕获嵌套的{%if…%}{%endif%}语句,php,regex,preg-replace-callback,Php,Regex,Preg Replace Callback,这就是我现在得到的: /{%if.+?%}.*.{%endif%}/gusi 它可以捕捉多个if语句等 IMG: 但是当我进行嵌套时,if中的if会在第一次出现{%endif%}时停止 IMG: 是否有一种方法可以捕获与{%if…%}语句一样多的{%endif%}语句,如果有,如何捕获?将模式更改为: 工作示例: 但是,这不是一个好主意:该模式缺少许多情况,而这些情况实际上是解析器中的错误。以下是几个常见的例子: 评论: 转义字符您确实需要转义字符,对吗 <p>To start a

这就是我现在得到的:

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

它可以捕捉多个if语句等

IMG:

但是当我进行嵌套时,if中的if会在第一次出现{%endif%}时停止

IMG:


是否有一种方法可以捕获与{%if…%}语句一样多的{%endif%}语句,如果有,如何捕获?

将模式更改为:

工作示例:

但是,这不是一个好主意:该模式缺少许多情况,而这些情况实际上是解析器中的错误。以下是几个常见的例子:

评论:

转义字符您确实需要转义字符,对吗

<p>To start a condition, use <code>\{% if aaa %}</code></p>
无效输入: 如果解析器能够在无效输入上相对良好地工作,并指向错误的正确位置,那就太好了


不要使用regexen,使用现有的细枝解析器。下面是我编写的一个提取器示例,它解析自定义标记并提取它们:

lexer的工作是将细枝源代码转换为对象;如果需要挂接到该流程中,您可以对其进行扩展:

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]);
    }

    ...

}
通常,细枝注释节点被细枝丢弃,此lexer将保存它们

但是,您主要关心的是如何使用解析器:

$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此处是节点树的根节点,这些节点以面向对象的方式表示细枝源,所有节点都已正确解析。您只需处理此树,而不必担心生成它所使用的确切语法:

 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);
          }
      }
 }

只需遍历它,直到找到要查找的节点类型并获取其信息。好好玩玩吧。这个示例代码可能有点过时,也可能没有。无论如何,您必须深入细枝解析器源代码才能理解它。

您使用的是什么语言?PHP正则表达式。preg_uu函数我在标签中添加了它,但是有人@Michael9说我最好删除它。您真的不应该为此使用regexen,解析器会做得更好更精确。这看起来像细枝语法;如果是这样的话,Twig有一个奇妙的解析器,您可以劫持/扩展/适用于此任务。@deceze这就是它的确切用途。我正在尝试编写一个可配置的模板解析器,您可以将其设置为读取变量,例如{{var}}或{$var},或者以您想要的方式读取。虽然我从未尝试过使用块和其他东西,但对我来说似乎太复杂了,它看起来也类似于Smarty模板语法。如果是现有模板语言,请使用提供的/本机库/扩展。如果您正在尝试使用自己的模板语言,最好在遇到更多问题之前立即停止,并使用现有的模板引擎。模板化需要对可能的输入流进行广泛的关注和测试,除了一些简单的正则表达式之外,还需要定义非常好的lexer/parser/tokenizer规则集。很抱歉,我不能接受两个最佳答案,所以我选择了最简单的一个。你的解释真的很好,但我正在寻找一个有效的递归正则表达式模式,也是为了了解它们是如何工作的。尽管如此,谢谢你的小树枝,不知道如何称呼它。你会很高兴使用正则表达式解决方案,直到有一天它在某些特殊情况下几乎不可避免地破裂。或者直到你需要扩展它。只是说…:是的,我知道。这就是为什么我要使用它:我经常创建小型PHP项目。因为理解PHP和HTML相互传递时发生的事情很痛苦,所以我总是使用模板引擎。因为大多数模板引擎对于简单的项目来说太复杂了,所以我决定只制作自己的模板引擎来完成简单的事情,比如if-then-else和循环。它不适用于任何复杂的事情。对于complex,我已经使用了Twig,这也是我选择该语法的原因。节省了我自己设计的时间。@aronvanwillige-避免php/HTML流问题的诀窍是不要将两者交织在一起。一个好的模板引擎应该比尝试创建和解析自己的模板引擎要轻松。好吧,这是公认的答案:虽然它没有回答我的问题,但你似乎真的很喜欢它……这是一段非常好的代码,Kobi。我找到了一些问题的解决办法:1。评论:这些可以在任何魔法发生之前过滤掉。preg_替换'//usi',$context;我想是吧?2.转义字符可以使用preg_replace_callback 3在页面上更改为HTML实体。字符串文字:与转义字符相同。不过,您的示例并没有太大意义,因为在条件的HTML输出中使用了逻辑。对于在一个坚实的模板框架上或者(如果他们坚持使用国产解决方案)在一组定义良好的工具上实现lexer/parser/tokenizer的自定义词典,您有什么建议吗?想想看,与其让他们认为他们需要将你的答案视为一个挑战/障碍,不如指向较低级别的工具。@Anthony-My-ans
wer是159个字符,后面是605个字符的警告,这些只是示例。我认为这就足够了,尤其是德塞兹的回答。这很公平。我没想到聊天会如此活跃,我想OP可能需要一些指导。
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   = 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);
 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);
          }
      }
 }