在PHP中调试DOMDocument对象 [英] Debug a DOMDocument Object in PHP

查看:73
本文介绍了在PHP中调试DOMDocument对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在php中调试大型且复杂的DOMDocument对象.理想情况下,如果我可以使DOMDocument以类似数组的格式输出,那就太好了.

I'm trying to debug a large and complex DOMDocument object in php. Ideally it'd be nice if I could get DOMDocument to output in a array-like format.

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

或者类似的东西.为什么没有方便的调试或输出呢?!?

Or something like that. Why is there no handy debug or output for this?!?

推荐答案

这个答案可能有点晚了,但是我喜欢你的问题!

This answer is a little late probably, but I liked your question!

PHP没有直接内置的组件可以解决您的问题,因此没有XML转储之类的东西.

PHP has nothing build-in directly to solve your problem, so there is not XML dump or something.

但是,PHP具有 RecursiveTreeIterator ­ Docs 非常接近您的输出:

However, PHP has the RecursiveTreeIterator­Docs that comes pretty close to your output:

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

(如果您的X(HT)ML结构看起来更复杂,它将看起来更好.)

(it will look better if your X(HT)ML structure looks more complicated.)

它与foreach一起使用非常简单(与大多数迭代器一样):

It's used quite simple (as most iterators) with a foreach:

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

(您可以将其包装在一个函数中,因此只需要调用该函数即可)

(You can wrap this inside a function, so you only need to call the function)

即使这看起来很简单,也有一个警告:它在DOMDocument树上需要一个RecursiveIterator.由于PHP无法猜测您需要什么,因此需要将其包装到代码中.如所写,我发现这个问题很有趣(显然您没有要求XML输出),所以我写了一些小代码来提供所需的递归迭代器.所以我们开始吧.

Even this looks simple, there's one caveat: it needs a RecursiveIterator over the DOMDocument tree. As PHP can not guess what you need, it needs to be wrapped into code. As written, I found the question interesting (and obviously you have not asked for XML output), so I wrote some little code that offers the recursive iterator needed. So here we go.

首先,您可能不熟悉PHP中的迭代器.使用将向我显示的代码向后时,这没什么用,但是,每当您考虑自己运行一些代码时,请考虑是否可以使用PHP必须提供的迭代器功能.我写这封信是因为它有助于解决常见问题,并使彼此之间不真正相关的组件相互配合.例如, RecursiveTreeIterator ­ Docs 是内置的,它将与您提供的任何内容一起使用(甚至可以对其进行配置).但是,需要使用RecursiveIterator进行操作.

First of all you might not be familiar with iterators in PHP. That's no deal to make use of the code I'll show as I'll do it backwards, however, whenever you consider to run some code on your own, consider whether or not you can make use of the iterator capabilities PHP has to offer. I write that because it helps to solve common problems and to make components that are not really related with each other to work with each other. For example, the RecursiveTreeIterator­Docs is built-in, and it will work with anything you feed it with (and you can even configure it). However it needs a RecursiveIterator to operate upon.

因此,让我们给它一个RecursiveIterator,它为DOMNodes提供<tag>,这些<tag>是标签(元素),而如果text是textnode,则仅提供text:

So let's give it a RecursiveIterator that offers <tag> for DOMNodes that are tags (elements) and just the text if they are 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函数,该函数仅在方括号 Wikipedia (<>)和textnodes的文本保持原样.这就是您的输出所需要的,所以这就是编码所需要的一切.

This DOMRecursiveDecoratorStringAsCurrent class (the name is exemplary only) makes use of some abstract code in RecursiveIteratorDecoratorStub. The important part however is the ::current function which just returns the tagName of a DOMNode in bracketsWikipedia (<>) and the text of textnodes as-is. That's what your output needs, so that's everything needed to code.

实际上,这只有在您拥有抽象代码后才能起作用,但是要可视化代码的使用方式(最有趣的部分),我们来看一下:

Actually this does not work until you have the abstract code as well, but to visualize the code how it's used (the most interesting part), let's view it:

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

因为它是向后完成的,所以目前,我们已经根据RecursiveTreeIterator要显示的DOMNode指定了输出.到目前为止还不错,很容易获得.但是缺少的地方在抽象代码中,以及如何在DOMElement中的所有节点上创建RecursiveIterator.只需预览整个代码的调用方式即可(如前所述,您可以将其放入函数中,以使其易于在代码中访问以进行调试.可能是名为xmltree_dump的函数):

As it's done backwards, for the moment we have the output specified based on which DOMNode is to be displayed by the RecursiveTreeIterator. Fine so far, easy to get. But the missing meat it is inside the abstract code and how to create a RecursiveIterator over all nodes inside a DOMElement. Just preview the whole code how it is invoked (as written before, you can put this into a function to make it easily accessible within your code for debugging purposes. Probably a function called 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代码.

So what do we got here in addition to the code already covered? First there is a DOMRecursiveIterator - and that's it. The rest of the code is standard DOMDocument code.

