2009-03-26 16 views
16

Estoy intentando depurar un objeto DOMDocument grande y complejo en php. Idealmente, estaría bien si pudiera hacer que DOMDocument salga en un formato de matriz.Depurar un objeto DOMDocument en PHP

DomDocument:

$dom = new DOMDocument(); 
$dom->loadHTML("<html><body><p>Hello World</p></body></html>"); 
var_dump($dom); //or something equivalent

Esto da salida a

DOMDocument Object ()

mientras que me gustaría que a la salida

DOMDocument: 
html 
=>body 
==>p 
===>Hello World

O algo por el estilo. ¿Por qué no hay una depuración o salida útil para esto?

Respuesta

0

Aunque no lo he probado yo mismo, consulte Zend_Dom, parte de Zend Framework. La documentación y los ejemplos para la mayoría de los componentes de Zend Framework son realmente minuciosos.

-1

Acabo de utilizar DOMDocument :: save. Es lamentable que tenga que escribir en un archivo, pero lo que sea.

+2

Si lo hizo, podría simplemente guardar HHTML en su lugar y hacer que vaya a una cadena. –

8

para un nodo DOM, sólo tiene que utilizar el siguiente:

print_r(simplexml_import_dom($entry)->asXML()); 
31

Esta respuesta es probablemente un poco tarde, pero me gusta su pregunta!

PHP no tiene nada incorporado directamente para resolver su problema, por lo que no hay volcado de XML o algo así.

Sin embargo, PHP tiene la RecursiveTreeIterator­Docs que se acerca mucho a la salida:

\-<html> 
    \-<body> 
    \-<p> 
     \-Hello World 

(. Que se verá mejor si su X (HT) Estructura ML parece más complicado)

Es utilizado con bastante simples (como la mayoría de los iteradores) con un foreach:

$tree = new RecursiveTreeIterator($iterator); 
foreach($tree as $key => $value) 
{ 
    echo $value . "\n"; 
} 

(usted puede terminar con esto dentro de una función, por lo que sólo necesita llamar a la fu nction)

Incluso esto parece simple, hay una advertencia: necesita un RecursiveIterator sobre el árbol DOMDocument. Como PHP no puede adivinar lo que necesita, necesita ser incluido en el código. Tal como está escrito, encontré la pregunta interesante (y obviamente no me has pedido el resultado XML), así que escribí un pequeño código que ofrece el iterador recursivo necesario. Así que, aquí vamos.

En primer lugar es posible que no esté familiarizado con los iteradores en PHP. Eso no es un trato para hacer uso del código que mostraré, ya que lo haré hacia atrás, sin embargo, siempre que considere ejecutar algún código por su cuenta, considere si puede o no hacer uso de las capacidades del iterador que PHP tiene ofrecer. Escribo eso porque ayuda a resolver problemas comunes y hacer que los componentes que no están realmente relacionados entre sí trabajen entre ellos. Por ejemplo, el RecursiveTreeIterator­Docs está incorporado y funcionará con cualquier cosa que alimente (e incluso puede configurarlo). Sin embargo, necesita un RecursiveIterator para operar.

Así que vamos a darle un RecursiveIterator que ofrece <tag> para DOMNodes que son etiquetas (elementos) y sólo el text si son textnodes:

class DOMRecursiveDecoratorStringAsCurrent extends RecursiveIteratorDecoratorStub 
{ 
    public function current() 
    { 
     $node = parent::current(); 
     $nodeType = $node->nodeType; 

     switch($nodeType) 
     { 
      case XML_ELEMENT_NODE: 
       return "<$node->tagName>"; 

      case XML_TEXT_NODE: 
       return $node->nodeValue; 

      default: 
       return sprintf('(%d) %s', $nodeType, $node->nodeValue); 
     } 
    } 
} 

Esta clase DOMRecursiveDecoratorStringAsCurrent (el nombre es sólo un ejemplo) hace uso de algún código abstracto en RecursiveIteratorDecoratorStub. Sin embargo, la parte más importante es la función ::current que simplemente devuelve el tagName de un DOMNode en bracketsWikipedia (<>) y el texto de los nodos de texto tal como están. Eso es lo que su salida necesita, entonces eso es todo lo que se necesita para codificar.

