【PHP8.x】childNodesプロパティの使い方
childNodesプロパティの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
childNodesプロパティは、Dom\CharacterDataクラスに属する、読み取り専用のプロパティです。このプロパティは、ノードの子ノードをNodeListオブジェクトとして保持します。Dom\CharacterDataは、XMLドキュメントやHTMLドキュメントにおけるテキストノードなどの文字データを扱うための抽象クラスです。childNodesプロパティは、この文字データノードが持つ子ノードのリストへのアクセスを提供します。
NodeListオブジェクトは、ノードの順序付きコレクションを表します。コレクション内の個々のノードには、インデックス番号(0から始まる)を使用してアクセスできます。childNodesプロパティを通じて取得されるNodeListは、ライブリストであるため、ドキュメントの構造が変更されると、リストの内容も自動的に更新されます。
Dom\CharacterDataクラスは、テキストノード、コメントノード、CDATAセクションノードなどの特殊なノードタイプを扱うために使用されます。これらのノードは、テキストデータや特殊な文字データを含んでいるため、childNodesプロパティを通じてアクセスできる子ノードを持つ可能性があります。ただし、テキストノード自体は、通常子ノードを持ちません。childNodesプロパティは、ノードの子ノードを反復処理したり、特定のノードを検索したりするために役立ちます。例えば、ループ処理を使用して、NodeList内のすべてのノードに対して特定の操作を実行できます。このプロパティを使用することで、ドキュメントの構造をプログラム的に解析し、操作することが可能になります。
構文(syntax)
1Dom\CharacterData::$childNodes;
引数(parameters)
引数なし
引数はありません
戻り値(return)
DOMNodeList|null
このプロパティは、指定されたノードの子ノードのリスト、または子ノードがない場合は null を返します。
サンプルコード
PHP DOM childNodesによるCharacterDataの振る舞いを理解する
1<?php 2 3/** 4 * Dom\CharacterDataクラスのchildNodesプロパティの使用例です。 5 * 6 * Dom\CharacterDataは、テキストノード(DOMText)やコメントノード(DOMComment)などの 7 * 親クラスにあたります。これらのノードは構造上、子ノードを持つことができません。 8 * そのため、childNodesプロパティにアクセスすると、常にnullが返されます。 9 * 10 * このサンプルコードでは、要素が持つテキストノードとコメントノードを取得し、 11 * それぞれのchildNodesプロパティがnullであることを確認します。 12 */ 13function demonstrateCharacterDataChildNodes(): void 14{ 15 // HTML文字列を定義します。 16 // この<p>タグは、子ノードとして「テキストノード」と「コメントノード」を持っています。 17 $html = '<p>サンプルテキスト<!-- サンプルコメント --></p>'; 18 19 // DOMDocumentオブジェクトを生成し、HTMLを読み込みます。 20 $dom = new \DOMDocument(); 21 22 // HTMLの断片として正しく解析するためのオプションを指定し、libxmlのエラー出力を抑制します。 23 libxml_use_internal_errors(true); 24 $dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); 25 libxml_clear_errors(); 26 27 // 最初の<p>要素を取得します。 28 /** @var \DOMElement $pElement */ 29 $pElement = $dom->getElementsByTagName('p')->item(0); 30 31 // 比較のため、<p>要素が持つ子ノードの数を表示します (2と表示されるはずです)。 32 echo "Parent <p> element has {$pElement->childNodes->length} child nodes." . PHP_EOL; 33 echo '----------------------------------' . PHP_EOL; 34 35 // <p>要素の子ノード(テキストノードとコメントノード)をループ処理します。 36 foreach ($pElement->childNodes as $childNode) { 37 echo 'Node Type: ' . get_class($childNode) . PHP_EOL; 38 39 // ノードがDom\CharacterDataのインスタンスであるかを確認します。 40 if ($childNode instanceof \Dom\CharacterData) { 41 // CharacterData型ノードのchildNodesプロパティを取得します。 42 $grandChildNodes = $childNode->childNodes; 43 44 // プロパティがnullであることを確認し、結果を表示します。 45 if ($grandChildNodes === null) { 46 echo " -> childNodes is null, as expected for CharacterData." . PHP_EOL; 47 } else { 48 // このブロックは通常は実行されません。 49 echo " -> childNodes is not null. (Unexpected)" . PHP_EOL; 50 } 51 } 52 echo PHP_EOL; 53 } 54} 55 56// 関数を実行します。 57demonstrateCharacterDataChildNodes();
PHPのDom\CharacterDataクラスが持つchildNodesプロパティについて解説します。このプロパティは、あるノードが持つ子ノードの一覧を取得するために使用されます。Dom\CharacterDataは、テキストノード(DOMText)やコメントノード(DOMComment)といった、文字データを表現するノードの親となるクラスです。
DOMの構造上、テキストやコメントのようなノードは、それ自体が子ノードを持つことはできません。このルールに基づき、Dom\CharacterDataクラス、およびそれを継承するクラスのインスタンスからchildNodesプロパティにアクセスすると、戻り値は常にnullとなります。引数は必要ありません。
サンプルコードでは、まず<p>タグとその中に含まれるテキストノード、コメントノードを読み込んでいます。次に、ループ処理でこれらのテキストノードとコメントノードを一つずつ取り出し、それぞれのchildNodesプロパティを調べています。どちらのノードもDom\CharacterData型であるため、childNodesプロパティは期待通りnullとなり、その結果が出力されます。この挙動は、要素ノードが子ノードのリスト(DOMNodeList)を返すのとは対照的です。
childNodesプロパティは、アクセスするノードの種類によって戻り値が異なります。DOMElementのような要素ノードでは子ノードのリスト(DOMNodeList)が返されますが、Dom\CharacterDataを継承するテキストノードやコメントノードでは注意が必要です。これらのノードは構造上、子ノードを持つことができないため、childNodesプロパティは常にnullを返します。そのため、このプロパティから返された値を使う前には、必ずnullでないかを確認する処理を記述してください。この確認を怠ると、nullに対してプロパティやメソッドを呼び出そうとしてしまい、プログラムが停止する致命的なエラーの原因となります。
PHP XPathでchildNodesを取得する
1<?php 2 3declare(strict_types=1); 4 5/** 6 * XPathを使用してXML要素を取得し、その子ノードリスト (childNodes) を処理するサンプル。 7 * 8 * この関数は、XMLドキュメントからXPathを使って特定の親ノードを検索し、 9 * そのchildNodesプロパティを通じて全ての子ノードを反復処理します。 10 * 子ノードには、要素ノード(例: <title>)だけでなく、 11 * テキストノード(CharacterDataの一種)やコメントノードも含まれることを示します。 12 */ 13function processChildNodesWithXPath(): void 14{ 15 // サンプルXMLデータ 16 $xmlString = <<<XML 17 <?xml version="1.0" encoding="UTF-8"?> 18 <bookstore> 19 <book category="cooking"> 20 <title lang="en">Everyday Italian</title> 21 <!-- Author information --> 22 <author>Giada De Laurentiis</author> 23 This is a text node. 24 </book> 25 </bookstore> 26 XML; 27 28 // DOMDocumentオブジェクトを生成し、XMLを読み込む 29 $dom = new DOMDocument(); 30 // 空白のテキストノードを無視しないように設定 31 $dom->preserveWhiteSpace = true; 32 $dom->loadXML($xmlString); 33 34 // DOMXPathオブジェクトを生成 35 $xpath = new DOMXPath($dom); 36 37 // XPathクエリで<book>要素を取得する 38 // query()はDOMNodeListを返す 39 $nodes = $xpath->query('//book'); 40 41 // 最初にマッチした<book>要素を取得 42 $bookNode = $nodes?->item(0); 43 44 // ノードが存在し、childNodesプロパティがDOMNodeListを返すことを確認 45 if ($bookNode && $bookNode->childNodes instanceof DOMNodeList) { 46 echo "Found <{$bookNode->nodeName}> element. Processing its child nodes...\n\n"; 47 48 // childNodesプロパティを使って子ノードのリストを反復処理 49 foreach ($bookNode->childNodes as $child) { 50 // ノードの種類に応じて情報を出力 51 switch ($child->nodeType) { 52 case XML_ELEMENT_NODE: // 要素ノード (例: <title>, <author>) 53 printf( 54 "Type: Element, Name: %s, Content: %s\n", 55 $child->nodeName, 56 trim($child->textContent) 57 ); 58 break; 59 case XML_TEXT_NODE: // テキストノード (CharacterDataを継承) 60 if (trim($child->nodeValue) !== '') { 61 printf( 62 "Type: Text, Content: \"%s\"\n", 63 trim($child->nodeValue) 64 ); 65 } 66 break; 67 case XML_COMMENT_NODE: // コメントノード (CharacterDataを継承) 68 printf( 69 "Type: Comment, Content: \"%s\"\n", 70 trim($child->nodeValue) 71 ); 72 break; 73 } 74 } 75 } else { 76 echo "Target node not found.\n"; 77 } 78} 79 80// 関数を実行して結果を表示 81processChildNodesWithXPath();
childNodesは、XMLやHTMLドキュメント内の特定のノード(要素)の直下に含まれる、すべての子ノードのリストを取得するためのプロパティです。このプロパティが返す子ノードには、<title>のような要素だけでなく、要素の間に存在する改行やスペースなどの「テキストノード」や、「コメントノード」もすべて含まれる点が特徴です。
このサンプルコードでは、まずXPathを利用してXMLデータの中から<book>要素を特定し、取得しています。次に、その<book>要素に対してchildNodesプロパティを使い、直下の子ノード群をDOMNodeListオブジェクトとして取得します。そしてforeach文でこのリストを一つずつ取り出し、nodeTypeプロパティでノードの種類を判別しています。種類に応じて、要素名やテキストの内容などを画面に出力することで、どのような子ノードが含まれているかを確認できます。
このプロパティは引数を取らず、戻り値として子ノードのリストであるDOMNodeListを返します。子ノードが存在しない場合でも、空のDOMNodeListが返されるため、安全にループ処理を行えます。ただし、テキストノードやコメントノードのように構造的に子を持てないノードでこのプロパティを参照した場合はnullが返ります。
childNodesプロパティは、要素ノード(タグ)だけでなく、改行やインデントなどの空白を含むテキストノードや、コメントノードもすべて取得します。そのため、要素ノードだけを処理したい場合は、ループ内でnodeTypeプロパティを確認して種類を判別する必要があります。特に、XMLを整形していると意図しない空白テキストノードが含まれることが多いため、サンプルコードのようにtrim関数で中身が空か判別する処理は重要です。また、XPathのqueryメソッドはノードのリストを返し、対象が見つからない場合は空のリストになります。そのため、item(0)で要素を取得する際は、結果がnullになる可能性を考慮し、必ず存在チェックを行ってください。