2010-08-25 5 views
6

Esto suena como una pregunta bastante fácil de responder, pero no he podido hacer que funcione. Estoy ejecutando PHP 5.2.6.¿Cómo puedo averiguar el espacio de nombre de un elemento en PHP DOM?

que tienen un elemento DOM (el elemento raíz) que, cuando voy a $ element-> saveXML(), que da salida a un atributo xmlns:

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

Sin embargo, no puedo encontrar ninguna manera mediante programación dentro de PHP para ver ese espacio de nombres. Quiero poder verificar si existe y en qué está configurado.

Verificar $document->documentElement->namespaceURI sería la respuesta obvia, pero está vacía (nunca he podido conseguir que no esté vacía). ¿Qué está generando ese valor xmlns en la salida y cómo puedo leerlo?

La única forma práctica que he podido hacer esto hasta ahora es un truco completo, guardándolo como XML en una cadena usando saveXML() y luego leyendo a través de expresiones regulares.

Editar:

Esto puede ser una peculiaridad de XML de carga en el uso de loadHTML() en lugar de loadXML() y luego imprimirlo usando saveXML(). Cuando lo hace, parece que, por algún motivo, saveXML agrega un atributo xmlns, aunque no hay forma de detectar que este valor xmlns forma parte del documento con los métodos DOM. Lo que supongo significa que si tuviera una forma de detectar si el documento pasado se había cargado usando loadHTML() entonces podría resolverlo de otra manera.

Respuesta

5

Like edorian already showed, obteniendo el espacio de nombres funciona bien cuando el Margen está cargado con loadXML. Pero tienes razón en que esto no funcionará para el marcado de carga 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); 

producirá resultados vacíos.Pero se puede utilizar XPath

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

y para el cuerpo

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

o nodo de contexto

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

Mi suposición sin educación es que internamente, un documento HTML es diferente de un verdadera Documento. Internamente libxml uses a different module to parse HTML y el propio DOMDocument será de un nodeType diferente, ya que simplemente puede verificar haciendo

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

con 13 siendo un XML_HTML_DOCUMENT_NODE.

+0

muy bonito y detallado, no sabía acerca de los nodeTypes según el método de análisis, pero tiene sentido – edorian

+0

Gracias por la pista sobre los tipos de nodo y la capacidad de usar xpath: ¡resuelve muchos de mis problemas! – thomasrutter

3

Con PHP 5.2.6 que he encontrado 2 maneras de esto:

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

impresiones

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

esa es la esperanza lo que solicitó :)

+0

Gracias por su respuesta, no resuelve mi problema pero me indica que parece ser algo peculiar de los documentos cargados desde loadHTML() en lugar de loadXML() porque de hecho, su ejemplo funciona con loadXML(). Parece que loadHTML crea documentos con un "espacio de nombre invisible" que no se puede leer con los métodos DOM pero que aparece cuando se guarda el archivo XML(). – thomasrutter

+0

No estoy seguro de que pueda seguirte al 100%, pero cargar algo con loadHtml y volver a guardarlo a través de saveXml no agrega un xmlns para mí. Simplemente agrega/conserva un doctype del html. Tal vez si puede proporcionar un pequeño script de reproducción junto con el resultado que desea puedo profundizar más – edorian

+0

Interesante - a veces lo hace y otras veces no. Si su documento HTML de entrada tiene un XHTML DOCTYPE, lo hace. Lo hará por esta entrada: thomasrutter

1

Bueno, puede hacerlo entonces con una función como esta:

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

Por lo tanto, le da de comer una DomEelement, y luego se encuentran todos los espacios de nombres relacionados:

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

Prints salida:

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

nota que DomDocument se tira automáticamente todos los espacios de nombres utilizados ...

En cuanto a por qué $dom->documentElement->namespaceURI es siempre null, es porque el elemento del documento no tiene un espacio de nombre. El atributo xmlns proporciona un espacio de nombre predeterminado para el documento, pero no otorga la etiqueta html con un espacio de nombre (para fines de interacción DOM). Puede intentar hacer un $dom->documentElement->removeAttribute('xmlns'), pero no estoy 100% seguro de si funcionará ...

Cuestiones relacionadas