PHP关键词高亮需先preg_quote转义防正则崩坏,关键词数组按长度降序排序以解决长短词包含问题,htmlspecialchars预转义防XSS,中文关键词需归一化空白,用负向断言防止重复嵌套。

要在PHP中对搜索结果里的关键词做高亮,又不想写一堆if-else嵌套、避免重复包裹、防止XSS注入、还能正确处理“PHP”和“PHP框架”这种长短词共存的情况,就得靠三元运算符配合正则与预处理逻辑来精准控制替换时机和范围。
先转义再高亮:防止正则崩掉的关键一步
获取用户提交的关键词后,立即用 preg_quote($keyword, '/') 转义所有正则特殊字符。比如用户搜“a+b[c]”,不转义就会被当作正则模式执行,直接报错或匹配失控。
这一步不能跳过,否则遇到点号、星号、括号等符号,preg_replace 会抛出警告甚至中断脚本。
转义后的关键词可安全拼入正则表达式:'/'. preg_quote($kw, '/') . '/iu'。
立即学习“PHP免费学习笔记(深入)”;
多词高亮必须排序:长词优先,否则“PHP”会吃掉“PHP框架”
第一步:把关键词数组按字符串长度降序排列——用 usort($keywords, function($a, $b) { return strlen($b) - strlen($a); });。
第二步:遍历排序后的数组,对每个词单独执行一次 preg_replace 替换。
不排序的后果很直接:先处理“PHP”,全文所有“PHP”都被加了标签;再处理“PHP框架”时,已带标签的HTML文本里根本找不到连续未标记的“PHP框架”字符串,导致漏高亮。排序是解决包含关系的唯一低成本方案。
用三元运算符动态控制是否加高亮标签
方法一:在循环内用三元决定是否包裹
$highlighted = preg_match('/'. preg_quote($kw, '/') .'/iu', $text) ? preg_replace('/'. preg_quote($kw, '/') .'/iu', '<mark>$0</mark>', $text) : $text;
方法二:更实用的是在拼接HTML前做判断,避免无谓替换
$content = htmlspecialchars($raw_content, ENT_QUOTES, 'UTF-8'); // 先转义防XSS
$output = $has_keyword ? preg_replace($pattern, '<mark>$0</mark>', $content) : $content;
【注意:必须先 htmlspecialchars 再高亮】 否则用户输入 <script>alert(1)</script>,高亮后变成 <mark><script>alert(1)</script></mark>,浏览器仍会执行脚本。
中文关键词要归一化空白,否则搜不到
用户可能输入全角空格、换行、多个连续空格,而 \s 在PHP正则中不自动覆盖全角空白。
执行:$keyword = str_replace([' ', "\r", "\n"], ' ', $keyword);
再执行:$keyword = preg_replace('/\s+/', ' ', trim($keyword));
最后才对这个清洗后的 $keyword 做 preg_quote 和匹配——否则90%的中文搜索会失败。
单次高亮不重复嵌套:用负向先行断言保护已有
如果同一段文本要多次高亮(比如分页重渲染),需防止 <mark>PHP</mark> 再被套成 <mark><mark>PHP</mark></mark>。
构造模式时加上 (?) 和 (?!):
$pattern = '/(?)'. preg_quote($kw, '/') . '(?!)/iu';
这样只会匹配未被包裹的原始关键词,跳过已高亮部分,彻底杜绝嵌套。



















