DOMツリーを再帰的に走査する
XML構造におけるタグは、他のタグ(ツリーの枝)を含むものと、子タグを1つも含まないもの(=リーフ(葉)タグ)のどちらかに分類されます。そのため、任意のノードから開始してそれぞれの枝を末端の葉までたどっていけば、ツリー全体や部分ツリーを再帰的に走査できます。次の例では、与えられたルートノード($node)から任意のXML部分ツリーを走査し、見つかった各ノードの名前と値を出力します。
function getNodesInfo($node)
{
if ($node->hasChildNodes())
{
$subNodes = $node->childNodes;
foreach ($subNodes as $subNode)
{
if (($subNode->nodeType != 3) ||
(($subNode->nodeType == 3) &&
(strlen(trim($subNode->wholeText))>=1)))
{
echo "Node name: ".$subNode->nodeName."\n";
echo "Node value: ".$subNode->nodeValue."\n";
}
getNodesInfo($subNode);
}
}
}
上の例では、次の条件判定に基づいて空のテキストノードを削除し、出力を整形しています。
if (($subNode->nodeType != 3) || (($subNode->nodeType == 3) && (strlen(trim($subNode->wholeText))>=1)))
このコードは、処理中のノードがテキストノード以外(nodeType != 3)か、テキストノードでかつ空でないテキストを持つかをチェックするものです。こうしなくても定義済みのpreserveWhiteSpaceプロパティをFALSEに設定すれば、余計な空白は取り除かれます。このデフォルト値はTRUEです。
次の例では、テストのためにBook.xml文書のルートノードを再帰関数getNodesInfoに渡してDOMツリー全体のタグと値を出力します。
<?php
//Create a document instance
$doc = new DOMDocument();
//Load the Book.xml file
$doc->load( 'Book.xml' );
//Setting the objects tree root
$root = $dom->firstChild;
// Recursive function to list all nodes of a subtree
function getNodesInfo($node)
{
if ($node->hasChildNodes())
{
$subNodes = $node->childNodes;
foreach ($subNodes as $subNode)
{
if (($subNode->nodeType != 3) ||
(($subNode->nodeType == 3)
&&(strlen(trim($subNode->wholeText))>=1)))
{
echo "Node name: ".$subNode->nodeName."\n";
echo "Node value: ".$subNode->nodeValue."\n";
}
getNodesInfo($subNode);
}
}
}
//The getNodesInfo function call
getNodesInfo($root);
?>
図1に出力の一部を示します。
新しいノードを追加する
DOMNodeインターフェースには、ノードを作成するメソッドや、作成したノードをDOMツリーに挿入するメソッドが用意されています。新しいノードを作成するには、createElementメソッドかcreateTextNodeメソッドを使います。作成したノードをDOMツリーに挿入するには、appendChildメソッドかinsertBeforeメソッドを使います。appendChildメソッドは指定した子ノードのリストの最後に新しい子ノードを追加し、insertBeforeメソッドは指定したノードの前に新しい子を挿入します。
次に、これらのメソッドのプロトタイプを示します。
DOMElement createElement(string $name [, string $value ])
このメソッドはDOMElementクラスのインスタンスを生成します。引数$nameは新しい要素に付けるタグ名、引数$valueは要素の値です。DOMElement->nodeValueプロパティを使えば、この値を後で設定することもできます。
DOMText createTextNode(string $content)
このメソッドはDOMTextクラスのインスタンスを生成します。引数$contentは新しいテキストノードに与えるテキストコンテンツです。
DOMNode DOMNode::appendChild(DOMNode $newnode)
この関数は引数$newnodeを既存の子ノードリストの最後に付加します。または指定したノードを持つ新しい子ノードリストを作成します。
DOMNode DOMNode::insertBefore(DOMNode $newnode [,DOMNode $refnode])
このメソッドは$refnodeで指定した参照ノードの前に引数$newnodeを挿入します。$refnodeを省略した場合、新しいノードはノードの子ノードリストの前に付加されます。
次の例では<bibliography>ノードを作成し、ツリーの最後に付加します。
//Create a new element
$newElement = $dom->createElement('bibliography','Martin Didier, Professional XML');
// Add it to the root using the appendChild method
//The appendChild function call
appendNewChild($root,$newElement);
//This function appends a new child node
function appendNewChild($currentNode, $node)
{
$currentNode->appendChild($node);
}
この結果をgetNodeInfo()メソッドで処理すると、図2のような出力が得られます。

次の例では、新しい子<foreword>を<publisher>ノードの前に追加します。
//create a new <foreword> element
$newElement = $dom->createElement('foreword',
'What I love about this book is that it '.
'grew out of just such a process, '.
'and shows it on every page.');
//Set the reference node
$allContents = $dom->getElementsByTagName('publisher');
$contents = $allContents->item(0);
//Call the insertNewChild function
insertNewChild($contents,$newElement);
//This function inserts a new child
//as the first child of $currentNode
function insertNewChild($currentNode, $node)
{
$currentNode->insertBefore(
$node, $currentNode->firstChild);
}
変更後の文書をgetNodesInfoで処理すると新しいノードが表示されます(図3を参照)。


