改进此正则表达式以防止preg_replace引发PREG_BACKTRACK_LIMIT_ERROR

时间:2019-02-28 22:01:47

标签: php regex

我想从HTML页面中删除所有带有文字foobar的scipt标记。 所以我想出了这句话:

$content = preg_replace('#<script((?!foo|bar).)*?</script>#is', '', $content);
echo "Last error: " + preg_last_error();

这适用于较小的页面。但是现在我有一个包含30个大脚本标签的页面,它不起作用。 我得到的错误是:PREG_BACKTRACK_LIMIT_ERROR

因此,我认为我需要改进正则表达式以防止出现此错误,因为此语句有效:

$content = preg_replace('#<script.*?</script>#is', '', $content); 

但是此语句将删除所有脚本标记,而我想保留其中的一部分。

有一些关于增加pcre.backtrack_limit的解决方案,但是我不想走那条路。恕我直言,应该有一个更好的解决方案。

问题是我不知道如何解决此问题,因为就我所知,正则表达式存在问题。

您能否指导我使正则表达式更好,以免发生此错误?

1 个答案:

答案 0 :(得分:0)

在此我强烈建议使用正则表达式,而是改用DOM parsing,这在这种情况下通常更合适:

$doc = new \DOMDocument();
$doc->loadHTML($html, LIBXML_HTML_NODEFDTD);

$xpath = new \DOMXPath($doc);
foreach ($xpath->query('//script[contains(text(), \'foo\') or contains(text(), \'bar\')]') as $script_tag) {
  $script_tag->parentNode->removeChild($script_tag);
}

echo $doc->saveHTML();

如果您有更多的单词,则可以改用数组构建xpath查询:

$blacklist = ['foo', 'bar', 'apple', 'cold'];

$query = '//script[' . join(' or ', array_map(function($banword) { 
  return "contains(text(), '$banword')"; 
}, $blacklist)) . ']';

foreach ($xpath->query($query) as $script_tag) {
  $script_tag->parentNode->removeChild($script_tag);
}

演示:https://3v4l.org/dHGDt