preg_match杀死PHP与核心转储/没有错误显示:如何改进RegEx


preg_match kills PHP with core dump / no error displayed: How to improve RegEx?

我有以下正则表达式:

preg_match('/function ([[:alpha:]]{1,3})'((.(?!function ))*getKeywordSuggestValue=function/simU', $foo, $matches);

结果页只是空白(返回空结果)。看起来匹配的块会非常大。

500 Server closed connection without sending any data back
Content-Type: text/plain
Client-Date: Fri, 11 Sep 2015 09:35:55 GMT
Client-Warning: Internal response

display_errors打开,当我产生一个解析错误时,它会显示出来。只是这个不行。

preg_match如何杀死这样一个PHP脚本?

使用PHP 5.6

编辑

当尝试使用MCVE制作CLI脚本时,我发现和错误消息:

Segmentation fault (core dumped)

下面是可复制的示例:

<?php
$content = str_repeat('function fo(){ bar; bar; }', 1000) . <<<'EOF'
function cz(h,j,k,l,m,n,o,p,q,r){var x=this;x.setValue=function(a){if(!t.isActive)return;var b=t.Elements.input.value;if(a!=b){t.Elements.input.value=a;}}
x.getValue=function(){if(!t.isActive)return;var a=t.Elements.input.value;a=a.replace(/^'s+/,"").replace(/'s+$/,"");a=a.replace(/''/g,"");if(a){return a;}else{return null;}}
x.getKeywordSuggestValue=function(){if(!t.isActive)return;var a=t.Elements.input.value;a=a.replace(/^'s+/,"").replace(/['s ]+/g," ");a=a.replace(/''/g,"");if(a){return a;}else{return null;}}
EOF;
preg_match('/function ([[:alpha:]]{1,3})'((.(?!prototype'.render=function))*getKeywordSuggestValue=function/simU', $content, $matches);
echo 'preg_match passed';

经过一番研究,我发现了一个可能相关的PHP bug https://bugs.php.net/bug.php?id=45735

现在的问题是如何修复正则表达式,以不击中这个错误。

这可能是由于引擎不得不在堆栈上分配太多空间来跟踪重复捕获组(.(?!function ))*。由于捕获组在这里显然是无用的,因此将其更改为非捕获组(?:.(?!function ))*可以解决分割错误问题。

我建议将其更改为(?:(?!function ).)*,因为您应该在消费字符之前检查。

一个重现捕获组问题的最小示例:

print (preg_match('~^(a)*$~', str_repeat('a', 10000) . 'b'));

hhvm-3.6.1 - 3.9.0的输出

0

输出7.0.0alpha1——7.0.0rc2

(空输出)

5.3.18 - 5.6.13

Process exited with code 139.

和非捕获组:

print (preg_match('~^(?:a)*$~', str_repeat('a', 10000) . 'b'));

输出5.3.18——5.6.13 hhvm-3.6.1——3.9.0

0

输出7.0.0alpha1——7.0.0rc2

(空输出)

灾难性回溯-过多的回溯会导致时间和内存呈指数级增长;PHP似乎要崩溃了;其他一些实现将CPU占用到100%,最终失败;有些允许您定义超时或递归深度;像c#一样,你可以定义一个超时来避免正则表达式失控。

"?似乎是你表达方式的问题,导致了你这么多的回头是道。我不确定;但是移除呢?使回溯消失。

至于如何解决这个问题-重新学习RegEx,投资一个工具。我曾经调查过灾难性回溯,发现这个资源很有用- http://www.regular-expressions.info/catastrophic.html。该网站还出售一个工具,它在图形显示回溯方面非常出色,并希望能给你足够的提示,告诉你如何避免回溯。

或使用大锤-限制回溯的数量。

ini_set('pcre.backtrack_limit','1000');

可能达到目的;尽管在做了1000次回溯之后,它会报告一个"不匹配",即使在允许更多回溯的情况下可以找到匹配。