2009-03-26 1 views
16

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

DomDocument:

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

Это выводит

DOMDocument Object ()

, тогда как я хотел бы его к выходу

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

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

ответ

0

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

-1

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

+2

Если вы сделали это, вы могли бы просто сохранитьHTML вместо этого и перейти к строке. –

8

для узла DOM, просто используйте следующую команду:

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

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

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

Однако PHP имеет RecursiveTreeIterator­Docs, который приходит довольно близко к выходу:

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

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

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

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

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

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

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

Так давайте дадим ему RecursiveIterator, который предлагает <tag> для DOMNodes, которые являются теги (элементы) и только text если они 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); 
     } 
    } 
} 

Этот DOMRecursiveDecoratorStringAsCurrent класс (имя только в качестве примера) использует некоторый абстрактный код в RecursiveIteratorDecoratorStub. Важной частью является функция ::current, которая возвращает tagNameDOMNode в bracketsWikipedia (<>) и текст текстовых полей как есть. Это то, что требует ваш выход, так что все, что нужно для кодирования.

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

$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­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 для вывода.Итак, в нем конструктор a 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 или интегрировать их с автозагрузчиком, который вы, вероятно, используете. Full code at once.

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

+14

+1 .... и все это вы написали и на Рождество. Достижение «Forever Alone» разблокировано. – Dunhamzzz

+5

Счастливый Chanukka! – hakre

+0

Я получаю 'Catchable fatal error: Аргумент 1 передан IteratorIterator :: __ construct() должен реализовать интерфейс Traversable, экземпляр DOMNodeList, заданный' - что я делаю неправильно? Я схватил код из gist и использовал последний пример в блоке 'usage' внизу ... – cwd

-1

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^