php 7.4 字符串高亮需定位+包裹+转义:先用 preg_match_all(带 preg_offset_capture 和 u 修饰符)安全获取所有匹配位置,关键词用 preg_quote 转义;再倒序 substr_replace 包裹 标签;原始文本和关键词均需 htmlspecialchars 转义,确保 xss 安全与 utf-8 正确性。

PHP 7.4 中实现字符串搜索并高亮匹配结果,核心是“定位 + 包裹 + 转义”,不是简单替换。关键要避免 XSS、保留原始结构、正确处理多次匹配和重叠边界。
先安全定位所有匹配位置
直接用 preg_match_all 获取全部起始偏移和长度,比循环调用 strpos 更可靠(后者易漏重叠、难控制边界):
- 启用 PREG_OFFSET_CAPTURE 标志,一次拿到所有匹配的文本和字节位置
- 对用户输入的关键词做 preg_quote($keyword, '/') 转义,防止正则元字符被误执行
- 中文或 emoji 必须加 u 修饰符,否则位置计算会错乱
按位置倒序替换,避免偏移错位
从字符串末尾开始高亮,能保证前面替换不影响后续位置索引:
- 把 preg_match_all 结果按 offset 降序排列
- 用 substr_replace 逐个包裹,例如:
substr_replace($text, '<mark>$match</mark>', $offset, $length) - 不推荐 str_replace —— 它无法区分“abc”在 “abcabc” 中是第一次还是第二次出现,且无法控制 HTML 转义
输出前必须做上下文转义
高亮标签是 HTML,但原文本可能含 、<code>" 等,必须分层处理:
- 先对原始文本整体调用 htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'),只转义非高亮部分
- 再把
<mark>...</mark>中的内容单独用 htmlspecialchars 处理(不能跳过) - 最终拼接时,确保
<mark></mark>标签本身不被二次转义——它应作为纯 HTML 输出
完整轻量示例(兼容 PHP 7.4)
以下函数可直接复用,已包含错误防护和 UTF-8 安全:
function highlightText(string $text, string $keyword): string {if ($keyword === '' || $text === '') return htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
$quoted = preg_quote($keyword, '/');
$escapedText = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
$matches = [];
if (preg_match_all('/' . $quoted . '/iu', $text, $matches, PREG_OFFSET_CAPTURE)) {
$result = $escapedText;
$offsets = array_column($matches[0], 1);
rsort($offsets); // 倒序处理
foreach ($offsets as $offset) {
$len = strlen($keyword);
$highlighted = '' . htmlspecialchars($keyword, ENT_NOQUOTES, 'UTF-8') . '';
$result = substr_replace($result, $highlighted, $offset, $len);
}
return $result;
}
return $escapedText;
}
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











