我想帮忙。我以前见过这个问题!
您的正则表达式在逻辑上看起来 A-Ok,但是当应用于较大的主题字符串时,它可能会导致大量递归回溯,这会导致 PCRE 引擎中的堆栈溢出。此溢出会导致分段错误和 PCRE 可执行文件(Apache 或 PHP)崩溃,而不会发出警告。 (症状是 "connection closed by remote server" 消息。)这种未处理的崩溃是由于 PHP 对 pcre.recursion_limit 参数的默认设置选择不当(默认为 100,000,即太高)。首先让我们看看这是否真的是问题的一部分。
将以下代码添加到您的脚本中:
// Place this at the top of the script
ini_set("pcre.recursion_limit", "524"); // 256KB stack. Win32 Apache
$re = '#<(code|pre)([^>]*)>(((?!</?\1).)*|(?R))*</\1>#si';
$text = preg_replace_callback($re, 'self::replaceit', $text);
// Check the return value for NULL which indicates a PCRE error.
if ($text === null) exit("PCRE Error! Subject too large or complex.");
有了这个,您应该不再收到“连接关闭”消息,而是 PCRE 错误退出消息。请注意,上述 524 设置适用于 Win32 Apache httpd.exe(具有 256KB 堆栈)。如果您在 *nix 服务器上运行,则可以将此值提高到 16777。这些数字背后的原因是 recursion _limit 值应设置为可执行文件堆栈大小除以 500。WIN32 可执行文件通常具有 256KB 堆栈和 *nix 可执行文件通常使用 8MB 堆栈构建。 Philip Hazel,(excellent PCRE 引擎的作者)已经详细解决了这个问题。见:pcrestack man page
完成此操作后,请回来报告,我会在下一阶段提供帮助...
(请注意,导致问题的不是 (?R) 表达式。稍后会详细说明。)
通过实施 Jeffrey Friedl 的 “Unrolling-the-Loop” 效率技术,可以显着改进正则表达式(在解决此问题和提高速度方面)。这将大大减少必要的回溯次数,并可能解决您的问题。这是您的正则表达式的改进(和彻底评论)版本。
$re = '% # Match an outermost PRE or CODE element.
( # $1: PRE/CODE element open tag
<(code|pre) # $2: Open tag name
[^>]*+> # Remainder of opening tag.
) # End $1: PRE/CODE element open tag.
( # $3: PRE/CODE element contents.
(?: # Group for contents alternatives
(?R) # Either a nested PRE or CODE element
| # Or non- <CODE, </CODE, <PRE or </PRE stuff.
[^<]*+ # Begin: {normal* (special normal*)*} construct
(?: # See: "Mastering Regular Expressions".
< # {special} Match a <, but only if it is
(?!/?\2) # not the start of a nested or closing tag.
[^<]*+ # match more {normal*}
)*+ # Finish "Unrolling the loop"
)*+ # Zero or more contents alternatives.
) # End $3: PRE/CODE element contents.
(</\2>) # $4: PRE/CODE element close tag
%ix';
然而,这个正则表达式的不同之处在于它使用了四个捕获组:$1 包含整个元素开始标记,$2 包含元素标记名称(用作反向引用),$3 包含元素内容,$4 包含元素结束标记。