21

PHP で大規模で複雑な DOMDocument オブジェクトをデバッグしようとしています。理想的には、DOMDocument を配列のような形式で出力できればいいのですが。

DoMDocument:

$dom = 新しい DOMDocument();
$dom->loadHTML("<html><body><p>Hello World</p></body></html>");
var_dump($ドム); //または同等のもの

これは出力します

DOMDocument オブジェクト ( )

一方、出力したい

DOMドキュメント:
html
=>本体
==>p
===>ハローワールド

またはそのようなもの。なぜ便利なデバッグや出力がないのですか?!?

4

6 に答える 6

36

この答えはおそらく少し遅れていますが、私はあなたの質問が好きでした!

PHP には、問題を解決するために直接組み込まれているものは何もないため、XML ダンプなどはありません。

ただし、PHP には、出力にかなり近いRecursiveTreeIteratorドキュメントがあります。

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

(X(HT)ML 構造がより複雑に見えると、見栄えが良くなります。)

非常に単純に (ほとんどのイテレータと同様に) 使用されますforeach

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

(これを関数内にラップできるため、関数を呼び出すだけで済みます)

これは単純に見えますが、注意点が 1 つありRecursiveIteratorますDOMDocument。PHP は必要なものを推測できないため、コードにラップする必要があります。書かれているように、私はその質問が興味深いものであることがわかりました (そして明らかに XML 出力を要求していません)。そのため、必要な再帰イテレーターを提供する小さなコードをいくつか書きました。それでは、行きましょう。

まず第一に、PHP のイテレーターに慣れていないかもしれません。ここで示すコードをに使用することは問題ありませんが、独自にコードを実行する場合は、PHP が提供するイテレーター機能を使用できるかどうかを検討してください。 . 私がこれを書いているのは、一般的な問題を解決し、実際には相互に関連していないコンポーネントを相互に連携させるのに役立つからです。たとえば、RecursiveTreeIteratorDocsは組み込みであり、それをフィードするものなら何でも動作します (さらに、構成することもできます)。ただし、操作するには が必要RecursiveIteratorです。

では、タグ (要素) と、それらがテキストノードである場合にRecursiveIterator提供<tag>されるを与えましょう。DOMNodestext

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ます。ただし、重要な部分は、括弧内Wikipedia ( ) と textnodes のテキストをそのまま::current返す関数です。それが出力に必要なものであり、コーディングに必要なすべてです。tagNameDOMNode<>

実際には、これは抽象コードも取得するまで機能しませんが、コードがどのように使用されているか (最も興味深い部分) を視覚化するために、コードを表示してみましょう。

$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コードです。

では、について書きましょうDOMRecursiveIteratorRecursiveIterator内で最終的に必要とされるのは、必要な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);
    }
}

関数が 2 つしかない非常に短いクラスです。このクラスは別のクラスからも拡張されているため、ここでごまかしています。しかし、書かれているように、これは逆なので、実際にはこのクラスが再帰を処理します: hasChildrenand getChildren. 明らかに、これら 2 つの関数でさえ多くのコードを持っているわけではありません。「質問」( hasChildren? getChildren?) を標準にマッピングしているだけですDOMNode。ノードに子がある場合は、「はい」と答えるか、それらを返すだけです (これはイテレータであり、イテレータの形式で返すため、new self())。

これは非常に短いので、窒息させた後、親クラスを続行しますDOMIteratorimplements RecursiveIteratorドキュメントはそれを機能させるためのものです):

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には aDOMNodeまたは aが必要DOMNodeListです。DOM はこの種の with をDOMNodeList既にサポートしているため、これは少し余分に聞こえるかもしれませんが、a はサポートしておらず、出力RecursiveIterator用に必要であることは既にわかっています。RecursiveTreeIteratorしたがって、そのコンストラクターIteratorが作成され、親クラスに渡されます。これも抽象コードです。このコードはすぐに公開します。これは逆なので、これまでに行われたことを確認しましょう。

  • RecursiveTreeIteratorツリーのような出力用。
  • DOMRecursiveDecoratorStringAsCurrentDOMNodeツリー内の a の視覚化
  • DOMRecursiveIteratora 内のすべてのノードを再帰的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です。繰り返しのように見えますが、イテレータは繰り返しに関するものです。これを抽象クラスに入れるので、この非常に単純なコードを 1 回記述するだけで済みます。ですから、少なくとも私自身は繰り返す必要はありません。

これら 2 つの抽象クラスは、既に説明した他のクラスによって使用されます。簡単なのでここまでにしておきました。

さて、ここまで読むことはたくさんありますが、良い点はそれだけです。

要するに、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);

必要なのは、使用されるすべてのクラス定義を含める/必須にすることだけです。それらを 1 つのファイルにまとめて、require_onceおそらく使用しているオートローダーで使用または統合することができます。一度に完全なコード

出力方法を編集する必要がある場合は、内部DOMRecursiveDecoratorStringAsCurrentの構成を編集または変更できます。これが役に立てば幸いです (非常に長くても、後方はかなり間接的です)。RecursiveTreeIterator­xmltree_dump

于 2011-12-25T22:50:17.870 に答える
15

http://usphp.com/manual/en/function.dom-domdocument-savexml.php

$dom->formatOutput = true;
echo $dom->saveXML();
于 2010-03-17T16:04:15.287 に答える
10

dom ノードの場合は、次を使用します。

print_r(simplexml_import_dom($entry)->asXML());
于 2010-12-16T22:18:29.083 に答える
0

自分で試したことはありませんが、 Zend Frameworkの一部であるZend_Domを調べてください。ほとんどの Zend Framework コンポーネントのドキュメントとサンプルは非常に詳細です。

于 2009-03-26T05:02:08.017 に答える
-1

JSON をチートして使用し、配列に変換することで構造を調べることができます。

print_r(json_decode(json_encode($node), true));
于 2012-05-17T19:20:57.500 に答える