【PHP8.x】Dom\Node::childNodesプロパティの使い方
childNodesプロパティの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『childNodesプロパティは、あるノードの直接の子である全ての子ノードの集合を、Dom\NodeListオブジェクトとして保持するプロパティです。このプロパティは読み取り専用であり、DOMツリーの構造を調べる際に使用されます。返されるDom\NodeListは、子ノードの順序付きコレクションであり、配列のようにインデックス番号を用いて各子ノードにアクセスできます。子ノードには、HTMLタグなどの要素ノードだけでなく、テキスト、コメント、処理命令など、あらゆる種類のノードが含まれる点に注意が必要です。例えば、要素間の改行やインデントに使われる空白文字もテキストノードとして扱われることがあります。また、childNodesが返すNodeListは「ライブ」な状態です。これは、元のDOMツリーに対してappendChildメソッドなどで子ノードが追加または削除されると、その変更がNodeListオブジェクトにも自動的に反映されることを意味します。もし対象のノードに子ノードが一つも存在しない場合は、空のDom\NodeListが返されます。
構文(syntax)
1<?php 2$dom = new DOMDocument(); 3$dom->loadHTML('<html><body><p>段落1</p>テキストノード<!-- コメント --></div></body></html>', LIBXML_NOEMPTYTAG); 4 5// body要素 (Dom\Node) を取得します 6$body = $dom->getElementsByTagName('body')->item(0); 7 8// body要素のすべての子ノード (Dom\NodeList) を取得します 9$childNodes = $body->childNodes; 10 11// すべての子ノードをループ処理して、そのノード名を出力します 12foreach ($childNodes as $node) { 13 // $nodeはDom\Nodeオブジェクトです 14 echo $node->nodeName . PHP_EOL; 15} 16?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
?Dom\NodeList
Dom\Nodeオブジェクトの子ノードのコレクションをDom\NodeListオブジェクトとして返します。子ノードが存在しない場合はnullを返します。
サンプルコード
PHP 8 Dom childNodesで子ノードを取得する
1<?php 2 3/** 4 * Demonstrates the use of the Dom\Node::childNodes property in PHP 8. 5 * 6 * This function parses a simple HTML string, identifies a specific parent element, 7 * and then iterates through its direct child nodes using the `childNodes` property. 8 * It shows how to access different types of nodes (element, text, comment) that 9 * can be returned by this property. 10 */ 11function demonstrateDomChildNodes(): void 12{ 13 // 1. Create a new Dom\Document instance. 14 $document = new Dom\Document(); 15 16 // 2. Load a simple HTML string into the document. 17 // The whitespace between elements (like newline characters) will also be treated as text nodes. 18 $html = <<<HTML 19<!DOCTYPE html> 20<html> 21<head> 22 <title>ChildNodes Example</title> 23</head> 24<body> 25 <div id="main-container"> 26 <!-- This is a comment inside the container --> 27 <p>This is the first paragraph child.</p> 28 Some direct text content. 29 <span>This is a span element child.</span> 30 </div> 31</body> 32</html> 33HTML; 34 $document->loadHTML($html); 35 36 // 3. Find the parent node whose children we want to examine. 37 // We'll get the div element with id="main-container". 38 $containerNode = $document->getElementById('main-container'); 39 40 if ($containerNode === null) { 41 echo "Error: Could not find the '#main-container' element.\n"; 42 return; 43 } 44 45 echo "--- Examining childNodes of the '{$containerNode->nodeName}' element (ID: {$containerNode->getAttribute('id')}) ---\n\n"; 46 47 // 4. Access the `childNodes` property. 48 // This property returns a `Dom\NodeList` containing all direct children of the node, 49 // including element nodes, text nodes, and comment nodes. 50 // The return type is `?Dom\NodeList`, meaning it could theoretically be null, 51 // but for an existing node, it typically returns an empty `Dom\NodeList` if there are no children. 52 $childNodes = $containerNode->childNodes; 53 54 // Check if the property returned a Dom\NodeList. 55 if ($childNodes === null) { 56 echo "The childNodes property returned null. This is unexpected for an existing node.\n"; 57 return; 58 } 59 60 // 5. Iterate through the `Dom\NodeList` and display information about each child node. 61 if ($childNodes->length > 0) { 62 echo "Found {$childNodes->length} direct child nodes:\n"; 63 foreach ($childNodes as $index => $node) { 64 // Determine the human-readable type of the node. 65 $nodeTypeName = match ($node->nodeType) { 66 XML_ELEMENT_NODE => 'ELEMENT_NODE', 67 XML_TEXT_NODE => 'TEXT_NODE', 68 XML_COMMENT_NODE => 'COMMENT_NODE', 69 XML_CDATA_SECTION_NODE => 'CDATA_SECTION_NODE', 70 XML_DOCUMENT_TYPE_NODE => 'DOCUMENT_TYPE_NODE', 71 default => 'UNKNOWN_NODE_TYPE (' . $node->nodeType . ')', 72 }; 73 74 echo " [$index] Type: {$nodeTypeName} (Code: {$node->nodeType})"; 75 echo ", Name: '{$node->nodeName}'"; 76 77 // Truncate long text values for better readability and clean up whitespace. 78 $valuePreview = str_replace(["\n", "\r"], ' ', $node->nodeValue); 79 $valuePreview = preg_replace('/\s+/', ' ', $valuePreview); // Replace multiple spaces with single space 80 $valuePreview = trim($valuePreview); 81 82 if (strlen($valuePreview) > 50) { 83 $valuePreview = substr($valuePreview, 0, 50) . '...'; 84 } 85 echo ", Value (Preview): '{$valuePreview}'\n"; 86 } 87 } else { 88 echo "No direct child nodes found for this element.\n"; 89 } 90} 91 92// Execute the demonstration function. 93demonstrateDomChildNodes(); 94 95?>
PHP 8で提供されるDom\Node::childNodesは、DOM(Document Object Model)ツリーを操作する際に、特定のノードが直接持つ全ての子ノードのリストを取得するためのプロパティです。これはDom\Nodeクラスの一部であり、HTMLやXML文書の構造をプログラムから詳細に扱う際に活用されます。
このプロパティは引数を取りません。戻り値は?Dom\NodeListで、これは取得された全ての子ノードを順序付けて格納するリスト形式のオブジェクトです。戻り値が?(Nullable)と示されているのは、理論上nullを返す可能性があるためですが、通常、既存のノードに対しては、子ノードが存在しない場合でも空のDom\NodeListオブジェクトが返されます。
サンプルコードでは、まず簡単なHTML文字列をDom\Documentに読み込み、特定のdiv要素を親ノードとして特定しています。その後、この親ノードのchildNodesプロパティにアクセスすることで、その直下にある全ての子ノード(要素ノード、テキストノード、コメントノードなど)のリストを取得しています。そして、取得したDom\NodeListをループ処理し、各子ノードのタイプ(要素、テキスト、コメントなど)や内容(nodeValue)を出力することで、DOMツリーがどのように構築され、どのような情報を含んでいるのかを具体的に理解できるようになっています。特に、要素間の改行や空白もテキストノードとして認識される点に注目してください。
Dom\Node::childNodesプロパティは、HTMLやXML文書の特定のノード直下にある全ての子ノードを取得します。初心者が特に注意すべき点は、HTMLソース中の改行やインデントといった空白もテキストノード(XML_TEXT_NODE)として含まれることです。そのため、期待する要素ノード(XML_ELEMENT_NODE)のみを扱いたい場合は、取得したノードリストをループ処理し、nodeTypeプロパティでノードの種類を判別し、明示的にフィルタリングする必要があります。
また、このプロパティは通常Dom\NodeListオブジェクトを返しますが、理論的にはnullを返す可能性もゼロではありません。安全なコードのため、取得したノードリストがnullでないか確認するチェックを行うことが推奨されます。取得したDom\NodeListは、lengthプロパティで子ノードの数を取得でき、foreach文で各子ノードに簡単にアクセスして処理できます。
PHP DOM childNodesで要素の子ノードを列挙する
1<?php 2 3/** 4 * ノードタイプに対応する説明文字列を返します。 5 * システムエンジニアを目指す初心者向けに、各ノードがどのような種類かを示します。 6 * 7 * @param int $type XML_ELEMENT_NODEなどのノードタイプ定数。 8 * @return string ノードタイプの分かりやすい説明。 9 */ 10function getNodeTypeDescription(int $type): string 11{ 12 // PHP 8以降で利用可能なmatch式を使って、ノードタイプに応じて説明を返します。 13 return match ($type) { 14 XML_ELEMENT_NODE => '要素ノード (例: <div>, <p>, <h1>)', 15 XML_ATTRIBUTE_NODE => '属性ノード (例: href="...", src="...") - childNodesには直接含まれません', 16 XML_TEXT_NODE => 'テキストノード (要素間の文字列や空白)', 17 XML_CDATA_SECTION_NODE => 'CDATAセクションノード', 18 XML_ENTITY_REF_NODE => '実体参照ノード', 19 XML_ENTITY_NODE => '実体ノード - childNodesには直接含まれません', 20 XML_PI_NODE => '処理命令ノード (例: <?php ... ?>)', 21 XML_COMMENT_NODE => 'コメントノード (例: <!-- コメント -->)', 22 XML_DOCUMENT_NODE => 'ドキュメントノード (DOM全体のルート)', 23 XML_DOCUMENT_TYPE_NODE => 'DOCTYPEノード (例: <!DOCTYPE html>)', 24 XML_DOCUMENT_FRAG_NODE => 'ドキュメントフラグメントノード', 25 XML_NOTATION_NODE => '記法ノード - childNodesには直接含まれません', 26 default => '不明なノード', 27 }; 28} 29 30/** 31 * 指定されたHTML文字列をパースし、特定のXPathセレクタにマッチする最初の要素の子ノードを列挙します。 32 * Dom\NodeクラスのchildNodesプロパティの使用例を示します。 33 * 34 * @param string $html HTML文字列。 35 * @param string $selector 子ノードを列挙したい要素を特定するためのXPathセレクタ。 36 * 例: '/html/body' は <body> 要素を指します。 37 * @return void 38 */ 39function displayChildNodesExample(string $html, string $selector): void 40{ 41 // DOMDocument オブジェクトを作成します。 42 // このクラスはHTMLやXMLドキュメントをオブジェクトとして扱うための基本的なクラスです。 43 // PHP 8ではDom\Documentが新しい名前空間ですが、DOMDocumentはDom\Documentを継承しており、 44 // 広く利用されているため、初心者にはこちらが推奨されます。 45 $dom = new DOMDocument(); 46 47 // HTMLをロードします。 48 // LIBXML_HTML_NOIMPLIED: HTML、HEAD、BODYタグが自動追加されるのを防ぎます。 49 // LIBXML_HTML_NODEFDTD: デフォルトのDTDが自動追加されるのを防ぎます。 50 // @ を付けてエラー出力を抑制し、後でlibxml_get_errors()でエラーを確認します。 51 @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); 52 53 // HTMLパース中に発生したエラーをチェックします。 54 if (libxml_get_errors()) { 55 echo "エラー: HTMLのパース中に問題が発生しました。\n"; 56 libxml_clear_errors(); // エラーバッファをクリアします。 57 return; 58 } 59 60 // DOMXPath オブジェクトを作成し、XPathを使って目的のノードを検索します。 61 // XPathはXMLドキュメントの特定の部分を選択するための言語です。 62 $xpath = new DOMXPath($dom); 63 $nodes = $xpath->query($selector); 64 65 // 指定されたセレクタに一致するノードが見つからなかった場合。 66 if ($nodes->length === 0) { 67 echo "情報: セレクタ '{$selector}' に一致するノードが見つかりませんでした。\n"; 68 return; 69 } 70 71 // 最初のマッチノードを取得します。 72 // XPathクエリは複数のノードを返すことがありますが、ここでは最初のノードに焦点を当てます。 73 $targetNode = $nodes->item(0); 74 75 // ターゲットノードが有効でない場合。 76 if (!$targetNode) { 77 echo "エラー: ターゲットノードの取得に失敗しました。\n"; 78 return; 79 } 80 81 echo "--- ノード '{$selector}' の子ノード一覧 ---\n"; 82 83 // Dom\Node::childNodes プロパティは、そのノードのすべての子ノードを含む Dom\NodeList を返します。 84 // Dom\NodeList は Traversable インターフェースを実装しているため、foreachループで直接反復できます。 85 if ($targetNode->childNodes === null || $targetNode->childNodes->length === 0) { 86 echo " 子ノードはありません。\n"; 87 return; 88 } 89 90 foreach ($targetNode->childNodes as $index => $childNode) { 91 // 各子ノードの情報を出力します。 92 // nodeType: ノードの種類 (例: 1は要素、3はテキスト、8はコメント)。 93 // nodeName: 要素名 (例: 'div', 'p') または特殊な名前 ('#text', '#comment')。 94 // nodeValue: テキストノードの値やコメントの内容など。要素ノードでは通常空です。 95 96 // nodeValueが長い場合は最初の50文字に切り詰めます。 97 // 空白のみのテキストノードはtrim()で取り除き、表示を簡潔にします。 98 $nodeValueDisplay = trim($childNode->nodeValue ?? ''); 99 if (!empty($nodeValueDisplay)) { 100 $nodeValueDisplay = mb_substr($nodeValueDisplay, 0, 50) . (mb_strlen($nodeValueDisplay) > 50 ? '...' : ''); 101 $nodeValueDisplay = " (値: '{$nodeValueDisplay}')"; 102 } else { 103 $nodeValueDisplay = ""; 104 } 105 106 echo sprintf( 107 " [%d] タイプ: %d (%s), 名前: '%s'%s\n", 108 $index, 109 $childNode->nodeType, 110 getNodeTypeDescription($childNode->nodeType), 111 $childNode->nodeName, 112 $nodeValueDisplay 113 ); 114 } 115} 116 117// --- サンプルコードの実行 --- 118 119// 複雑なHTMLの例 120$sampleHtml = <<<HTML 121<!DOCTYPE html> 122<html> 123<head> 124 <title>PHP DOMサンプル</title> 125</head> 126<body> 127 <h1>DOM操作の基本</h1> 128 <p>これは<em>子ノード</em>の<strong id="strong-element">例</strong>です。</p> 129 <!-- これはHTMLコメントです --> 130 <div> 131 <span>ネストされた要素</span> 132 <br/> 133 テキストが<span>続きます</span>。 134 </div> 135 空白や改行もテキストノードとして扱われます。 136</body> 137</html> 138HTML; 139 140// 1. <body>要素の子ノードを列挙します。 141// loadHTMLは通常<html><body>を補完するため、/html/bodyでアクセスします。 142displayChildNodesExample($sampleHtml, '/html/body'); 143 144echo "\n----------------------------------------\n\n"; 145 146// 2. <p>要素の子ノードを列挙します。 147// テキストノード、<em>要素、<strong>要素などが含まれることが分かります。 148displayChildNodesExample($sampleHtml, '/html/body/p'); 149 150echo "\n----------------------------------------\n\n"; 151 152// 3. 子ノードを持たない要素の例 (<img>はvoid要素です)。 153$htmlWithImg = '<!DOCTYPE html><html><body><div><img src="example.jpg" alt="test"></div></body></html>'; 154displayChildNodesExample($htmlWithImg, '/html/body/div/img'); 155 156echo "\n----------------------------------------\n\n"; 157 158// 4. 空の<div>要素の子ノードを列挙します。 159$emptyDivHtml = '<!DOCTYPE html><html><body><div></div></body></html>'; 160displayChildNodesExample($emptyDivHtml, '/html/body/div'); 161 162?>
Dom\Node::childNodesプロパティは、HTMLやXMLドキュメントの構造を表現するDom\Nodeオブジェクトに属し、そのノードの直接の子ノードのリストを取得するために使用されます。このプロパティは引数を持ちません。戻り値は?Dom\NodeList型で、対象ノードの子ノードが順序付けられたリストとして含まれます。子ノードが存在しない場合は空のDom\NodeListが返されます。
サンプルコードでは、HTML文字列をDOMDocumentにロードしてDOMツリーを構築した後、DOMXPathを使って特定の要素を検索し、その要素のchildNodesプロパティにアクセスしています。これにより取得されるDom\NodeListは、foreachループで反復処理が可能です。リストには、他の要素ノードだけでなく、要素間のテキストを表すテキストノード、HTMLコメントノードなども含まれます。
コードは、各子ノードのnodeType(ノードの種類)、nodeName(名前)、nodeValue(値)を出力することで、DOMツリー内の各ノードの詳細な情報を確認する方法を示しています。childNodesプロパティは、DOMツリーを探索し、特定の要素の子要素やテキスト、コメントなど、ドキュメントのあらゆる内容にアクセスし、操作する上で非常に重要な役割を果たします。
Dom\Node::childNodesは、要素だけでなく空白や改行を含むテキストノード、コメントノードなど、そのノードのすべての子ノードをDom\NodeListとして返します。特にHTMLソース上の空白や改行が、DOM上では意図しないテキストノードとして含まれることがある点にご注意ください。属性ノードはchildNodesには含まれず、別途attributesプロパティで取得する必要があります。childNodesは子ノードがない場合でも空のDom\NodeListを返すため、lengthプロパティで個数を判断するのが確実です。サンプルコードにおける@演算子でのエラー抑制は、その後のlibxml_get_errors()による明示的なエラーチェックと合わせて利用するようにしてください。
PHP: XPathでchildNodesを取得する
1<?php 2 3/** 4 * Dom\Node::$childNodes プロパティの使用方法をデモンストレーションする関数。 5 * 6 * この関数は、HTML文字列からDOMドキュメントを構築し、XPathを使って特定の親ノードを選択します。 7 * その後、選択した親ノードの childNodes プロパティを使用して、直接の子ノード全てを取得し、 8 * それぞれの子ノードの型と内容を表示します。 9 * システムエンジニアを目指す初心者向けに、DOM操作とXPathの基本的な連携を示します。 10 */ 11function demonstrateChildNodesProperty(): void 12{ 13 // 処理対象となる簡単なHTML文字列を定義します。 14 $htmlContent = <<<HTML 15<div> 16 <p>これは最初のパラグラフです。</p> 17 <!-- これはHTMLコメントです --> 18 <span>これはスパン要素です。</span> 19 テキストノードの例。 20</div> 21HTML; 22 23 // DOMDocument オブジェクトを作成し、HTMLを読み込みます。 24 // loadHTML() で発生する可能性のある警告を抑制し、内部でエラーを処理するように設定します。 25 libxml_use_internal_errors(true); 26 $dom = new DOMDocument(); 27 $dom->loadHTML($htmlContent); 28 libxml_clear_errors(); // エラー処理設定後、前のエラーをクリアします。 29 30 // DOMXPath オブジェクトを作成し、DOMDocument に関連付けます。 31 // これにより、XPathクエリを使用してDOM内のノードを検索できるようになります。 32 $xpath = new DOMXPath($dom); 33 34 // XPathクエリ '//div' を使用して、ドキュメント内の最初の 'div' 要素を検索します。 35 // この 'div' 要素を、子ノードを取得する「親ノード」として扱います。 36 $divNodes = $xpath->query('//div'); 37 38 // 'div' 要素が見つかった場合のみ、処理を続行します。 39 if ($divNodes->length > 0) { 40 /** @var Dom\Node $parentNode 取得したノードリストの最初の要素(div) */ 41 $parentNode = $divNodes->item(0); 42 43 echo "--- 親ノード (タグ名: '{$parentNode->nodeName}') の直接の子ノード一覧 ---\n"; 44 45 // Dom\Node::$childNodes プロパティを使用して、親ノードの直接の子ノード全てを取得します。 46 // 戻り値は Dom\NodeList オブジェクトです。 47 // このリストには、要素ノード、テキストノード、コメントノードなどが含まれます。 48 $childNodes = $parentNode->childNodes; 49 50 // 取得した Dom\NodeList をループし、各子ノードの情報を表示します。 51 foreach ($childNodes as $index => $childNode) { 52 echo " [$index] "; // ノードのインデックスを表示 53 54 // ノードのタイプによって情報を出力します。 55 if ($childNode instanceof Dom\Element) { 56 // 要素ノードの場合(例: <p>, <span>) 57 echo "型: 要素ノード ('{$childNode->tagName}')"; 58 echo ", 内容: '" . trim($childNode->textContent) . "'"; 59 } elseif ($childNode instanceof Dom\Text) { 60 // テキストノードの場合 61 $trimmedText = trim($childNode->nodeValue); 62 if ($trimmedText !== '') { // 空白のみのテキストノードはスキップ 63 echo "型: テキストノード"; 64 echo ", 内容: '" . $trimmedText . "'"; 65 } else { 66 // 整形のための改行や空白だけのテキストノードは「空白テキストノード」として表示 67 echo "型: 空白テキストノード (整形用)"; 68 } 69 } elseif ($childNode instanceof Dom\Comment) { 70 // コメントノードの場合 71 echo "型: コメントノード"; 72 echo ", 内容: '<!-- " . trim($childNode->nodeValue) . " -->'"; 73 } else { 74 // その他のノードタイプの場合 75 echo "型: その他のノード ({$childNode->nodeName})"; 76 } 77 echo "\n"; // 各ノードの情報の終わりに改行 78 } 79 } else { 80 echo "指定された 'div' 要素がHTMLドキュメントに見つかりませんでした。\n"; 81 } 82} 83 84// 関数を実行して、Dom\Node::$childNodes プロパティの動作を確認します。 85demonstrateChildNodesProperty();
Dom\Node::$childNodesプロパティは、PHPでHTMLやXMLの文書構造を扱う際に、特定のノード(要素)の直接の子ノードをすべて取得するために使用されます。
このサンプルコードでは、まず指定されたHTML文字列をDOMDocumentとして読み込み、DOMXPathというツールを使って文書内から特定のdiv要素を見つけ出します。このdiv要素が、子ノードを取得したい「親ノード」となります。
次に、この親ノードに対して$parentNode->childNodesとアクセスすることで、その直下にあるすべての子ノードをDom\NodeListというリスト形式で取得します。このプロパティは引数をとりません。
取得されるDom\NodeListには、<p>や<span>のようなHTMLタグで囲まれた「要素ノード」のほか、タグ間に存在する改行や空白、実際のテキスト内容を表す「テキストノード」、そして<!-- ... -->のような「コメントノード」など、様々な種類のノードが含まれます。サンプルコードでは、これらの子ノードを一つずつ確認し、その種類と内容を表示することで、childNodesプロパティがどのようなノードを返すかを示しています。
戻り値の?Dom\NodeListは、子ノードが存在しない場合や特殊な状況でnullを返す可能性があることを示しており、柔軟なエラーハンドリングが可能です。このプロパティを使うことで、システムのHTML解析処理において、特定のノードの子要素を効率的かつ網羅的に取得し、その構造を理解したり操作したりすることが可能になります。
Dom\Node::$childNodes プロパティは、指定した親ノードの直下にある全ての子ノードを Dom\NodeList オブジェクトとして返します。このリストには、<p>や<span>のような要素ノードだけでなく、要素間の文字列であるテキストノードやHTMLコメントノードも含まれる点にご注意ください。特に、HTMLソースコードの整形のために挿入された改行や空白もテキストノードとして扱われるため、意図しない空白ノードを処理しないよう、textContent や nodeValue を trim() 関数などで確認することが重要です。このプロパティは直接の子ノードのみを対象としますので、ドキュメント全体から複雑な条件でノードを探す場合は、DOMXPath を利用して対象ノードを特定し、そのノードに対して childNodes プロパティを使用する組み合わせが効果的です。