2010-08-25 9 views
6

Sembra una domanda facile a cui rispondere ma non sono riuscito a farlo funzionare. Sto eseguendo PHP 5.2.6.Come posso trovare lo spazio dei nomi di un elemento in PHP DOM?

Ho un elemento DOM (l'elemento radice), che, quando vado a $ elemento-> saveXML(), emette un attributo xmlns:

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

Tuttavia, non riesco a trovare un modo di programmazione entro PHP per vedere quel namespace. Voglio essere in grado di verificare se esiste e a cosa è impostato.

Il controllo $document->documentElement->namespaceURI sarebbe la risposta ovvia ma è vuoto (non sono mai stato in grado di farlo essere non vuoto). Cosa sta generando quel valore xmlns nell'output e come posso leggerlo?

L'unico modo pratico in cui sono stato in grado di farlo fino ad ora è un hack completo: salvandolo come XML in una stringa utilizzando saveXML() e leggendolo attraverso le espressioni regolari.

Edit:

questo può essere una peculiarità di carico XML nell'uso loadHTML() piuttosto che loadXML() e quindi stamparlo utilizzando saveXML(). Quando lo fai, sembra che per qualche ragione saveXML aggiunge un attributo xmlns anche se non c'è modo di rilevare che questo valore xmlns fa parte del documento usando i metodi DOM. Il che, credo, significa che se avessi un modo per scoprire se il documento passato fosse stato caricato con loadHTML(), avrei potuto risolvere questo problema in un modo diverso.

risposta

5

Like edorian already showed, ottenere lo spazio dei nomi funziona correttamente quando il Markup è caricato con loadXML. Ma hai ragione che questo non funzionerà per Markup caricato con 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); 

produrrà risultati vuoti.Ma è possibile utilizzare XPath

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

e per il corpo

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

o con nodo di contesto

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

mia ipotesi ignorante è che internamente, di un documento HTML è diverso da un vera Documento. Internamente libxml uses a different module to parse HTML e il DOMDocument stesso sarà di un nodeType diverso, come si può semplicemente verificare facendo

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

con 13 essendo un XML_HTML_DOCUMENT_NODE.

+0

molto bello e dettagliato, non conoscevo i nodeTypes a seconda del metodo di analisi ma ha senso – edorian

+0

Grazie per il suggerimento sui nodetypes e la possibilità di usare xpath - risolve molti dei miei problemi! – thomasrutter

3

con PHP 5.2.6 ho trovato 2 modi per questo:

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

stampe

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

questo è speranza che cosa avete chiesto :)

+0

Grazie per la risposta - non risolve il mio problema ma mi dà la brutta impressione che sembra essere qualcosa di peculiare dei documenti caricati da loadHTML() piuttosto che loadXML() perché, in effetti, il tuo esempio funziona con loadXML(). Sembra che loadHTML crei documenti con uno "spazio dei nomi invisibile" che non può essere letto usando i metodi DOM ma che appare quando si salvaXML(). – thomasrutter

+0

Non sono sicuro di poterti seguire al 100% ma caricare qualcosa con loadHtml e salvarlo di nuovo tramite saveXml non aggiunge un xmlns per me. Aggiunge/preserva semplicemente un doctype dall'html. Forse se riesci a fornire un piccolo script di riproduzione insieme all'output che desideri, posso scavare più in profondità – edorian

+0

Interessante - a volte lo fa e talvolta no. Se il tuo documento HTML di input ha un DOCTYPE XHTML, lo fa. Lo farà per questo input: thomasrutter

1

Beh, si può fare quindi con una funzione come questa:

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

Quindi, si alimentano un DomEelement, e poi si trova tutti gli spazi dei nomi correlati:

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

Stampa in:

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

Nota che DomDocument eliminerà automaticamente tutti gli spazi dei nomi inutilizzati ...

Per quanto riguarda il motivo per cui $dom->documentElement->namespaceURI è sempre null, è perché l'elemento del documento non ha uno spazio dei nomi. L'attributo xmlns fornisce uno spazio dei nomi predefinito per il documento, ma non fornisce il tag html con uno spazio dei nomi (ai fini dell'interazione DOM). Puoi provare a fare un $dom->documentElement->removeAttribute('xmlns'), ma non sono sicuro al 100% se funzionerà ...

Problemi correlati