PHPでDOMを操作するこの記事のURLhttp://japan.internet.com/developer/20090915/26.html
著者:Octavia Andreea Anghel
海外internet.com発の記事
はじめにDocument Object Model(DOM)とは、XML(またはHTML)文書をツリー構造のオブジェクトとして表現するための一連のインターフェースを定めたW3C規格です。DOMツリーは文書の論理的な構造を定義し、プログラムによる文書の操作を制御します。開発者はDOMを使用して、XML文書やHTML文書の作成、ツリー構造内での移動、要素やコンテンツの追加、変更、削除を行うことができます。DOMは任意のプログラミング言語から操作できます。本稿ではPHP 5のDOMエクステンションを使います。これはPHPコアに既に実装されているので、別途インストールすべきものは特にありません。DOMツリーはXML規約に基づいて命名されたノードで構成されます。よく知られているDOMノードには次のものがあります。
要素を抽出するDOMツリーから要素と値を抽出する方法を具体的に説明します。本稿で使用するサンプル文書「Book.xml」をリスト1に示します。リスト1 Book.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<book>
<!--XML Processing [part I] -->
<name>XML Processing I</name>
<author>John Smith Jr.</author>
<publisher>HisOwnTM</publisher>
<ISBN>111-222-333-4441</ISBN>
<contents>
<chapter_I>
<title>What is XML about ?</title>
<content>XML (Extensible Markup Language) is a ...</content>
</chapter_I>
<chapter_II>
<title>SAX</title>
<content>SAX is a simple API for ...</content>
</chapter_II>
<chapter_III>
<title>StAX</title>
<content>Much powerful and flexible, StAX, is very...</content>
</chapter_III>
<chapter_IV>
<title>DOM
<subtitle>DOM concept
<continut>Starting to use DOM...</continut>
</subtitle>
<subchapter_IV_I>
<title>First DOM application...</title>
<content>Here it is your first DOM application...</content>
</subchapter_IV_I>
</title>
</chapter_IV>
<end>The end...</end>
</contents>
<!-- See you in XML Processing [part II] -->
</book>
最初のアプリケーションではBook.xmlを使って、関連付けられたツリーを抽出し、いくつかの子ノードの最初のオカレンスをDOMElementインターフェースの getElementsByTagNameメソッドを使って表示します。getElementsByTagNameメソッド
DOMNodeList DOMElement::getElementsByTagName(string $name) $nameパラメータで指定したタグ名を持つすべての子要素のリストを返します。次の例では、<book>ルートノードを検索し、そのすべての子要素<author>、<publisher>、<name>を検索して各要素の最初のオカレンスを選択し、それらのノードの値を出力します。<?php // Create a document instance $doc = new DOMDocument(); //Load the Book.xml file $doc->load( 'Book.xml' ); //Searches for all elements with the "book" tag name $books = $doc->getElementsByTagName( "book" ); //Searches for all elements with the "author" tag name $authors = $doc->getElementsByTagName( "author" ); //Returns the first element found having the tag name "author" $author = $authors->item(0)->nodeValue; //Searches for all elements with the "publisher" tag name $publishers = $doc->getElementsByTagName( "publisher" ); //Returns the first element found //having the tag name "publisher" $publisher = $publishers->item(0)->nodeValue; //Searches for all elements with the "name" tag name $titles = $doc->getElementsByTagName( "name" ); //Returns the first element found having the tag name "name" $title = $titles->item(0)->nodeValue; //Printing the found values echo "$title - $author - $publisher \n"; ?> XML Processing I - John Smith Jr. - HisOwnTM 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);
?>
新しいノードを追加するDOMNodeインターフェースには、ノードを作成するメソッドや、作成したノードをDOMツリーに挿入するメソッドが用意されています。新しいノードを作成するには、createElementメソッドかcreateTextNodeメソッドを使います。作成したノードをDOMツリーに挿入するには、appendChildメソッドかinsertBeforeメソッドを使います。appendChildメソッドは指定した子ノードのリストの最後に新しい子ノードを追加し、insertBeforeメソッドは指定したノードの前に新しい子を挿入します。次に、これらのメソッドのプロトタイプを示します。 createElementメソッド
DOMElement createElement(string $name [, string $value ]) $nameは新しい要素に付けるタグ名、引数$valueは要素の値です。DOMElement->nodeValueプロパティを使えば、この値を後で設定することもできます。createTextNodeメソッド
DOMText createTextNode(string $content) $contentは新しいテキストノードに与えるテキストコンテンツです。appendChildメソッド
DOMNode DOMNode::appendChild(DOMNode $newnode) $newnodeを既存の子ノードリストの最後に付加します。または指定したノードを持つ新しい子ノードリストを作成します。insertBeforeメソッド
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のような出力が得られます。図2 付加されたノード: 新しいノード<bibliography>とそのコンテンツが文書の最後に付加されている
![]() <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を参照)。ノードを複製するノードの複製とは、同じ型の新しいノードを作成し、(必要なら)現在のノードと同じコンテンツに設定することを意味します。ノードを複製するにはcloneNodeメソッドを使います。cloneNodeメソッド
DOMNode DOMNode::cloneNode([ bool $deep]) $deepは、現在のノードの子もコピーするかどうかを指定するものです。デフォルト値はFALSE(コピーしない)です。例えば、次のコードは要素<author>を複製し、元の要素<author>の子として付加します。図4に結果を示します。
//Set the reference node
$author = $root->getElementsByTagName('author')->item(0);
//Call the cloningNode function
cloningNode($author);
//This function clone the $currentNode
function cloningNode($currentNode)
{
$clonenode = $currentNode -> cloneNode(true);
$newnode = $currentNode->appendChild($clonenode);
}
図4 ノードの複製: 子ノード<author>を複製して元のノード<author>に付加した結果。ノードのテキスト値を取得すると子ノードのテキスト値も取得されるので、元のノードのテキスト値は二重になる
![]() 子ノードを削除するノードをDOMツリーから削除するにはremoveChildメソッドを使います。removeChildメソッド
DOMNode DOMNode::removeChild(DOMNode $oldnode) $oldnodeは削除する子ノードです。例えば、次のコードは子ノード<bibliography>をBooks.xml文書から削除します。結果(図5)を見ると、bibliographyノードがなくなっていることが分かります。
//Get a reference to the bibliography node
$bibliography = $root->getElementsByTagName(
'bibliography')->item(0);
//Call the removingChild function
removingChild($bibliography);
//This function remove the $currentNode node
function removingChild($currentNode)
{
$oldbibliography = $root->removeChild($currentNode);
}
ノードを置換する既存のノードを新しいノードに置き換えるには、replaceChildメソッドを使います。replaceChildメソッド
DOMNode DOMNode::replaceChild(DOMNode $newnode, DOMNode $oldnode) $oldnodeを新しい子ノード$newnodeに置き換えます。例えば、子ノード ISBNを新しい子ノードcodeに置き換えるには次のようにします。
//Get the ISBN node
$element = $dom->getElementsByTagName('ISBN')->item(0);
//Create the new <code> element
$code = $dom->createElement('code', '909090');
//Call the replacingNode function
replacingNode($code,$element);
//This function replaces $currentNode with $node
function replacingNode($currentNode, $node)
{
$node->parentNode->replaceChild($currentNode, $node);
}
図6 ノードの置換: 文書のこの部分でノード<ISBN>が新しいノード<code>に置き換えられている
![]() ノードをインポートする現在のツリーに別のツリーからノードをコピーするにはimportNodeメソッドを使います。importNodeメソッド
DOMNode DOMDocument::importNode(DOMNode $importedNode [,bool $deep]) $oldnodeを新しい子ノード$newnodeに置き換えます。このメソッドはノードを別のXML文書からインポートし、現在の文書のDOMツリーに挿入します。引数$importedNodeはインポートするノードです。インポートされるノードは元のノードの''コピー''なので、インポートによって外部のツリーが変更されることはありません。引数$deepはインポートするノードをディープコピーするかどうかを指示するものです。TRUEのときは、ノードの部分ツリー全体がインポートされ、FALSEのときは、直接のノードだけがインポートされます。例として、ノード <continue>をBook_continue.xmlファイルからBook.xmlにインポートすることを考えます。Book_continue.xml文書の内容は次のとおりです。
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!--chapter V-->
<continue>
<chapter_V>
<title>XPath</title>
<content>XPath is language for...</content>
</chapter_V>
<![CDATA[
This chaper is a bonus to...
]]>
<printing cap_I="click_here_for_chapter_I"
cap_II="click_here_for_chapter_II"
cap_III="click_here_for_chapter_III"
cap_IV="click_here_for_chapter_IV"
cap_V="click_here_for_chapter_V" />
</continue>
<continue>をインポートするコードは次のようになります。
<?php
$olddoc = new DOMDocument;
$olddoc->load("Book_continue.xml");
// The node we want to import to a new document
$node = $olddoc->getElementsByTagName("continue")->item(0);
$newdoc = new DOMDocument;
$newdoc->formatOutput = true;
$newdoc->load("Book.xml");
// Import the node, and all its children, to the document
$node = $newdoc->importNode($node, true);
// And then append it to the root node
$newdoc->documentElement->appendChild($node);
echo "\nThe 'new document' after copying the nodes into it:\n";
$root = $newdoc->firstChild;
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);
}
}
}
getNodesInfo($root);
?>
ノードの等値性をチェックする2つのノードが同じかチェックするには関数isSameNodeを使います。isSameNodeメソッド
bool DOMNode::isSameNode(DOMNode $node) $nodeは現在のノードと比較するノードです。この比較はノードのコンテンツに基づいて行われるものではないことに注意してください。
//Checking if two nodes are equals
$author1 = $root->getElementsByTagName('autor')->item(0);
$author2 = $root->getElementsByTagName('autor')->item(1);
//The verifyNodes function call
verifyNodes($author1,$author2);
function verifyNodes($currentNode, $node)
{
if (($currentNode->isSameNode($node))==true)
{
echo "These two nodes are the same";
}
}
新しいツリーを作成する既存のツリーを最初から使わなくてもかまいません。PHP 5のDOMエクステンションではツリーをゼロから作ることもできます。次の例では、まったく新しいXML文書を作成します。ここではコメントノードを作成する関数とCDATAノードを作成する関数を使用しています。createCommentメソッド
DOMComment DOMDocument::createComment(string $data) $dataはノードのコンテンツです。createCDATASectionメソッド
DOMCDATASection DOMDocument::createCDATASection(string $data) $dataはノードのコンテンツです。リスト2の例ではオブジェクトツリーを作成し、Flowers.xmlという名前で保存します。 リスト2 新しいDOMツリーの作成
<?php
//Create a document instance
$document = new DOMDocument();
//Formats output with indentation
$document->formatOutput = true;
//Create a comment
$comment = $document->createComment('Beautiful flowers!!!');
$document->appendChild( $comment );
//Create the <flowers> root element
$root = $document->createElement( 'flowers' );
$document->appendChild( $root );
//Create the <tulips> children of the root
$tulips = $document->createElement( 'tulips' );
//Create the first child of the <tulips> element,<bulbs>,
// and set its attribute
$bulbs_1 = $document->createElement( 'bulbs' );
$bulbs_1->setAttribute('price','€ 7.65');
$bulbs_1->appendChild($document->createTextNode( 'Parrot'));
$tulips->appendChild( $bulbs_1 );
//Create the second child of the <tulips> element,<bulbs>,
// and set its attribute
$bulbs_2 = $document->createElement( 'bulbs' );
$bulbs_2->setAttribute('color','magenta');
$bulbs_2->appendChild($document->createTextNode( 'Lily flowering' ));
$tulips->appendChild( $bulbs_2 );
//Append the <tulips> node to the root
$root->appendChild( $tulips );
//Create a CDATA section
$cdata = $document->createCDATASection(
'<gladiolus><species>Sword Lily</species>'.
'<species>Starface</species></gladiolus>');
$document->appendChild( $cdata );
//Save the object tree to Flowers.xml
echo $document->saveXML();
$document->save('Flowers.xml');
?>
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--Beautiful flowers!!!-->
<flowers>
<tulips>
<bulbs price="€ 7.65">Parrot</bulbs>
<bulbs color="magenta">Lily flowering</bulbs>
</tulips>
</flowers>
<![CDATA[<gladiolus>
<species>Sword Lily</species>
<species>Starface</species>
</gladiolus>
]]>
著者紹介Octavia Andreea Anghel(Octavia Andreea Anghel)
経験豊富なPHP開発者。現在は、国内外のソフトウェア開発コンテストに参加するプログラミングチームの主任トレーナーを務める。国レベルの教育プロジェクト開発のコンサルティングも担当している。共著書に『XML technologies?XML in Java』があり、XML部分の執筆を担当。PHPやXMLのほか、ソフトウェアアーキテクチャ、Webサービス、UML、ハイパフォーマンスな単体テストについても関心を寄せている。
japan.internet.comのウエブサイトの内容は全て、国際法、日本国内法の定める著作権法並びに商標法の規定によって保護されており、その知的財産権、著作権、商標の所有者はインターネットコム株式会社、インターネットコム株式会社の関連会社または第三者にあたる権利者となっています。
本サイトの全てのコンテンツ、テキスト、グラフィック、写真、表、グラフ、音声、動画などに関して、その一部または全部を、japan.internet.comの許諾なしに、変更、複製、再出版、アップロード、掲示、転送、配布、さらには、社内LAN、メーリングリストなどにおいて共有することはできません。 ただし、コンテンツの著作権又は所有権情報を変更あるいは削除せず、利用者自身の個人的かつ非商業的な利用目的に限ってのみ、本サイトのコンテンツをプリント、ダウンロードすることは認められています。 |