在PHP7中正则表达式超时?

在PHP7中正则表达式超时?,php,regex,Php,Regex,下面我有2个正则表达式,它在PHP5.6中工作,但如果字符+空格>1035,则在PHP7.x中不工作。示例:[扰流板]1035个字符的计数+ws[/spoiler]。我使用了这个站点,用它来测试正则表达式,它似乎工作得很好 从中,我可以看出问题可能与正则表达式超时有关?但这是密码,除非我弄错了 // [spoiler]Text[/spoiler] preg_match_all('/\[spoiler\]\s*((\s|.)+?)\s*\[\/spoiler\]/', $s, $matches,

下面我有2个正则表达式,它在PHP5.6中工作,但如果字符+空格>1035,则在PHP7.x中不工作。示例:[扰流板]1035个字符的计数+ws[/spoiler]。我使用了这个站点,用它来测试正则表达式,它似乎工作得很好

从中,我可以看出问题可能与正则表达式超时有关?但这是密码,除非我弄错了

// [spoiler]Text[/spoiler]
preg_match_all('/\[spoiler\]\s*((\s|.)+?)\s*\[\/spoiler\]/', $s, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
    $id = substr(md5($match[1]), 0, 10) . mt_rand(0, 10000);
    $s  = str_replace($match[0],
                      '<a href="javascript:klappe_function(\'' . $id . '\')" style="color: #aaa" title="Open Spoiler"><img src="images/plus.png" id="pic' . $id . '" class="spoiler" alt="spoiler" /></a><div id="k' . $id . '" style="display: none;">' . $match[1] . '</div><br />',
                      $s);
}

// [spoiler=Heading]Text[/spoiler]
preg_match_all('/\[spoiler=(.+?)\]\s*((\s|.)+?)\s*\[\/spoiler\]/', $s, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
    $id = substr(md5($match[2]), 0, 10). mt_rand(0, 10000);
    $s  = str_replace($match[0],
                      '<a href="javascript:klappe_function(\'' . $id . '\')" style="color: #aaa" title="Open ' . myhtmlentities($match[1], ENT_QUOTES)  . '"><img src="images/plus.png" id="pic' . $id . '" class="spoiler" alt="spoiler" /> <b>' . $match[1] . '</b></a><div id="k' . $id . '" style="display: none;">' . $match[2] . '</div><br />',
                      $s);
}
/[spoiler]文本[/spoiler]
预匹配所有('/\[spoiler\]\s*(\s.+?)\s*\[\/spoiler\]/',$s,$matches,预设置顺序);
foreach($matches作为$match进行匹配){
$id=substr(md5($match[1]),0,10).mt_rand(0,10000);
$s=str_replace($match[0],
'.$match[1].
, $s); } //[spoiler=标题]文本[/spoiler] 预匹配所有('/\[spoiler=(.+?)\]\s*(\s.+?)\s*\[\/spoiler\]/',$s,$matches,预设置顺序); foreach($matches作为$match进行匹配){ $id=substr(md5($match[2]),0,10).mt_rand(0,10000); $s=str_replace($match[0], '.$match[2].
, $s); }
正如评论所说,正则表达式效率非常低,这可能是原因所在-您可以使用一个正则表达式来完成所有这些:

\[扰流板(:?\=([^\]]+)?\]((:?[^\[]+\124;\[(?!=\/spoiler\]))+)\[\/spoiler\]

  • \[spoiler
    匹配
    [spoiler
  • (:?
    启动非捕获组(不会在$matches中)
    • \=
      匹配
      =
    • 启动第一个捕获组
    • [^\]]+
      匹配除
      ]
      字符以外的任何字符一次或多次
    • 关闭第一个捕获组
  • )?
    关闭组并使其成为可选
  • 启动第二个匹配组
    • (:?
      启动非捕获组(不会在$matches中)
      • [^\[]+
        匹配除
        [
        字符以外的任何字符一次或多次
      • |
      • \[(?!=\/spoiler\]
        单个
        [
        字符,只要后面没有
        /spoiler]
    • )+
      重复分组一次或多次
  • 关闭第二个匹配组
  • \[\/spoiler\]
    匹配
    [/spoiler]
这两种情况都可以考虑(只需检查$match[1]是否为空就可以知道是哪种情况),但它不会尝试删除空白,在使用
trim($match[1],“\r\n\t”)后,您可以在php中轻松地执行此操作。

这里有两个关键点:

匹配“标题”文本

[^\]+
[
字符以外的任何字符匹配一次或多次

这比查找任何可能导致“回溯”的字符要快得多,请参见:

与扰流板内容物匹配

  • (:?
    启动非捕获组(不会在$matches中)
    • [^\[]+
      匹配除
      [
      字符以外的任何字符一次或多次
    • |
    • \[(?!=\/spoiler\]
      单个
      [
      字符,只要后面没有
      /spoiler]
  • )+
    重复分组一次或多次
这允许在您的扰流板文本中添加
[
字符,但同样不会导致回溯。它通过查找一系列非
[
字符或单个
[
字符来实现此目的,只要后面没有紧跟
/spoiler]
-这是一种“消极的前瞻”,请参见:


尽管如此,如果你想高效地完成这项工作,正则表达式可能不是最好的工具,标记器或者preg_split可能会更好。

毫不奇怪,正则表达式是一团糟。如果你替换
(\s|]+?
(“正则表达式性能杀手”)使用
+?
并使用
/s
修饰符,它将更加高效,但仍然没有那么高效。正则表达式在去除周围空白的同时搜索扰流板标记内的任何文本。您可能更适合简单地捕获扰流板标记内的所有文本,然后将其作为单独的st去除ep
已经匹配了
\s
,您的正则表达式可能运行缓慢并且超时,因为它执行了大量的回溯。@Willbanwell:不完全是
匹配除换行符以外的所有字符。您应该使用
preg\u match\u all
,而不是使用
foreach
循环和
str\u replace
uld使用
preg\u replace\u callback
。您需要
[^\[]+
中的
+
吗?对于非捕获组外部的
+
来说,它似乎是多余的。第一个实例是捕获您的“头”文本,这比查找任何字符都好,因为这会导致正则表达式引擎做更多的工作。第二个实例是,您仍然可以在扰流板中保留
[
字符,因此它查找除
[
字符以外的任何字符,或者特别是
[
结束标记文本后面不跟其他字符的字符。