我正在尝试在php中调试一个大而复杂的DOMDocument对象.理想情况下,如果我能让DOMDocument以类似数组的格式输出,那就太好了.
DOM文档:
$dom = new DOMDocument(); $dom->loadHTML("Hello World
"); var_dump($dom); //or something equivalent
这输出
DOMDocument Object ( )
而我希望它输出
DOMDocument: html =>body ==>p ===>Hello World
或类似的东西.为什么没有方便的调试或输出?!?
这个答案可能有点晚了,但我喜欢你的问题!
PHP没有直接内置来解决您的问题,因此没有XML转储或其他东西.
但是,PHP的RecursiveTreeIterator
Docs非常接近您的输出:
\- \- \-\-Hello World
(如果您的X(HT)ML结构看起来更复杂,它看起来会更好.)
使用它非常简单(如大多数迭代器)foreach
:
$tree = new RecursiveTreeIterator($iterator); foreach($tree as $key => $value) { echo $value . "\n"; }
(你可以将它包装在一个函数中,所以你只需要调用该函数)
即使这看起来很简单,有一点需要注意:它需要一个RecursiveIterator
在DOMDocument
树上.由于PHP无法猜测您需要什么,因此需要将其包装到代码中.正如所写,我发现这个问题很有意思(显然你没有要求XML输出),所以我编写了一些提供所需递归迭代器的小代码.所以我们走了.
首先,您可能不熟悉PHP中的迭代器.使用我将要显示的代码没有任何协议,因为我会向后执行它,但是,每当您考虑自己运行某些代码时,请考虑是否可以使用PHP提供的迭代器功能.我这样写是因为它有助于解决常见问题并使彼此之间并不真正相关的组件相互协作.例如,RecursiveTreeIterator
Docs是内置的,它可以与您提供的任何内容一起使用(您甚至可以配置它).然而,它需要一个RecursiveIterator
操作.
所以,让我们给它一个RecursiveIterator
提供
的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
的DOMNode
在括号维基百科(<>
)和textnodes的文本原样.这就是你的输出所需要的,所以这就是编码所需的一切.
实际上,除非你有抽象代码,否则这不起作用,但是为了可视化代码的使用方式(最有趣的部分),我们来看看它:
$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("Hello World
"); $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
已经支持这种类型,但它不支持a RecursiveIterator
,我们已经知道我们需要一个用于RecursiveTreeIterator
输出.因此,在它的构造函数的Iterator
创建和父类,这又是抽象的代码中传递.当然,我会在一分钟内透露这段代码.由于这是倒退,让我们回顾一下到目前为止所做的工作:
RecursiveTreeIterator
对于树状输出.
DOMRecursiveDecoratorStringAsCurrent
用于DOMNode
树中的可视化
DOMRecursiveIterator
并DOMIterator
以递归方式遍历a中的所有节点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("Hello World
"); 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());