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

时间:2010-08-25 13:58:26

标签: php dom domdocument xml-namespaces

这听起来很容易回答,但我无法让它发挥作用。我正在运行PHP 5.2.6。

我有一个DOM元素(根元素),当我转到$ element-> saveXML()时,它会输出一个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值以及如何读取它?

到目前为止,我能够做到这一点的唯一实用方法是完全破解 - 通过使用saveXML()将其保存为字符串,然后使用正则表达式读取它。

编辑:

这可能是使用loadHTML()而不是loadXML()加载XML然后使用saveXML()打印出来的一种特性。当你这样做时,似乎由于某种原因,saveXML添加了一个xmlns属性,即使没有办法使用DOM方法检测到这个xmlns值是文档的一部分。我想这意味着如果我有办法检测传入的文件是否已经使用loadHTML()加载,那么我可以用不同的方式解决这个问题。

3 个答案:

答案 0 :(得分:5)

Like edorian already showed,当标记加载loadXML时,获取命名空间的工作正常。但你是对的,这对于装有loadHTML的Markup来说不适用:

$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 uses a different module to parse HTML和DOMDocument本身将具有不同的nodeType,因为您只需通过执行验证

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

13为XML_HTML_DOCUMENT_NODE

答案 1 :(得分: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"

希望这就是你所要求的:)

答案 2 :(得分: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'),但我不能100%确定它是否有用......