2010-08-25 7 views
6

Это звучит довольно простой вопрос, но я не смог заставить его работать. Я запускаю PHP 5.2.6.Как узнать пространство имен элемента в PHP DOM?

У меня есть 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(), а затем чтение с помощью регулярных выражений.

Edit:

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

ответ

5

Like edorian already showed, получение пространства имен работает нормально, когда разметка загружена 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 uses a different module to parse HTML и сам DOMDocument будет другого NODETYPE, как вы можете просто проверить, выполнив

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

с 13 будучи XML_HTML_DOCUMENT_NODE.

+0

очень красивый и подробный, не знал о nodeTypes в зависимости от метода синтаксического анализа, но имеет смысл – edorian

+0

Спасибо за подсказку о nodetypes и возможность использовать xpath - решает много моих проблем! – thomasrutter

3

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

Надежда вот что вы просили :)

+0

Спасибо за ваш ответ - он не решает мою проблему, но подсказывает мне, что это похоже на что-то особенное для документов, загружаемых с loadHTML(), а не loadXML(), потому что действительно ваш пример работает с loadXML(). Похоже, loadHTML создает документы с «невидимым пространством имен», которые невозможно прочитать с помощью методов DOM, но которые появляются при сохраненииXML(). – thomasrutter

+0

Я не уверен, что могу следить за вами на 100%, но загружая что-то с помощью loadHtml и сохраняя его с помощью saveXml, я не добавляю xmlns для меня. Он просто добавляет/сохраняет doctype из html. Может быть, если вы можете предоставить небольшой скрипт воспроизведения вместе с выходом, который вы хотите, я могу углубиться глубже. – edorian

+0

Интересно - иногда это происходит, а иногда нет. Если ваш HTML-документ ввода имеет XHTML DOCTYPE, он делает это. Он будет делать это для этого ввода: 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'), но я не уверен на 100%, если он будет работать ...