preg_match函数中的RegExp返回浏览器错误

use*_*552 15 php regex apache wamp connection-reset

以下函数打破了我在$ pattern变量中提供的正则表达式.如果我改变正则表达式我很好,所以我认为这是问题所在.但是,我没有看到这个问题,即使它们已经打开,我也没有收到标准的PHP错误.

function parseAPIResults($results){
//Takes results from getAPIResults, returns array.

    $pattern = '/\[(.|\n)+\]/';
    $resultsArray = preg_match($pattern, $results, $matches);

}
Run Code Online (Sandbox Code Playgroud)

Firefox 6:连接已重置

Chrome 14:错误101(net :: ERR_CONNECTION_RESET):连接已重置.

IE 8:Internet Explorer无法显示网页

更新:
Apache/PHP可能崩溃.这是我运行脚本时的Apache错误日志:

[2011年10月1日星期六11:41:40] [通知]父:子进程退出状态255 - 重新启动.
[2011年10月1日星期六11:41:40] [通知]配置Apache/2.2.11(Win32)PHP/5.3.0 - 恢复正常运行

在Windows 7上运行WAMP 2.0.

rid*_*ner 53

简单的问题.复杂的答案!

是的,由于堆栈溢出,这类正则表达式将重复(并默默地)使Apache/PHP崩溃,导致未处理的分段错误!

背景:

PHP preg_*系列正则表达式函数使用Philip Hazel 强大的PCRE库.使用这个库,有一类regex需要对其内部match()函数进行大量的递归调用,这会占用大量的堆栈空间(并且使用的堆栈空间与正在匹配的主题字符串的大小成正比) .因此,如果主题字符串太长,则将发生堆栈溢出和相应的分段错误.PCRE文档最后在标题为:pcrestack的部分中描述了这种行为.

PHP Bug 1:PHP集:pcre.recursion_limit太大了.

PCRE文档描述了如何通过将递归深度限制为大致等于链接应用程序的堆栈大小除以500的安全值来避免堆栈溢出分段错误.当递归深度按照建议正确限制时,库不会生成堆栈溢出,而是优雅地退出并显示错误代码.在PHP下,使用pcre.recursion_limit配置变量指定此最大递归深度,并且(不幸的是)默认值设置为100,000.这个值太大了!以下是pcre.recursion_limit各种可执行堆栈大小的安全值表:

Stacksize   pcre.recursion_limit
 64 MB      134217
 32 MB      67108
 16 MB      33554
  8 MB      16777
  4 MB      8388
  2 MB      4194
  1 MB      2097
512 KB      1048
256 KB      524
Run Code Online (Sandbox Code Playgroud)

因此,对于Apache webserver(httpd.exe)的Win32版本,它具有(相对较小的)256KB的堆栈大小,正确的值pcre.recursion_limit应该设置为524.这可以通过以下PHP代码行来完成:

ini_set("pcre.recursion_limit", "524"); // PHP default is 100,000.
Run Code Online (Sandbox Code Playgroud)

将此代码添加到PHP脚本时,不会发生堆栈溢出,而是生成有意义的错误代码.也就是说,它应该生成一个错误代码!(但不幸的是,由于另一个PHP错误,preg_match()不会.)

PHP Bug 2:preg_match()出错时不返回FALSE.

