Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签
在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。
当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img>
为什么会这样?
当你使用:
Dom\HTML_NO_DEFAULT_NS
文档会变成 无命名空间模式,此时内部更接近 XML 风格的 DOM 处理逻辑。
一旦你对 DOM 做了修改(哪怕只是 removeAttribute()),序列化时:
$dom->saveHTML();
void 元素(如 img、br、hr、input)就可能被当成“普通元素”输出为:
<img src="1.png"></img>
而不是:
<img src="1.png">
HTML 规范里:
img 是 void element(不能有结束标签)
但在无 namespace 模式下,底层不会再按 HTML5 语义特殊处理 void 元素,而是统一按“普通元素”序列化。
只要发生“结构变更”,内部就会丢失“void element 标记”。
但如果不使用它,又会有另外一个副作用:XPath 不加 namespace 前缀,查询结果为空
为什么会这样?
当你 不使用:
Dom\HTML_NO_DEFAULT_NS
HTML 会自动带默认 namespace:
http://www.w3.org/1999/xhtml
这意味着:
//p
实际上等价于:
//{no-namespace}p而文档里的 <p> 实际是:
{xhtml-namespace}p所以查询结果为空。
正确做法(官方推荐方式)
注册 namespace,然后使用前缀:
$dom = Dom\HTMLDocument::createFromString($html);
$xpath = new Dom\XPath($dom);
$xpath->registerNamespace('x', 'http://www.w3.org/1999/xhtml');
$nodes = $xpath->query('//x:p');
这个两个副作用本质上是同一个 bug 的不同触发方式。这是 PHP Dom\HTMLDocument 目前一个已知的 双重 bug:
问题总结
| 情况 | 结果 |
|---|---|
不用 Dom\HTML_NO_DEFAULT_NS,直接操作 DOM(如 removeAttribute) | <img src="0.png"></img> ✕ |
| XPath 不加 namespace 前缀 | 查询结果为空 ✕ |
这两个 bug 都已在 PHP 官方 GitHub 追踪: