preg_match 粉碎阿帕奇

preg_match crush apache

我有一个模板引擎。解析 tpl 文件。但是当 tpl 文件有很多 {if},{foreach} 或 {language} 块时 preg_match crush apache.

这是我的 preg_match 函数;

preg_match_all('$\{(if|foreach)[\s]*(.*?)[\s]*\}((?:[^{]*(?:\{(?!\/?(if|foreach)[^}]*\})[^{]*)*|(?R))*)\{\/\}$iu',$content,$output);

这是 apache 日志

[Sun Jun 12 21:04:41.135620 2016] [mpm_winnt:notice] [pid 1888:tid 552] AH00428: Parent: child process 7620 exited with status 255 -- Restarting.
[Sun Jun 12 21:04:41.235425 2016] [mpm_winnt:notice] [pid 1888:tid 552] AH00455: Apache/2.4.9 (Win64) PHP/5.5.12 configured -- resuming normal operations
[Sun Jun 12 21:04:41.236426 2016] [mpm_winnt:notice] [pid 1888:tid 552] AH00456: Apache Lounge VC11 Server built: Mar 16 2014 12:42:59
[Sun Jun 12 21:04:41.236426 2016] [core:notice] [pid 1888:tid 552] AH00094: Command line: 'c:\wamp\bin\apache\apache2.4.9\bin\httpd.exe -d C:/wamp/bin/apache/apache2.4.9'
[Sun Jun 12 21:04:41.284459 2016] [mpm_winnt:notice] [pid 1888:tid 552] AH00418: Parent: Created child process 9668
[Sun Jun 12 21:04:41.642877 2016] [mpm_winnt:notice] [pid 9668:tid 452] AH00354: Child: Starting 64 worker threads.
[Sun Jun 12 21:04:42.047450 2016] [mpm_winnt:notice] [pid 1888:tid 552] AH00428: Parent: child process 9668 exited with status 255 -- Restarting.
[Sun Jun 12 21:04:42.147702 2016] [mpm_winnt:notice] [pid 1888:tid 552] AH00455: Apache/2.4.9 (Win64) PHP/5.5.12 configured -- resuming normal operations
[Sun Jun 12 21:04:42.147702 2016] [mpm_winnt:notice] [pid 1888:tid 552] AH00456: Apache Lounge VC11 Server built: Mar 16 2014 12:42:59
[Sun Jun 12 21:04:42.147702 2016] [core:notice] [pid 1888:tid 552] AH00094: Command line: 'c:\wamp\bin\apache\apache2.4.9\bin\httpd.exe -d C:/wamp/bin/apache/apache2.4.9'
[Sun Jun 12 21:04:42.194580 2016] [mpm_winnt:notice] [pid 1888:tid 552] AH00418: Parent: Created child process 10824
[Sun Jun 12 21:04:42.487866 2016] [mpm_winnt:notice] [pid 10824:tid 452] AH00354: Child: Starting 64 worker threads.

但是当我尝试regex101.com时,它匹配成功,没有任何错误。 https://regex101.com/r/uW8rZ8/3

Regex101 可能没有任何错误地运行,但它运行不正常。如果您看一下,它会显示:2 matches - 10792 steps,这意味着您的正则表达式可能发生了灾难性的回溯。如果不是,那么您可能没有使用正确的工具...您是否考虑过使用 actual 解析器?一种设计用于处理递归匹配的?

如果您仍然觉得这个需要用正则表达式来完成,您需要修复一些缺陷。

我不熟悉你必须直接帮助你解决你遇到的错误的设置(他们甚至是错误吗?),但我认为你需要解决一些更紧迫的问题才能解决其他事宜。此过程可能会解决您遇到的问题,因为它很可能是相关的。

现在对我来说最重要的是你的正则表达式缺乏可读性。我不知道你的正则表达式是做什么的。而且我不是你的普通程序员......我喜欢正则表达式,而且我通常能够轻松阅读它们。但不是这个。 (部分问题可能还在于,您并不是很清楚为什么 使用此正则表达式。)

我的第一个建议是使用 x 修饰符,它允许您像在普通程序中一样使用间距。我修改了您原来的正则表达式以使用间距,并且还删除了过多的反斜杠:

{(if|foreach)
[\s]*
(.*?)
[\s]*}
((?:
  [^{]*
  (?:{
    (?!/?
      (if|foreach)[^}]*}
    )
    [^{]*
  )*|(?R))*
)
{/}

我以前用正则表达式写过一个类似的递归解析器,所以我对这类东西有一些经验。据我所知,我的解析器速度更快,而且可读性更强(假设它的流程类似于 BNF 样式的解析器)。我想我现在如何更快,基于我的解析器:

$re = "`
{(if)     ((?&exp))}(*PRUNE)\s*((?&line)*)\s*(*PRUNE){/if}|
{(foreach)((?&exp))*}(*PRUNE)\s*((?&line)*)\s*(*PRUNE){/foreach}
|(?&other)+

|(*F)(?:
   (?'line'  (?&if)|(?&for)|(?&other)+)
   (?'if'    {if     (?&exp)}(*PRUNE)\s*(?&line)*\s*(*PRUNE){/if})
   (?'for'   {foreach(?&exp)}(*PRUNE)\s*(?&line)*\s*(*PRUNE){/foreach})
   (?'other' ([^{]+|{)(?! (/?if|/?foreach)))
   (?'exp'   [^}]*)
)`xis"; 

即使您的模式适用于 regex101,您也接近灾难性的回溯。您的模式适用于 regex101 而不适用于您的服务器的原因很简单:配置不一样。

您的模式中缺少两件事:

  • 使用所有格量词(或原子组)来禁止回溯无用的地方。
  • (?:A|B)* 之类的东西展开到 A*+(?:BA*)*+ 以避免交替并减少步骤数。

遵循这些建议将使您的模式效率提高约 3 倍:

~
{ (if\b|foreach\b) \s*+
( [^\s}]*+ (?:\s+[^\s}]+)*+ ) \s* }
(
    [^{]*+
    (?:  { (?!/?if\b|/?foreach\b) [^{]*
      |  (?R)                     [^{]* )*+

)
{/}
~ixu

demo