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%}语句,如果有,如何捕获?将模式更改为: 工作示例: 然而,这是一个坏主意:该模式缺少许多情况,而这些情况实际上是解析器中的错误。以下是几个常见的例子: 评论: {% if aaa %} 123 <!-- {% endif %
/{%if(+?)%}(.*){%endif%}/gusi
它可以捕捉多个if语句等
IMG:
但是当我进行嵌套时,if中的if会在第一次出现{%endif%}时停止
IMG:
是否有一种方法可以捕获与{%if…%}语句一样多的{%endif%}语句,如果有,如何捕获?将模式更改为: 工作示例: 然而,这是一个坏主意:该模式缺少许多情况,而这些情况实际上是解析器中的错误。以下是几个常见的例子:
- 评论:
{% if aaa %} 123 <!-- {% endif %} --> {% endif %}
- 转义字符(您确实需要转义字符,对吗?)
- 无效输入:
如果解析器能够在无效输入上相对良好地工作,并指向错误的正确位置,那就太好了
- 不要使用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规则集。很抱歉,我不能接受两个最佳答案,所以我选择了最简单的一个。你的解释真的很好,但我正在寻找一个有效的递归正则表达式模式,也是为了了解它们是如何工作的。尽管如此,还是要感谢你的小树枝(不知道如何称呼它),你会对正则表达式的解决方案感到满意,直到有一天它在某些特殊情况下几乎不可避免地破裂。或者直到你需要扩展它。只是说…:o) 是的,我知道。这就是为什么我要使用它:我经常创建小型PHP项目。因为理解PHP和HTML相互传递时发生的事情很痛苦,所以我总是使用模板引擎。因为大多数模板引擎对于简单的项目来说太复杂了,所以我决定只制作自己的模板引擎来完成简单的事情,比如if-then-else和循环。它不适用于任何复杂的事情。对于complex,我已经使用了Twig,这也是我选择该语法的原因。节省了我自己设计的时间。@aronvanwillige-避免php/HTML流问题的诀窍是不要将两者交织在一起。一个好的模板引擎应该比尝试创建和解析自己的模板引擎要轻松。好吧,这是一个公认的答案:)虽然它没有回答我的问题,但你似乎真的很喜欢它…这是一段非常好的代码,Kobi。我找到了一些问题的解决办法:1。评论:这些可以在任何魔法发生之前过滤掉<代码>preg_replace('//usi','$context)我想是吧?2.可以使用
preg\u replace\u callback
3将转义字符预先更改为HTML实体。字符串文字:与转义字符相同。不过,您的示例并没有太大意义,因为在条件的(HTML)输出中使用了逻辑。对于操作,您有什么建议吗?要么是一个坚实的模板框架,要么是一组定义良好的工具,用于实现lexer/parser/tokenizer的自定义词典?只是想一想,与其让他们认为他们需要将你的答案视为挑战/障碍,不如指向低级工具。@Anthony-我的答案是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);
}
}
}