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).
Si lo hizo, podría simplemente guardar HHTML en su lugar y hacer que vaya a una cadena. –