在PHP中调试DOMDocument对象 [英] Debug a DOMDocument Object in PHP
问题描述
我正在尝试在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);
}
}
这是一个很短的类,只有两个功能.我在这里作弊,因为这堂课也来自另一堂课.但是,正如所写的那样,这是向后的,因此此类实际上负责递归:hasChildren
和getChildren
.显然,即使这两个函数也没有太多代码,它们只是将问题"(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
-
DOMRecursiveIterator
和DOMIterator
在DOMDocument
中的所有节点上进行递归迭代.
RecursiveTreeIterator
for the tree-like output.DOMRecursiveDecoratorStringAsCurrent
for the visualization of aDOMNode
in the treeDOMRecursiveIterator
andDOMIterator
to iterate recursively over all nodes in aDOMDocument
.
就所有需要的定义而言,但这仍然缺少我称为抽象的代码.这只是某种简单的代理代码,它将相同的方法委托给另一个对象.一个相关的模式称为 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屋!