因此,让我们来写一下DOMRecursiveIterator.最终在RecursiveTreeIterator中需要的是RecursiveIterator.它得到了装饰,以便树的转储实际上将标记名打印在方括号中,并按原样打印文本.

So let's write about DOMRecursiveIterator. It's the needed RecursiveIterator that's finally needed within the RecursiveTreeIterator. It get's decorated so that the dump of the tree actually prints tagnames in brackets and text as-is.

现在值得分享它的代码:

Probably it's worth to share the code of it now:

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

这是一个很短的类,只有两个功能.我在这里作弊,因为这堂课也来自另一堂课.但是,正如所写的那样,这是向后的,因此此类实际上负责递归:hasChildrengetChildren.显然,即使这两个函数也没有太多代码,它们只是将问题"(hasChildren?getChildren?)映射到标准DOMNode上.如果节点上有子节点,那么,说是"或仅返回它们(这是一个迭代器,以迭代器的形式返回它们,因此返回new self()).

It's a pretty short class with only two functions. I'm cheating here as this class also extends from another class. But as written, this is backwards, so this class actually takes care of the recursion: hasChildren and getChildren. Obviously even those two functions don't have much code, they are just mapping the "question" (hasChildren? getChildren?) onto a standard DOMNode. If a node has children, well, say yes or just return them (and this is an iterator, return them in form of an iterator, hence the new self()).

由于这很短,在窒息之后,只需继续父类DOMIterator( implements RecursiveIterator ­ Docs 只是为了使其正常工作):

So as this is pretty short, after choking it, just continue with the parent class DOMIterator (the implements RecursiveIterator­Docs is just to make it working):

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并将其传递给父类,该父类也是抽象代码.当然,我会在一分钟内显示此代码.由于这是倒退,所以让我们回顾一下到目前为止所做的事情:

This is the base iterator for DOMPHP, it just takes a DOMNode or a DOMNodeList to iterate over. This sounds a bit superfluous maybe, as DOM supports this sort-of with DOMNodeList already, but it does not support a RecursiveIterator and we already know that we need one for RecursiveTreeIterator for the output. So in it's constructor an Iterator is created and passed on to the parent class, which again is abstract code. Sure I'll reveal this code in just a minute. As this is backwards, let's review what's been done so far:

  • RecursiveTreeIterator用于树状输出.
  • DOMRecursiveDecoratorStringAsCurrent用于在树中可视化DOMNode
  • DOMRecursiveIteratorDOMIteratorDOMDocument中的所有节点上进行递归迭代.
  • RecursiveTreeIterator for the tree-like output.
  • DOMRecursiveDecoratorStringAsCurrent for the visualization of a DOMNode in the tree
  • DOMRecursiveIterator and DOMIterator to iterate recursively over all nodes in a DOMDocument.

就所有需要的定义而言,但这仍然缺少我称为抽象的代码.这只是某种简单的代理代码,它将相同的方法委托给另一个对象.一个相关的模式称为 Decorator .但是,这只是代码,首先是Iterator,然后是RecursiveIterator朋友:

This in terms of definition as all that's needed, however the code that I called abstract is still missing. It's just some sort of simple proxy code, it delegates the same method down to another object. A related pattern is called Decorator. However, this is just the code, first the Iterator and then it's 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.看起来像是重复,并且良好的迭代器与重复有关.我将其放入抽象类中,因此只需要编写一次非常简单的代码即可.所以至少我自己不需要重复自己.

That's nothing very magically, it's just well delegating the method calls to it's inherited object $iterator. It looks like repeating and well iterators are about repetition. I put this into abstract classes so I only need to write this very simple code once. So at least I myself don't need to repeat myself.

这两个抽象类已由前面已经讨论过的其他类使用.因为它们是如此简单,所以我把它留到这里.

These two abstract classes are used by other classes which have been already discussed earlier. Because they are so simple, I left it until here.

好吧,直到这里要读的东西很多,但是好的是,就是这样.

Well, much to read until here but the good part is, that's it.

简而言之:PHP没有内置此功能,但是您可以自己编写,非常简单且可重复使用.如前所述,将其包装到名为xmltree_dump的函数中是一个好主意,以便可以出于调试目的轻松调用它:

In short: PHP does not have this build in, but you can write this on your own quite simple and re-useable. As written earlier, it's a good idea to wrap this into a function called xmltree_dump so it can be easily called for debugging purposes:

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或将它们与您可能正在使用的自动加载器集成. 一次完整的代码.

the only thing needed is to have all the class definitions used included / required. You can put them in one file and use require_once or integrate them with an autoloader that you're probably using. Full code at once.

如果需要编辑输出方式,可以编辑DOMRecursiveDecoratorStringAsCurrent或在xmltree_dump中更改RecursiveTreeIterator­的配置.希望这对您有所帮助(即使很长,向后也很间接).

If you need to edit the way of output, you can edit DOMRecursiveDecoratorStringAsCurrent or change the configuration of RecursiveTreeIterator­ inside xmltree_dump. Hope this is helpful (even quite lengthy, backwards is pretty in-direct).

这篇关于在PHP中调试DOMDocument对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