
本文介绍使用 PHP 的 DOMDocument 和 DOMXPath 安全、可靠地过滤 HTML 属性,仅保留 id 和 class 等指定属性,避免正则表达式解析 HTML 带来的不可靠性与维护难题。
本文介绍使用 php 的 domdocument 和 domxpath 安全、可靠地过滤 html 属性,仅保留 `id` 和 `class` 等指定属性,避免正则表达式解析 html 带来的不可靠性与维护难题。
在 Web 开发中,常需对用户提交或第三方来源的 HTML 进行净化处理,例如仅允许 <div>、<a> 等标签保留 id 和 class 属性,而移除 href、title、style、onclick 等潜在风险或非必要属性。虽然初看可用正则表达式(如 preg_replace)快速匹配,但HTML 是上下文敏感的嵌套结构,正则无法正确处理嵌套、引号转义、大小写混用、自闭合标签、无值布尔属性(如 disabled)等真实场景,极易导致解析错误、属性遗漏甚至 XSS 漏洞。
因此,推荐采用基于标准 HTML 解析器的方案:PHP 内置的 DOMDocument 类配合 DOMXPath 或原生 DOM 遍历。它能准确识别文档结构、属性命名与值边界,且天然支持大小写不敏感、多种引号格式及 HTML5 自定义属性。
以下为推荐实现(使用 getElementsByTagName('*'),简洁高效,无需 XPath):
$html = '<div id="one-id" class="someClassName">Some text <a href="#" title="Words" id="linkId" class="classLink" data-foo="bar">link</a> with only the class and id attributes.</div>';
$dom = new DOMDocument();
// 关键:禁用自动添加 <html><body> 包裹,避免污染输出
$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
// 遍历所有元素节点(* 表示任意标签)
foreach ($dom->getElementsByTagName('*') as $node) {
// 逆序遍历属性(因 removeAttribute() 会改变 attributes 集合长度)
for ($i = $node->attributes->length - 1; $i >= 0; $i--) {
$attr = $node->attributes->item($i);
// 白名单检查:只保留 'id' 和 'class'
if (!in_array($attr->name, ['id', 'class'], true)) {
$node->removeAttribute($attr->name);
}
}
}
// 输出净化后的 HTML(不含 <!DOCTYPE> 和 <html><body>)
echo $dom->saveHTML();✅ 输出结果:
立即学习“前端免费学习笔记(深入)”;
<div id="one-id" class="someClassName">Some text <a id="linkId" class="classLink">link</a> with only the class and id attributes.</div>
注意事项:
- LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD 是关键选项:防止 loadHTML() 自动补全 <html>、<body> 和 <!DOCTYPE>,确保输出纯净;
- 必须逆序遍历属性($i--),否则删除属性后索引错位,导致部分属性被跳过;
- in_array(..., true) 启用严格比较,避免 id 与 data-id 等名称误匹配;
- 如需扩展白名单(如增加 data-* 属性),可改用正则判断:!preg_match('/^(id|class|data-.+)$/i', $attr->name);
- 若需保留空格/换行格式,可在 saveHTML() 前调用 $dom->preserveWhiteSpace = true;,但通常建议后续用 HTML Tidy 或 minify 工具统一处理。
总结:面对 HTML 属性过滤任务,应坚决放弃正则表达式方案——它看似简短,实则脆弱、难调试、不可扩展。而 DOMDocument 方案虽代码略长,却具备语义准确性、可维护性与安全性,是生产环境的标准实践。



















