PHP正则表达式崩溃apache

Mee*_*p3D 10 php regex windows apache

我有一个匹配模板系统的正则表达式,遗憾的是它似乎在一些简单易读的查找中崩溃了apache(它在Windows上运行).我已经研究了这个问题,并且有一些建议可以提高堆栈大小等,但这些建议似乎都没有用,而且我真的不喜欢通过增加限制来处理这些问题,因为它通常只会将bug推向未来.

无论如何,任何关于如何改变正则表达式以使其不太可能被污染的想法?

我的想法是捕获最里面的块(在这种情况下{block:test}This should be caught first!{/block:test}),然后我将str_replace out起始/结束标记,并通过正则表达式重新运行整个事务,直到没有剩余块.

正则表达式:

~(?P<opening>{(?P<inverse>[!])?block:(?P<name>[a-z0-9\s_-]+)})(?P<contents>(?:(?!{/?block:[0-9a-z-_]+}).)*)(?P<closing>{/block:\3})~ism
Run Code Online (Sandbox Code Playgroud)

示例模板:

<div class="f_sponsors s_banners">
    <div class="s_previous">&laquo;</div>
    <div class="s_sponsors">
        <ul>
            {block:sponsors}
            <li>
                <a href="{var:url}" target="_blank">
                    <img src="image/160x126/{var:image}" alt="{var:name}" title="{var:name}" />
                </a>
            {block:test}This should be caught first!{/block:test}
            </li>
            {/block:sponsors}
        </ul>
    </div>
    <div class="s_next">&raquo;</div>
</div>
Run Code Online (Sandbox Code Playgroud)

我想这是一个很长的镜头.:(

Ala*_*ore 4

试试这个:

'~(?P<opening>\{(?P<inverse>[!])?block:(?P<name>[a-z0-9\s_-]+)\})(?P<contents>[^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*)(?P<closing>\{/block:(?P=name)\})~i'
Run Code Online (Sandbox Code Playgroud)

或者,以可读的形式:

'~(?P<opening>
  \{
  (?P<inverse>[!])?
  block:
  (?P<name>[a-z0-9\s_-]+)
  \}
)
(?P<contents>
  [^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*
)
(?P<closing>
  \{
  /block:(?P=name)
  \}
)~ix'
Run Code Online (Sandbox Code Playgroud)

最重要的部分是在(?P<contents>..)组中:

[^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*
Run Code Online (Sandbox Code Playgroud)

一开始,我们唯一感兴趣的字符是左大括号,因此我们可以使用 吞掉任何其他字符[^{]*。只有在看到 a 后,{我们才会检查它是否是{/block}标签的开头。如果不是,我们将继续使用它并开始扫描下一个,并根据需要重复。

使用 RegexBuddy,我通过将光标放在标记的开头{block:sponsors}并进行调试来测试每个正则表达式。然后我从结束{/block:sponsors}标记中删除了结束大括号以强制匹配失败并再次调试它。您的正则表达式执行了 940 步才成功,执行了 2265 步才失败。我的成功了 57 步,失败了 83 步。

顺便说一句,我删除了s修饰符,因为我没有使用点 ( .),而m修饰符则因为从来不需要它。我还使用了命名反向引用 (?P=name),而不是\3按照 @DaveRandom 的出色建议。我省略了所有的大括号({}),因为我发现这样更容易阅读。


编辑:如果您想匹配最里面的命名块,请更改正则表达式的中间部分:

(?P<contents>
  [^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*
)
Run Code Online (Sandbox Code Playgroud)

...对此(正如@Kobi 在他的评论中所建议的):

(?P<contents>
  [^{]*(?:\{(?!/?block:[a-z0-9\s_-]+\})[^{]*)*
)
Run Code Online (Sandbox Code Playgroud)

最初,该(?P<opening>...)组将抓取它看到的第一个开始标签,然后该(?P<contents>..)组将使用任何内容(包括其他标签),只要它们不是与该(?P<opening>...)组找到的结束标签相匹配的结束标签。(然后该(?P<closing>...)小组将继续消费它。)

现在,该(?P<contents>...)组拒绝匹配任何标签,无论是开始还是结束(请注意开头的/?),无论名称是什么。因此,正则表达式最初开始匹配该{block:sponsors}标签,但是当它遇到该{block:test}标签时,它会放弃该匹配并返回到搜索开始标签。它再次从标签处开始{block:test},这次当它找到结束标签时成功完成匹配{/block:test}

这样描述听起来效率很低,但事实并非如此。我之前描述的技巧,即吞掉非括号,淹没了这些错误开始的影响。之前您几乎在每个位置都进行了否定前瞻,现在您只在遇到{. 您甚至可以使用所有格量词,如 @godspeedlee 所建议的:

(?P<contents>
  [^{]*+(?:\{(?!/?block:[a-z0-9\s_-]+\})[^{]*+)*+
)
Run Code Online (Sandbox Code Playgroud)

...因为你知道它永远不会消耗任何稍后必须归还的东西。这会稍微加快速度,但这并不是真正必要的。

  • 很不错!我*认为*我理解OP想要什么...我认为“嵌套块”意味着任何名称的嵌套块,而不仅仅是同名的嵌套块,这些块会被迭代地替换。因此“{1} {2} {/2} {/1}”应在“1”之前捕获“2”。如果是这种情况,您可以轻松地将中间部分从 `[^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*` 更改为`[^{]*(?:\{(?!/?block:[a-z0-9\s_-]+\})[^{]*)*` - http://regexr.com?31qsf (2认同)