如何在PHP DOM中找到元素的命名空间?

7
这似乎是一个很容易回答的问题,但我一直无法让它工作。我正在运行PHP 5.2.6。
当我使用$element->saveXML()时,我有一个DOM元素(根元素),它会输出一个xmlns属性:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
...

然而,我无法在PHP程序中找到任何方法来查看该命名空间。我想要能够检查它是否存在以及它的设置是什么。
检查$document->documentElement->namespaceURI似乎是显而易见的答案,但那是空的(我从来没有能够使它非空)。是什么生成了输出中的xmlns值,我该如何读取它?
到目前为止,我唯一能够做到这一点的实用方法是一个完全的hack - 使用saveXML()将其保存为XML字符串,然后使用正则表达式读取其中内容。
编辑:
这可能是使用loadHTML()加载XML而不是loadXML(),然后使用saveXML()打印出来的特殊情况。当你这样做时,似乎由于某种原因,saveXML添加了一个xmlns属性,即使使用DOM方法也无法检测到此xmlns值是文档的一部分。这意味着如果我有一种检测传入文档是否已使用loadHTML()加载的方法,那么我可以用不同的方式解决这个问题。
3个回答

5

就像edorian已经展示的那样, 当使用loadXML加载标记时,获取命名空间是有效的。但是你说得对,当使用loadHTML加载标记时,这种方法不起作用:

$html = <<< XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:m="foo" lang="en">
    <body xmlns="foo">Bar</body>
</html>
XML;

$dom = new DOMDocument;
$dom->loadHTML($html);

var_dump($dom->documentElement->getAttribute("xmlns"));
var_dump($dom->documentElement->lookupNamespaceURI(NULL));
var_dump($dom->documentElement->namespaceURI);

将产生空结果。但是您可以使用XPath

$xp = new DOMXPath($dom);
echo $xp->evaluate('string(@xmlns)');
// http://www.w3.org/1999/xhtml;

并且用于正文

echo $xp->evaluate('string(body/@xmlns)'); // foo

或使用上下文节点
$body = $dom->documentElement->childNodes->item(0);
echo $xp->evaluate('string(@xmlns)', $body);
// foo

我不是专业人士,但我认为HTML文档在内部与一个“真实”的文档不同。在内部libxml使用不同的模块解析HTML,而DOMDocument本身将具有不同的节点类型,您可以通过以下方式进行验证:

var_dump($dom->nodeType); // 13 with loadHTML, 9 with loadXml

其中13是XML_HTML_DOCUMENT_NODE


非常好而且详细,我之前不知道节点类型取决于解析方法,但这很有道理。 - edorian
谢谢你提供有关节点类型和使用XPath的提示 - 解决了我很多问题! - thomasrutter

3

在PHP 5.2.6中,我发现有两种方法可以实现此操作:

<?php
$xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?'.
       '><html xmlns="http://www.w3.org/1999/xhtml" lang="en"></html>';
$x = DomDocument::loadXml($xml);
var_dump($x->documentElement->getAttribute("xmlns"));
var_dump($x->documentElement->lookupNamespaceURI(NULL));

打印

string(28) "http://www.w3.org/1999/xhtml"
string(28) "http://www.w3.org/1999/xhtml"

Hope thats what you asked for :)


谢谢您的回答 - 它没有解决我的问题,但是提示我似乎是从loadHTML()加载的文档中特有的问题,而不是loadXML(),因为确实,您的示例可以使用loadXML()工作。看起来loadHTML创建了具有“不可见命名空间”的文档,无法使用DOM方法读取,但在保存XML时会出现。 - thomasrutter
我不确定我能100%地理解你的意思,但是使用loadHtml加载某些内容并通过saveXml重新保存它并没有为我添加xmlns。它只是从html中添加/保留了doctype。也许如果你能提供一个小的重现脚本以及你想要的输出,我可以深入挖掘。 - edorian
有趣的是,它有时会这样做,有时不会。如果您的输入HTML文档具有XHTML DOCTYPE,则会这样做。它将为此输入执行此操作:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - thomasrutter
我不知道你如何在DOM中检测到它。 - thomasrutter

1
你可以使用以下这个函数来实现这个功能:

function getNamespaces(DomNode $node, $recurse = false) {
    $namespaces = array();
    if ($node->namespaceURI) {
        $namespaces[] = $node->namespaceURI;
    }
    if ($node instanceof DomElement && $node->hasAttribute('xmlns')) {
        $namespaces[] = $xmlns = $node->getAttribute('xmlns');
        foreach ($node->attributes as $attr) {
            if ($attr->namespaceURI == $xmlns) {
                $namespaces[] = $attr->value;
                }
        }
    }
    if ($recurse && $node instanceof DomElement) {
        foreach ($node->childNodes as $child) {
            $namespaces = array_merge($namespaces, getNamespaces($child, vtrue));
        }
    }
    return array_unique($namespaces);
}

所以,您将其提供给一个DomEelement,然后它会查找所有相关的命名空间:

$xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <html xmlns="http://www.w3.org/1999/xhtml" 
         lang="en" 
         xmlns:foo="http://example.com/bar">
           <body>
                <h1>foo</h1>
                <foo:h2>bar</foo:h2>
           </body>
 </html>';
var_dump(getNamespaces($dom->documentElement, true));

输出:

array(2) {
  [0]=>
  string(28) "http://www.w3.org/1999/xhtml"
  [3]=>
  string(22) "http://example.com/bar"
}

请注意,DomDocument将自动删除所有未使用的命名空间...
至于为什么$dom->documentElement->namespaceURI始终为null,这是因为文档元素没有命名空间。xmlns属性为文档提供了默认命名空间,但它并不赋予html标签命名空间(用于DOM交互)。您可以尝试执行$dom->documentElement->removeAttribute('xmlns'),但我不能百分之百确定它是否有效...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接