En realidad esto no funcionará hasta que tenga el código abstracto también, pero para visualizar el código de cómo se utiliza (la parte más interesante), vamos a ver que:

$iterator = new DOMRecursiveDecoratorStringAsCurrent($iterator); 
$tree = new RecursiveTreeIterator($iterator); 
foreach($tree as $key => $value) 
{ 
    echo $value . "\n"; 
} 

Como se hace hacia atrás, para el momento en que tenemos la salida especificada en base a la cual DOMNode se mostrará en el RecursiveTreeIterator. Bien hasta ahora, es fácil de conseguir. Pero la carne que falta está dentro del código abstracto y cómo crear un RecursiveIterator sobre todos los nodos dentro de un DOMElement. Sólo previa de todo el código de la forma en que se invoca (como está escrito antes, se puede poner esto en una función para que sea fácilmente accesible dentro de su código para fines de depuración Probablemente una función llamada xmltree_dump.):

$dom = new DOMDocument(); 
$dom->loadHTML("<html><body><p>Hello World</p></body></html>"); 
$iterator = new DOMRecursiveIterator($dom->documentElement); 
$iterator = new DOMRecursiveDecoratorStringAsCurrent($iterator); 
$tree = new RecursiveTreeIterator($iterator); 
foreach($tree as $key => $value) 
{ 
    echo $value . "\n"; 
} 

Entonces, ¿qué hacer llegamos aquí además del código ya cubierto? Primero hay un DOMRecursiveIterator - y eso es todo. El resto del código es el código estándar DOMDocument.

Así que vamos a escribir sobre DOMRecursiveIterator. Es el necesario RecursiveIterator que finalmente se necesita dentro del RecursiveTreeIterator. Obtiene decorado para que el volcado del árbol realmente imprima nombres de etiquetas entre paréntesis y texto tal cual.

Probablemente merece la pena compartir el código de ahora:

class DOMRecursiveIterator extends DOMIterator implements RecursiveIterator 
{ 
    public function hasChildren() 
    { 
     return $this->current()->hasChildNodes(); 
    } 
    public function getChildren() 
    { 
     $children = $this->current()->childNodes; 
     return new self($children); 
    } 
} 

Es una clase bastante corto con sólo dos funciones. Estoy haciendo trampa aquí ya que esta clase también se extiende desde otra clase. Pero como está escrito, esto es al revés, por lo que esta clase realmente se ocupa de la recursión: hasChildren y getChildren. Obviamente, incluso esas dos funciones no tienen mucho código, solo están mapeando la "pregunta" (hasChildren? getChildren?) En un estándar DOMNode. Si un nodo tiene hijos, bueno, diga sí o simplemente devuélvalos (y este es un iterador, devuélvalos en forma de un iterador, de ahí el new self()).

Por lo tanto, ya que es bastante corto, después de asfixia, simplemente continuar con la clase padre DOMIterator (el implements RecursiveIterator­Docs es sólo para asegurarse de que funcione):

class DOMIterator extends IteratorDecoratorStub 
{ 
    public function __construct($nodeOrNodes) 
    { 
     if ($nodeOrNodes instanceof DOMNode) 
     { 
      $nodeOrNodes = array($nodeOrNodes); 
     } 
     elseif ($nodeOrNodes instanceof DOMNodeList) 
     { 
      $nodeOrNodes = new IteratorIterator($nodeOrNodes); 
     } 
     if (is_array($nodeOrNodes)) 
     { 
      $nodeOrNodes = new ArrayIterator($nodeOrNodes); 
     } 

     if (! $nodeOrNodes instanceof Iterator) 
     { 
      throw new InvalidArgumentException('Not an array, DOMNode or DOMNodeList given.'); 
     } 

     parent::__construct($nodeOrNodes); 
    } 
} 

Este es el iterador base para DOMPHP, sólo toma un DOMNode o un DOMNodeList para repetir. Esto suena un poco superfluo, ya que DOM soporta este tipo de cosas con DOMNodeList ya, pero no es compatible con RecursiveIterator y ya sabemos que necesitamos uno para RecursiveTreeIterator para la salida.Por lo tanto, en su constructor se crea un Iterator y se pasa a la clase principal, que de nuevo es código abstracto. Claro que revelaré este código en solo un minuto. Como esto es al revés, revisemos lo que se ha hecho hasta ahora:

  • RecursiveTreeIterator para la salida de árbol.
  • DOMRecursiveDecoratorStringAsCurrent para la visualización de un DOMNode en el árbol
  • DOMRecursiveIterator y DOMIterator para iterar recursivamente sobre todos los nodos de una DOMDocument.

