真正能上线的高亮方案必须用 preg_replace + 严格顺序控制 + 先转义再替换:关键词需 preg_quote 转义,修饰符为 /iu,替换用 '$0';多词按长度降序处理;必须先 htmlspecialchars 再高亮;中文词不加 \b;空白符需归一化;html 内容须 dom 解析后仅对文本节点操作。

直接用 str_replace 做高亮,90% 的场景会出问题:XSS 漏洞、大小写错乱、HTML 标签被污染、长词被短词提前截断。真正能上线的方案,必须用 preg_replace + 严格顺序控制 + 先转义再替换。
preg_replace 是唯一靠谱的高亮入口
关键词可能含 .、+、[ 等正则元字符,不转义就崩;用户搜 php[8],preg_quote($kw, '/') 是硬性要求。修饰符必须带 i(不区分大小写)和 u(UTF-8 支持),写成 '/'.preg_quote($kw, '/').'/iu'。替换内容用 '<mark>$0</mark>',别用 $1——没捕获组时会空匹配或报错。
常见错误现象:
- 搜
api,结果把<span></span>里的api也高亮了 - 搜
PHP,小写php内容没变色 - 关键词含括号,页面直接 500 报错
多关键词必须按长度降序处理
搜 “php phpstorm”,如果先处理 php,原文 phpstorm 就变成 <mark>php</mark>storm,后面再处理 phpstorm 就找不到原串了。正确做法是:
- 用
explode(' ', $keywords)拆词 - 过滤空项:
array_filter(array_map('trim', $words)) - 按字符串长度降序排序:
usort($words, function($a, $b) { return strlen($b) - strlen($a); }); - 再逐个进
preg_replace
中文词不用加 \b——它对汉字无效,强行加反而漏匹配。需要“整词”效果?得靠 (? 和 <code>(?!\w),但多数业务直接去掉更稳。
必须先 htmlspecialchars,再 preg_replace
用户搜 <script>alert(1)</script>,如果你先 preg_replace 再 htmlspecialchars,<mark></mark> 会被当作文本输出,而脚本仍保留在 $0 里执行——高亮功能秒变 XSS 入口。
正确顺序只有这一种:
- 若原文不含 HTML:
$safe_text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');,再对$safe_text做高亮 - 若原文含 HTML(如富文本):
DOMDocument解析,只对TEXT_NODE节点内容高亮,跳过标签属性和标签名 - 绝对不要用
htmlentities——它会把中文转成中文,影响阅读
中文关键词要预处理空白符
用户可能输全角空格、换行、制表符,PHP 的 \s 不覆盖全角空白,直接拿去匹配基本失败。必须归一化:
- 先把常见干扰符换成空格:
str_replace([' ', "\r", "\n", "\t"], ' ', $keyword) - 再合并连续空格:
preg_replace('/ +/', ' ', $keyword) - 最后
trim()两端
这个步骤不能省,否则搜“人工智能 开源”,中间是全角空格,explode(' ', ...) 就拆不出两个词。
最易被忽略的是:高亮后的 HTML 必须由前端 CSS 控制样式,<mark></mark> 标签本身无默认样式;另外,如果搜索字段来自数据库且已含转义实体(如 &),得在高亮前还原,否则 & 会被当成字面量匹配不上。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