PHP文档preg_match()说它在出错时返回FALSE.不幸的是,PHP版本5.3.3及更低版本有一个错误(#52732),其中preg_match()不会返回FALSE错误(它返回int(0),这是在不匹配的情况下返回的相同值).PHP版本5.3.4中修复了此错误.

解:

假设您将继续使用WAMP 2.0(使用PHP 5.3.0),解决方案需要考虑上述两个错误.这是我建议的:

  • 需要降低pcre.recursion_limit到安全值:524.
  • 每当preg_match()返回除了之外的任何内容时,需要显式检查PCRE错误int(1).
  • 如果preg_match()返回int(1),则匹配成功.
  • 如果preg_match()返回int(0),则匹配不成功,或者出现错误.

以下是脚本的修改版本(旨在从命令行运行),它确定导致递归限制错误的主题字符串长度:

<?php
// This test script is designed to be run from the command line.
// It measures the subject string length that results in a
// PREG_RECURSION_LIMIT_ERROR error in the preg_match() function.

echo("Entering TEST.PHP...\n");

// Set and display pcre.recursion_limit. (set to stacksize / 500).
// Under Win32 httpd.exe has a stack = 256KB and 8MB for php.exe.
//ini_set("pcre.recursion_limit", "524");       // Stacksize = 256KB.
ini_set("pcre.recursion_limit", "16777");   // Stacksize = 8MB.
echo(sprintf("PCRE pcre.recursion_limit is set to %s\n",
    ini_get("pcre.recursion_limit")));

function parseAPIResults($results){
    $pattern = "/\[(.|\n)+\]/";
    $resultsArray = preg_match($pattern, $results, $matches);
    if ($resultsArray === 1) {
        $msg = 'Successful match.';
    } else {
        // Either an unsuccessful match, or a PCRE error occurred.
        $pcre_err = preg_last_error();  // PHP 5.2 and above.
        if ($pcre_err === PREG_NO_ERROR) {
            $msg = 'Successful non-match.';
        } else {
            // preg_match error!
            switch ($pcre_err) {
                case PREG_INTERNAL_ERROR:
                    $msg = 'PREG_INTERNAL_ERROR';
                    break;
                case PREG_BACKTRACK_LIMIT_ERROR:
                    $msg = 'PREG_BACKTRACK_LIMIT_ERROR';
                    break;
                case PREG_RECURSION_LIMIT_ERROR:
                    $msg = 'PREG_RECURSION_LIMIT_ERROR';
                    break;
                case PREG_BAD_UTF8_ERROR:
                    $msg = 'PREG_BAD_UTF8_ERROR';
                    break;
                case PREG_BAD_UTF8_OFFSET_ERROR:
                    $msg = 'PREG_BAD_UTF8_OFFSET_ERROR';
                    break;
                default:
                    $msg = 'Unrecognized PREG error';
                    break;
            }
        }
    }
    return($msg);
}

// Build a matching test string of increasing size.
function buildTestString() {
    static $content = "";
    $content .= "A";
    return '['. $content .']';
}

// Find subject string length that results in error.
for (;;) { // Infinite loop. Break out.
    $str = buildTestString();
    $msg = parseAPIResults($str);
    printf("Length =%10d\r", strlen($str));
    if ($msg !== 'Successful match.') break;
}

echo(sprintf("\nPCRE_ERROR = \"%s\" at subject string length = %d\n",
    $msg, strlen($str)));

echo("Exiting TEST.PHP...");

?>
Run Code Online (Sandbox Code Playgroud)

运行此脚本时,它会提供主题字符串当前长度的连续读数.如果pcre.recursion_limit保留其过高的默认值,则允许您测量导致可执行文件崩溃的字符串长度.

评论:

  • 在调查这个问题的答案之前,我不知道在PCRE库中发生错误时preg_match()无法返回的PHP错误FALSE.这个bug肯定会引起很多使用的代码的质疑preg_match!(我当然会对我自己的PHP代码进行清点.)
  • 在Windows下,Apache webserver executable(httpd.exe)使用256KB的堆栈大小构建.PHP命令行executable(php.exe)使用8MB的堆栈大小构建.pcre.recursion_limit应根据脚本运行的可执行文件(分别为524和16777)设置安全值.
  • 在*nix系统下,Apache Web服务器和命令行可执行文件通常都使用8MB的堆栈大小构建,因此不会经常遇到此问题.
  • PHP开发人员应将默认值设置pcre.recursion_limit为安全值.
  • PHP开发人员应该将错误preg_match()修复应用于PHP 5.2版.
  • 可以使用CFF Explorer免费软件程序手动修改Windows可执行文件的堆栈大小.您可以使用此程序来增加Apache httpd.exe可执行文件的堆栈大小.(这适用于XP,但Vista和Win7可能会抱怨.)

  • 它帮助我:http://stackoverflow.com/questions/5058845/how-do-i-increase-the-stack-size-for-apache-running-under-windows-7 (3认同)

jas*_*bar 0

preg_match返回为模式找到的匹配数。当你有一个匹配时,它会在 php 中导致致命错误(print_r(1)例如,导致错误)。print_r(0) (当您更改模式并且没有匹配项时)不会,只打印出 0。

你要print_r($matches)

顺便说一句,你的模式没有正确转义。使用双引号意味着您需要转义括号前面的反斜杠。