Отладка объекта DOMDocument в PHP

Я пытаюсь отлаживать большой и сложный объект DOMDocument в php. В идеале было бы неплохо, если бы я смог получить DOMDocument для вывода в виде массива.

DomDocument:

  $ dom = новый DOMDocument ();
 $ dom-> loadHTML ("<html> <body> <p> Hello World </ p> </ body> </ html>");
 var_dump ($ дом);  // или что-то эквивалентное 

Эти результаты

  Объект DOMDocument () 

тогда как я бы хотел, чтобы он выводил

  DOMDocument:
 HTML
 => Тело
 ==> р
 ===> Привет, мир 

Или что-то типа того. Почему нет удобного отладки или вывода для этого?!?

Этот ответ немного поздно, но мне понравился ваш вопрос!

PHP не имеет ничего встроенного для решения вашей проблемы, поэтому нет дампа XML или чего-то еще.

Однако PHP имеет документы RecursiveTreeIterator которые приближаются к вашему результату:

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

(он будет выглядеть лучше, если ваша структура X (HT) ML выглядит более сложной.)

Он используется довольно просто (как и большинство итераторов) с помощью foreach :

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

(Вы можете обернуть это внутри функции, поэтому вам нужно только вызвать функцию)

Даже это выглядит просто, есть одно предостережение: DOMDocument дерева DOMDocument нужен RecursiveIterator DOMDocument . Поскольку PHP не может догадаться, что вам нужно, его необходимо обернуть в код. Как написано, я нашел интересующий вопрос (и, очевидно, вы не запрашивали выход XML), поэтому я написал небольшой код, который требует рекурсивного итератора. Итак, идем.

Прежде всего, вы, возможно, не знакомы с итераторами в PHP. Это не имеет смысла использовать код, который я покажу, поскольку я сделаю это в обратном порядке , однако, всякий раз, когда вы планируете самостоятельно запускать какой-либо код, подумайте, можете ли вы использовать возможности итератора, которые PHP может предложить , Я пишу, потому что он помогает решать общие проблемы и создавать компоненты, которые не связаны друг с другом, чтобы работать друг с другом. Например, Документы RecursiveTreeIterator встроены, и он будет работать со всем, что вы его подаете (и вы даже можете его настроить). Однако для работы требуется RecursiveIterator Истребитель.

Итак, давайте представим RecursiveIterator который предлагает <tag> для DOMNodes которые являются тегами (элементами) и просто text если они являются text :

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

Этот класс DOMRecursiveDecoratorStringAsCurrent (только для примера) использует какой-то абстрактный код в RecursiveIteratorDecoratorStub . Однако важная часть – это функция ::current которая возвращает имя tagName DOMNode в скобках Wikipedia ( <> ) и текст текстовых полей как есть. Это то, что требует ваш выход, так что все, что нужно для кодирования.

На самом деле это не сработает, пока у вас не будет абстрактный код, но чтобы визуализировать код, как он используется (самая интересная часть), давайте посмотрим:

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

Поскольку это сделано в обратном направлении, на данный момент у нас есть выход, указанный на основании того, какой DOMNode должен отображаться RecursiveTreeIterator . Пока хорошо, легко добраться. Но недостающее мясо находится внутри абстрактного кода и как создать RecursiveIterator по всем узлам внутри DOMElement . Просто просмотрите весь код, как он вызывается (как было написано ранее, вы можете поместить это в функцию, чтобы сделать его легко доступным в вашем коде для целей отладки. Возможно, это функция, называемая 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"; } 

Итак, что мы получили в дополнение к уже охваченному коду? Сначала есть DOMRecursiveIterator – и все. Остальная часть кода является стандартным кодом DOMDocument .

Итак, давайте напишем о DOMRecursiveIterator . Это необходимый RecursiveIterator который, наконец, необходим в RecursiveTreeIterator . Он оформляется так, что дамп дерева фактически печатает тэги в скобках и текст как есть.

Вероятно, сейчас стоит поделиться своим кодом:

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

Это довольно короткий класс с двумя функциями. Я обманываю здесь, так как этот класс также распространяется из другого класса. Но, как написано, это обратное, поэтому этот класс действительно заботится о рекурсии: hasChildren и getChildren . Очевидно, что даже эти две функции не имеют большого количества кода, они просто отображают «вопрос» ( hasChildren ? getChildren ?) На стандартный DOMNode . Если узел имеет дочерние элементы, ну, скажите «да» или просто верните их (и это итератор, верните их в форме итератора, следовательно, new self() ).

Так как это довольно коротко, после его удушения, просто продолжайте с родительского класса DOMIterator (для implements RecursiveIterator DOMIterator Docs просто для его работы):

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

Это базовый итератор для DOMPHP , он просто DOMNode или DOMNodeList . Это звучит немного избыточно, возможно, поскольку DOM уже поддерживает этот тип с DOMNodeList , но он не поддерживает RecursiveIterator и мы уже знаем, что для выхода нам нужен один для RecursiveTreeIterator . Поэтому в его конструкторе Iterator создается и передается родительскому классу, который снова является абстрактным кодом. Конечно, я открою этот код всего за минуту. Поскольку это назад, давайте рассмотрим, что было сделано до сих пор:

  • RecursiveTreeIterator для древовидного вывода.
  • DOMRecursiveDecoratorStringAsCurrent для визуализации DOMNode в дереве
  • DOMRecursiveIterator и DOMIterator для итерации рекурсивно по всем узлам в DOMDocument .

Это с точки зрения определения, как все, что необходимо, однако код, который я назвал абстрактным, по-прежнему отсутствует. Это всего лишь простой прокси-код, он передает тот же метод другому объекту. Связанный шаблон называется Decorator . Однако это всего лишь код, сначала Iterator а затем его RecursiveIterator :

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

Это не так уж и волшебно, это просто делегирование вызова метода наследуемый объект $iterator . Похоже, повторяющиеся и хорошо повторяющиеся повторы повторяются. Я помещал это в абстрактные классы, поэтому мне нужно только написать этот очень простой код один раз. Поэтому, по крайней мере, мне самому не нужно повторять себя.

Эти два абстрактных класса используются другими классами, которые уже обсуждались ранее. Потому что они такие простые, что я оставил его до сих пор.

Ну, много читать, пока здесь, но хорошая часть, вот и все.

Короче: PHP не имеет этой сборки, но вы можете написать это по своему усмотрению довольно просто и повторно использовать. Как написано ранее, неплохо было бы превратить это в функцию с именем xmltree_dump поэтому ее можно легко вызвать для целей отладки:

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

Применение:

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

единственное, что нужно, это иметь все используемые определения классов. Вы можете поместить их в один файл и использовать require_once или интегрировать их с автозагрузчиком, который вы, вероятно, используете. Полный код сразу .

Если вам нужно отредактировать способ вывода, вы можете отредактировать DOMRecursiveDecoratorStringAsCurrent или изменить конфигурацию RecursiveTreeIterator внутри xmltree_dump . Надеюсь, что это полезно (даже довольно продолжительное, назад довольно прямое).

http://usphp.com/manual/en/function.dom-domdocument-savexml.php

 $dom->formatOutput = true; echo $dom->saveXML(); 

для узла dom, используйте только следующее:

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

Хотя я сам не пробовал, проверьте Zend_Dom , часть Zend Framework . Документация и примеры для большинства компонентов Zend Framework действительно тщательны.

Я просто использовал DOMDocument :: save. Это хрома, что он должен писать в файл, но что угодно.

Вы можете обманывать и использовать JSON для проверки структуры путем преобразования ее в массив.

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