Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

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

Однако PHP имеет RecursiveTreeIterator & shy; Docs, который близок к вашему результату:

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

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

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

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

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

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

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

Итак, дайте ему RecursiveIterator, который предлагает <tag> для DOMNodes, которые являются тегами (элементами) и просто 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 a DOMNode в скобки Википедия (<>) и текст текстовых узлов как есть. Это то, что нужно для вашего вывода, так что все, что нужно для кодирования.

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

$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. Если у node есть дети, ну, скажите "да" или просто верните их (и это итератор, верните их в форме итератора, следовательно, new self()).

Так как это довольно коротко, после удушения, просто продолжайте с родительского класса DOMIterator (implements RecursiveIterator & shy; 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 для визуализации a DOMNode в дереве
  • DOMRecursiveIterator и DOMIterator, чтобы итератировать рекурсивно по всем узлам в DOMDocument.

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

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. Надеюсь, что это полезно (даже довольно длинное, назад довольно прямое).

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

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