pcre.backtrack_limit的“单位”是什么?

10
我遇到了一个问题,使用复杂的正则表达式的preg_replace()函数会导致错误(PREG_BACKTRACK_LIMIT_ERROR),这是因为pcre.backtrack_limit设置得太低,默认为1,000,000。我将其设置为10,000,000,这在特定应用中有效。
我的问题是,backtracking limit的“单位”是什么?这个1,000,000的数字是否对应于内存大小?如果不是,它表示什么?我想了解在我的环境中对此设置一个合理值是什么意思。
关于preg_replace的参考资料: https://www.php.net/manual/en/pcre.configuration.php#ini.pcre.backtrack-limit 关于回溯的参考资料: 在正则表达式中,什么是回溯/回溯引用?

首先,你在这里询问的不是“单位” - 这类似于英里或千克,但这里没有涉及任何单位,只是一个普通的数字。它设置了回溯尝试的限制,在搜索变得过于复杂/内存消耗过大之前放弃搜索。 - CBroe
2
你刚刚回答了我的问题。"回溯尝试次数"是一个单位 : )。如果你能提供一个带有参考链接的链接,我会很高兴接受你的答案。 - laketuna
顺便说一句,如果你遇到这种错误,我建议你重写你的模式而不是改变回溯限制。 - Casimir et Hippolyte
@Casimir,很不幸我不确定我能否在这里发布它。这不是我的。我只是更感兴趣PHP设置是关于什么的。 - laketuna
3个回答

7

PCRE源代码来看,当“match()”被递归调用超过100万次时,会返回此错误:

/* First check that we haven't called match() too many times, or that we
haven't exceeded the recursive call limit. */

if (md->match_call_count++ >= md->match_limit) RRETURN(PCRE_ERROR_MATCHLIMIT);

在这里,它被转换为“PHP_PCRE_BACKTRACK_LIMIT_ERROR”错误

根据pcreapi manpage(请参见https://serverfault.com/a/408272/140833):

在内部,PCRE使用一个名为match()的函数进行匹配(有时是递归调用)。由match_limit设置的限制对在匹配期间调用该函数的次数施加了限制,从而限制了可以发生的回溯量。 对于未锚定的模式,计数从主题字符串中的每个位置重新开始。

因此,我认为单位是“回溯尝试次数”。但我不确定它是否是一对一的。

以下是使用简单的“灾难性回溯”正则表达式隔离错误情况的演示:

<?php

ini_set('pcre.backtrack_limit', 100);

for ($len = 1000; $len <= 1001; $len++) {

    $x = str_repeat("x", $len);
    $ret = preg_match("/x+x+y/", $x);

    echo "len = " . $len . "\n";
    echo "preg_match = " . $ret . "\n";
    echo "PREG_BACKTRACK_LIMIT_ERROR = " . PREG_BACKTRACK_LIMIT_ERROR . "\n";
    echo "preg_last_error = " . preg_last_error() . "\n";
    echo "\n";
}

在此处运行此代码:https://3v4l.org/EpaNC,可得到以下输出结果:
len = 1000
preg_match = 0
PREG_BACKTRACK_LIMIT_ERROR = 2
preg_last_error = 0

len = 1001
preg_match = 
PREG_BACKTRACK_LIMIT_ERROR = 2
preg_last_error = 2

1

不知道这是否有帮助: 根据pcre的源代码,当pcre触发PCRE_ERROR_MATCHLIMIT时,会出现此错误代码。 而根据pcre的此更改日志,这可能是您的问题,因为您的正则表达式可能会导致内存泄漏。

我建议您检查您的正则表达式以解决问题,否则,如果您坚持要使其工作,可以做(但我不建议)像这样的事情: ini_set('pcre.backtrack_limit',PHP_INT_MAX);

[编辑] 我相信这个设置与pcre的强大处理能力有关,这就是为什么我建议您检查您的正则表达式,尝试使其更轻(将其拆分为多个正则表达式,在数据上添加更多迭代等)


"这可能是你的问题,因为你的正则表达式可能会导致内存泄漏。" -- 我认为这个变更日志条目指的是一个特定的 bug 已经得到解决。使用太多回溯的正确正则表达式可能会触发此错误。 - Rich

0
这个 ini_set("pcre.backtrack_limit", "5000000"); 对我很有效。 我把它放在我的mpdf页面的开始阶段,仅用了1分04秒就生成了我276页的文档。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接