Esto en términos de definición como todo lo que se necesita, sin embargo, el código que llamé abstracto aún falta. Es solo un tipo de código proxy simple, delega el mismo método a otro objeto. Un patrón relacionado se llama Decorador. Sin embargo, esto es sólo el código, la primera Iterator y entonces es RecursiveIterator amigo:

abstract class IteratorDecoratorStub implements OuterIterator 
{ 
    private $iterator; 
    public function __construct(Iterator $iterator) 
    { 
     $this->iterator = $iterator; 
    } 
    public function getInnerIterator() 
    { 
     return $this->iterator; 
    } 
    public function rewind() 
    { 
     $this->iterator->rewind(); 
    } 
    public function valid() 
    { 
     return $this->iterator->valid(); 
    } 
    public function current() 
    { 
     return $this->iterator->current(); 
    } 
    public function key() 
    { 
     return $this->iterator->key(); 
    } 
    public function next() 
    { 
     $this->iterator->next(); 
    } 
} 

abstract class RecursiveIteratorDecoratorStub extends IteratorDecoratorStub implements RecursiveIterator 
{ 
    public function __construct(RecursiveIterator $iterator) 
    { 
     parent::__construct($iterator); 
    } 
    public function hasChildren() 
    { 
     return $this->getInnerIterator()->hasChildren(); 
    } 
public function getChildren() 
{ 
    return new static($this->getInnerIterator()->getChildren()); 
} 
} 

eso no es nada muy mágicamente, es simplemente bien delegar el método llama a que ha heredado objeto $iterator. Parece que repetir y bien los iteradores son acerca de la repetición. Puse esto en clases abstractas, así que solo necesito escribir este código muy simple una vez. Por lo tanto, al menos yo mismo no necesito repetirme.

Estas dos clases abstractas son utilizadas por otras clases que ya se han discutido anteriormente. Debido a que son tan simples, lo dejé hasta aquí.

Bueno, mucho para leer hasta aquí, pero la parte buena es, eso es todo.

En resumen: PHP no tiene esta compilación, pero puede escribir esto por su cuenta bastante simple y reutilizable. Según lo escrito anteriormente, es una buena idea para envolver esto en una función llamada xmltree_dump por lo que se puede llamar fácilmente para fines de depuración:

function xmltree_dump(DOMNode $node) 
{ 
    $iterator = new DOMRecursiveIterator($node); 
    $decorated = new DOMRecursiveDecoratorStringAsCurrent($iterator); 
    $tree = new RecursiveTreeIterator($decorated); 
    foreach($tree as $key => $value) 
    { 
     echo $value . "\n"; 
    } 
} 

Uso:

$dom = new DOMDocument(); 
$dom->loadHTML("<html><body><p>Hello World</p></body></html>"); 
xmltree_dump($dom->documentElement); 

el único que se necesita es tener todos las definiciones de clase utilizadas incluidas/requeridas. Puede ponerlos en un archivo y usar require_once o integrarlos con un autocargador que probablemente esté usando. Full code at once.

Si necesita editar la forma de salida, puede editar DOMRecursiveDecoratorStringAsCurrent o cambiar la configuración de RecursiveTreeIterator­ dentro de xmltree_dump. Espero que esto sea útil (incluso bastante largo, hacia atrás es bastante directo).

+14

+1 .... y escribiste todo esto el día de Navidad también. Logro "Forever Alone" desbloqueado. – Dunhamzzz

+5

Happy Chanukka! – hakre

+0

Obtengo 'Error fatal capturable: el argumento 1 pasado a IteratorIterator :: __ construct() debe implementar la interfaz Traversable, instancia de DOMNodeList dada' - ¿qué estoy haciendo mal? Agarré el código de la esencia y utilicé el último ejemplo en el bloque 'usage' en la parte inferior ... – cwd

-1

Puede hacer trampa y usar JSON para inspeccionar la estructura convirtiéndola en una matriz.

print_r(json_decode(json_encode($node), true));