【问题标题】:preg_match kills PHP with core dump / no error displayed: How to improve RegEx?preg_match 使用核心转储杀死 PHP/不显示错误:如何改进 RegEx?
【发布时间】:2015-09-14 18:11:00
【问题描述】:

我有以下正则表达式:

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 错误 https://bugs.php.net/bug.php?id=45735

现在的问题是如何修复正则表达式以免遇到此错误。

【问题讨论】:

  • 在您的preg_match() 电话之后输入:print_r($matches);
  • 检查服务器上的PHP错误日志。
  • @Rizier123:不起作用。 preg_match 之后的步骤永远不会到达。我在 preg_match 之后用die('hard'); 验证了这一点。结果相同。 hard 从不显示。
  • @Barmar:什么都没有……
  • @Rizier123:正在努力……

标签: php regex debugging


【解决方案1】:

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

我建议将其更改为 (?:(?!function ).)*,因为您应该在使用角色之前进行检查。

重现问题with capturing group的最小示例:

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.

with non-capturing group:

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 的输出

(空输出)

【讨论】:

    【解决方案2】:

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

    那个'?'似乎是你表达中的问题导致了这么多回溯。我不知道;但删除?确实使回溯消失了。

    至于如何解决这个问题 - 重温 RegEx,投资一个工具。我曾经调查过灾难性的回溯,发现这个资源很有用 - http://www.regular-expressions.info/catastrophic.html。该网站还出售一种工具,该工具非常擅长以图形方式显示回溯,并希望为您提供有关如何避免回溯的足够提示。

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

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

    可能达到目的;尽管它会在执行 1000 次回溯后报告“不匹配”,即使如果允许更多回溯,也可以找到匹配项。

    【讨论】:

    • 你的帖子没有任何意义。正则表达式中没有?,除了(?!...),表示负前瞻。灾难性的回溯不会导致分段错误 - 特征行为是指数运行时间。相反,我已经看到实现(Java)在没有灾难性回溯的模式上给出 StackOverflowError - 原因是由于大量重复占用了堆栈空间以跟踪回溯到的位置。
    • 好的;你怎么知道在灾难性回溯中没有导致分段错误的错误?不可能?为什么限制回溯后故障就消失了?
    • 故障消失仅仅是因为您通过降低回溯限制间接限制了重复次数。您可能在灾难性回溯模式中观察到这种行为,但原因是由于量词的实现分配了太多空间。比较 3v4l.org/DRCKD3v4l.org/5Hg1v
    • 和分配太多空间会导致段错误?失控回溯->堆栈上分配的空间过多不是吗?究竟什么是太多的空间?文件有多大?量词能有多大?
    • 分段错误是量词实现的副作用。这不是灾难性回溯的必要条件(尽管它可能发生)。问题是 - 问题中的正则表达式不是灾难性回溯的情况,它是重复可以重复很多次的情况,这里的解决方法是不捕获重复的组。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-04
    相关资源
    最近更新 更多