function parseAPIResults($results){ //Takes results from getAPIResults,returns array. $pattern = '/\[(.|\n)+\]/'; $resultsArray = preg_match($pattern,$results,$matches); }
Firefox 6: The connection was reset
Chrome 14: Error 101 (net::ERR_CONNECTION_RESET): The connection was
reset.IE 8: Internet Explorer cannot display the webpage
更新:
Apache / PHP可能会崩溃.以下是运行脚本时的Apache错误日志:
[Sat Oct 01 11:41:40 2011] [notice] Parent: child process exited with
status 255 — Restarting.
[Sat Oct 01 11:41:40 2011] [notice]
Apache/2.2.11 (Win32) PHP/5.3.0 configured — resuming normal
operations
在Windows 7上运行WAMP 2.0.
是的,由于堆栈溢出,这类正则表达式会重复(并且默默地)使Apache / PHP崩溃,从而导致未处理的分段错误!
背景:
PHP preg_ *系列正则表达式功能使用了Philip Hazel强大的PCRE library.使用这个库,有一类正则表达式需要对其内部match()函数进行大量的递归调用,并且这会占用很多堆栈空间(并且所使用的堆栈空间与主题字符串的大小成正比)匹配).因此,如果主题串太长,则会发生堆栈溢出和相应的分段故障.这个行为在PCRE documentation年底的标题为:pcrestack的部分描述.
PHP Bug 1:PHP集:pcre.recursion_limit太大.
PCRE文档描述了如何通过将递归深度限制为大致等于链接应用程序的堆栈大小的安全值除以500来避免堆栈溢出分段错误.当递归深度按照推荐适当限制时,库不会生成一个堆栈溢出,而是正常退出并显示错误代码.在PHP下,这个最大递归深度用pcre.recursion_limit配置变量指定,(不幸的是)默认值设置为100,000.这个值是TOO BIG!以下是各种可执行堆栈大小的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
因此,对于具有256KB的(相对较小)的堆栈大小的Apache Web服务器(httpd.exe)的Win32构建,pcre.recursion_limit的正确值应设置为524.这可以通过以下行PHP代码:
ini_set("pcre.recursion_limit","524"); // PHP default is 100,000.
当此代码添加到PHP脚本时,不会发生堆栈溢出,而是生成有意义的错误代码.那就是它应该会产生一个错误代码! (但不幸的是,由于另一个PHP错误,preg_match()不会.)
PHP Bug 2:preg_match()不会返回FALSE错误.
preg_match()的PHP文档表示错误返回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()返回除int(1)以外的任何东西,需要明确检查PCRE错误.
>如果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,$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..."); ?>
当您运行此脚本时,它会连续读取主题字符串的当前长度.如果pcre.recursion_limit的默认值过高,则允许您测量导致可执行文件崩溃的字符串长度.
注释:
>在调查这个问题的答案之前,我不知道在PHPE库发生错误时,preg_match()无法返回FALSE的PHP错误.这个bug肯定会引起大量的使用preg_match的代码! (我当然会做一个自己的PHP代码的清单.)
>在Windows下,Apache Webserver可执行文件(httpd.exe)的堆栈大小为256KB. PHP命令行可执行文件(PHP.exe)的堆栈大小为8MB. pcre.recursion_limit的安全值应根据脚本在(524和16777)下运行的可执行文件进行设置.
>在* nix系统下,Apache Web服务器和命令行可执行文件通常使用8MB的堆栈大小来构建,因此不会经常遇到此问题.
> PHP开发人员应将pcre.recursion_limit的默认值设置为安全值.
> PHP开发人员应该将preg_match()修补程序应用于PHP版本5.2.
> Windows可执行文件的堆栈大小可以使用CFF Explorer免费程序手动修改.您可以使用此程序来增加Apache httpd.exe可执行文件的堆栈大小. (这在XP下工作,但Vista和Win7可能会抱怨)