Как я могу узнать пространство имен элемента в PHP DOM? - PullRequest
6 голосов
/ 25 августа 2010

Звучит как довольно простой вопрос, но я не смог заставить его работать.Я использую 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 в выводе и как я могу его прочитать?

Единственный практический способ, которым я смог сделать это до сих пор, - это полный взлом - сохранение его в виде XML в строку с использованиемзатем saveXML (), читая его с помощью регулярных выражений.

Редактировать:

Это может быть особенностью загрузки XML с использованием loadHTML () вместо loadXML () и последующей печати с использованием saveXML().Когда вы делаете это, кажется, что по какой-то причине saveXML добавляет атрибут xmlns, даже если нет способа обнаружить, что это значение xmlns является частью документа с использованием методов DOM.То, что я предполагаю, означает, что если бы у меня был способ определить, был ли переданный документ загружен с использованием loadHTML (), я мог бы решить эту проблему другим способом.

Ответы [ 3 ]

5 голосов
/ 25 августа 2010

Как 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.

3 голосов
/ 25 августа 2010

С PHP 5.2.6 я нашел 2 пути к этому:

<?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"

Надеюсь, это то, что вы просили:)

1 голос
/ 25 августа 2010

Ну, вы можете сделать это с помощью такой функции:

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));

Печатаетout:

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%, будет ли он работать ...

...