PHP 正则表达式使 Apache 崩溃

标签 php regex windows apache

我有一个匹配模板系统的正则表达式,不幸的是,它似乎在一些适度琐碎的查找中使 apache(它在 Windows 上运行)崩溃。我已经研究了这个问题,并且有一些关于增加堆栈大小等的建议,但似乎都没有用,而且我真的不喜欢通过提高限制来处理此类问题,因为它通常只会将错误推向 future 。

关于如何更改正则表达式以使其不太可能出错的任何想法?

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

正则表达式:

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

示例模板:

<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>

我想这是一个远景。 :(

最佳答案

试试这个:

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

或者,以可读的形式:

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

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

[^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*

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

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

附带说明,我删除了 s修饰符,因为因为我没有使用点( . )和 m修饰符,因为从来不需要它。我还使用了命名反向引用 (?P=name)而不是 \3根据@DaveRandom 的极好建议。我转义了所有大括号({}),因为我发现这样更容易阅读。


编辑:如果你想匹配最里面命名 block ,改变正则表达式的中间部分:

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

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

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

最初,(?P<opening>...) group 会捕获它看到的第一个开始标签,然后是 (?P<contents>..) group 会消费任何东西——包括其他标签——只要它们不是与 (?P<opening>...) 找到的标签相匹配的结束标签。团体。 (然后 (?P<closing>...) 组将继续使用它。)

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

这样描述听起来效率很低,但事实并非如此。我之前描述的技巧,吸食非牙套,淹没了这些错误开始的影响。你几乎在每个位置都进行了负面前瞻,现在你只在遇到 { 时才做一个。 .您甚至可以使用所有格量词,正如@godspeedlee 所建议的那样:

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

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

关于PHP 正则表达式使 Apache 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11850676/

相关文章:

php - 使用具有两个不同域的 cakephp session

类似于路由器登录的 PHP 用户身份验证

php - Preg 在 php 中将抑扬符与 ^ 匹配

javascript - 如何使用正则表达式转换文本中的表情符号?

windows - 我正在 groovy 中的 jenkins 中构建一个管道作业,它必须首先运行一个批处理命令,在 s

php - INSERT IGNORE INTO 不起作用

regex - Intellij 想法 : how to put dollar sign to the replaced content

regex - 我的正则表达式匹配太多。我如何让它停止?

java - WEPOS 的 JVM

c - 如何在 C win32 控制台应用程序 EXE 文件中保存包含的库