2010-08-25 12 views
6

To brzmi jak dość łatwe pytanie, ale nie udało mi się go uruchomić. Używam PHP 5.2.6.Jak mogę znaleźć przestrzeń nazw elementu w PHP DOM?

Mam elementu DOM (element główny), który, gdy idę do $ element-> saveXML() wyprowadza atrybut xmlns:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<html xmlns="http://www.w3.org/1999/xhtml" lang="en"> 
... 

Jednak nie mogę znaleźć żadnego sposobu programowo zasięgu PHP, aby zobaczyć tę przestrzeń nazw. Chcę móc sprawdzić, czy istnieje i do czego służy.

Sprawdzanie $document->documentElement->namespaceURI byłoby oczywistą odpowiedzią, ale jest ona pusta (nigdy nie byłem w stanie sprawić, że nie będzie pusta). Co generuje ta wartość xmlns na wyjściu i jak mogę ją odczytać?

Jedyny praktyczny sposób, w jaki udało mi się to zrobić do tej pory, to kompletny hack - zapisując go jako XML do napisu przy użyciu saveXML(), a następnie odczytując go za pomocą wyrażeń regularnych.

Edit:

To może być osobliwością ładowania XML w użyciu loadHTML() zamiast loadXML(), a następnie drukowanie z wykorzystaniem saveXML(). Kiedy to zrobisz, wydaje się, że z jakiegoś powodu saveXML dodaje atrybut xmlns, nawet jeśli nie ma sposobu, aby wykryć, że ta wartość xmlns jest częścią dokumentu przy użyciu metod DOM. Który, jak sądzę, oznacza, że ​​gdybym miał sposób wykrycia, czy dokument przekazany został załadowany przy użyciu metody loadHTML(), to mógłbym rozwiązać to w inny sposób.

Odpowiedz

5

Like edorian already showed, uzyskanie przestrzeni nazw działa dobrze, gdy znacznik jest załadowany loadXML. Ale masz rację, że to przyzwyczajenie praca dla Markup załadowany 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); 

będzie wynik pusty.Ale można użyć XPath

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

i dla ciała

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

lub z węzła kontekstowego

$body = $dom->documentElement->childNodes->item(0); 
echo $xp->evaluate('string(@xmlns)', $body); 
// foo 

Moja niewykształcony założeniem jest, że wewnętrznie, dokument HTML różni się od rzeczywistym Dokument. Wewnętrznie libxml uses a different module to parse HTML a sam DOMDocument będzie innego NODETYPE, jak można po prostu sprawdzić, wykonując

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

z 13 będąc XML_HTML_DOCUMENT_NODE.

+0

bardzo ładne i szczegółowe, nie wiedział o nodeTypes w zależności od metody parsowania, ale ma sens – edorian

+0

Dzięki za podpowiedź o nodetypes i możliwość korzystania z xpath - rozwiązuje wiele moich problemów! – thomasrutter

3

W PHP 5.2.6 Znalazłem 2 sposoby to:

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

drukuje

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

Nadzieja to co prosiłeś :)

+0

Dziękuję za odpowiedź - nie rozwiązuje ona mojego problemu, ale podpowiada mi, że wydaje się być czymś osobliwym dla dokumentów załadowanych z loadHTML() niż loadXML(), ponieważ w rzeczy samej, twój przykład działa z loadXML(). Wygląda na to, że loadHTML tworzy dokumenty z "niewidoczną przestrzenią nazw", której nie można odczytać przy użyciu metod DOM, ale która pojawia się, gdy saveXX(). – thomasrutter

+0

Nie jestem pewien, czy mogę za tobą podążać w 100%, ale ładowanie czegoś za pomocą loadHtml i ponowne zapisywanie go przez saveXml nie dodaje mi xmlns. To po prostu dodaje/zachowuje doctype z html. Może, jeśli możesz podać trochę reprodukcyjnego skryptu obok wyjścia, które chcesz, mogę głębiej zagłębić – edorian

+0

Ciekawe - czasami tak się dzieje, a czasami nie. Jeśli twój wejściowy dokument HTML ma DOCTYPE XHTML, robi to. Zrobi to dla tego wejścia: thomasrutter

1

Cóż, można zrobić więc z funkcją podobną do tej:

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

więc karmić jej DomEelement, a następnie wyszukuje wszystkie powiązane przestrzenie nazw:

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

wypisuje:

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

pamiętać, że domDocument automatycznie rozebrać wszystkie niewykorzystane przestrzenie nazw ...

Co do tego, dlaczego $dom->documentElement->namespaceURI jest zawsze , to dlatego, że element dokumentu nie ma przestrzeni nazw. Atrybut xmlns zapewnia domyślny obszar nazw dla dokumentu, ale nie nadaje znacznikowi html przestrzeni nazw (dla celów interakcji DOM). Możesz spróbować zrobić $dom->documentElement->removeAttribute('xmlns'), ale nie jestem w 100% pewien, czy to zadziała ...