【PHP8.x】Dom\Node::nextSiblingプロパティの使い方
nextSiblingプロパティの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『nextSiblingプロパティは、DOMツリーにおいて、現在のノードの直後に位置する兄弟ノードを保持するプロパティです。DOMツリーでは、同じ親ノードを持つノード同士を兄弟ノードと呼びます。このプロパティにアクセスすると、現在のノードのすぐ次にある兄弟ノードが Dom\Node オブジェクトとして返されます。もし、現在のノードが親ノードの最後の子ノードであり、次に兄弟ノードが存在しない場合には null を返します。この性質は、while ループなどを用いて、あるノード以降のすべての兄弟ノードを順番に処理する際の終了条件としてよく利用されます。例えば、特定の要素以降のすべての兄弟要素をたどるような処理を実装できます。このプロパティは読み取り専用であり、直接値を代入してDOM構造を変更することはできません。ノードの順序を変更するには insertBefore メソッドなどを使用する必要があります。また、要素ノードだけでなく、要素間の空白文字などが原因で生成されるテキストノードや、コメントノードなども兄弟ノードとして取得される点に注意が必要です。
構文(syntax)
1<?php 2 3$html = '<div><p>段落1</p><p>段落2</p></div>'; 4 5$dom = new DOMDocument(); 6$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); 7 8// 最初の <p> 要素ノードを取得 9$p1 = $dom->getElementsByTagName('p')->item(0); 10 11// nextSibling プロパティで、同じ階層の次のノードを取得 12$siblingNode = $p1->nextSibling; 13 14if ($siblingNode) { 15 // 出力: 段落2 16 echo $siblingNode->textContent; 17} 18 19?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
Dom\Node|null
次に兄弟ノードが存在する場合はそのノードオブジェクトを返します。存在しない場合はnullを返します。
サンプルコード
PHP DOMDocument nextSibling で兄弟ノードをたどる
1<?php 2 3// Dom\Document クラスのインスタンスを作成します。 4// これはHTMLやXMLドキュメントをオブジェクトとして扱うための基盤となります。 5$document = new Dom\Document(); 6 7// 解析するHTMLコンテンツを定義します。 8// nextSiblingプロパティの動作を分かりやすく示すため、 9// コメントノードやテキストノード(要素間の改行や空白)も意図的に含めます。 10$htmlContent = ' 11<!DOCTYPE html> 12<html> 13<body> 14 <div id="container"> 15 <!-- 最初のコメントノード --> 16 <span>最初のスパン要素</span> 17 18 テキストノード(スパンとパラグラフの間) 19 <p>二番目のパラグラフ要素</p> 20 21 <a href="#">三番目のリンク要素</a> 22 <!-- 最後のコメントノード --> 23 </div> 24</body> 25</html>'; 26 27// 定義したHTMLコンテンツをDom\Documentオブジェクトにロード(解析)します。 28$document->loadHTML($htmlContent); 29 30// idが "container" の要素を取得します。 31// これを基点として、子ノードとnextSiblingを探索します。 32$container = $document->getElementById('container'); 33 34if ($container instanceof Dom\Element) { // $containerがDom\Elementのインスタンスであることを確認 35 echo "ID 'container' の要素が見つかりました。\n"; 36 echo "----------------------------------------\n"; 37 38 // 'container' 要素の最初の子ノードを取得します。 39 // nextSiblingプロパティは、このノードの次の兄弟ノードを返します。 40 $currentNode = $container->firstChild; 41 42 // 現在のノードが存在する限り、次の兄弟ノードをたどって情報を出力します。 43 while ($currentNode !== null) { 44 $nodeInfo = "現在のノード: " . ($currentNode->nodeName === '#text' ? 'テキストノード' : $currentNode->nodeName); 45 46 // テキストノードの場合、その内容の一部を表示します。 47 if ($currentNode->nodeName === '#text') { 48 $nodeValue = trim($currentNode->nodeValue); 49 if (!empty($nodeValue)) { // 空白のみのテキストノードは表示をスキップ 50 $nodeInfo .= " (内容: \"" . mb_substr($nodeValue, 0, 20) . (mb_strlen($nodeValue) > 20 ? '...' : '') . "\")"; 51 } else { 52 // 空白のみのテキストノードは次のノードへスキップ 53 $currentNode = $currentNode->nextSibling; 54 continue; 55 } 56 } elseif ($currentNode->nodeName === '#comment') { 57 $nodeInfo .= " (コメント: \"" . mb_substr(trim($currentNode->nodeValue), 0, 20) . (mb_strlen(trim($currentNode->nodeValue)) > 20 ? '...' : '') . "\")"; 58 } else { 59 // 要素ノードの場合、タグ名を表示します。 60 $nodeInfo .= " (タグ: <" . $currentNode->nodeName . ">)"; 61 } 62 echo $nodeInfo . "\n"; 63 64 // nextSiblingプロパティを使用して、現在のノードの次の兄弟ノードを取得します。 65 // 次の兄弟ノードが存在しない場合は null が返されます。 66 $nextNode = $currentNode->nextSibling; 67 68 if ($nextNode === null) { 69 echo " 次の兄弟ノードはありません。\n"; 70 } else { 71 $nextNodeInfo = " 次の兄弟ノード: " . ($nextNode->nodeName === '#text' ? 'テキストノード' : $nextNode->nodeName); 72 if ($nextNode->nodeName === '#text') { 73 $nodeValue = trim($nextNode->nodeValue); 74 if (!empty($nodeValue)) { 75 $nextNodeInfo .= " (内容: \"" . mb_substr($nodeValue, 0, 20) . (mb_strlen($nodeValue) > 20 ? '...' : '') . "\")"; 76 } 77 } elseif ($nextNode->nodeName === '#comment') { 78 $nextNodeInfo .= " (コメント: \"" . mb_substr(trim($nextNode->nodeValue), 0, 20) . (mb_strlen(trim($nextNode->nodeValue)) > 20 ? '...' : '') . "\")"; 79 } else { 80 $nextNodeInfo .= " (タグ: <" . $nextNode->nodeName . ">)"; 81 } 82 echo $nextNodeInfo . "\n"; 83 } 84 echo "----------------------------------------\n"; 85 86 // 次のノードを現在のノードに設定し、ループを続行します。 87 $currentNode = $nextNode; 88 } 89} else { 90 echo "ID 'container' の要素が見つかりませんでした。\n"; 91} 92 93?>
Dom\Node::nextSiblingプロパティは、HTMLやXMLドキュメントをプログラムで操作するDOM(Document Object Model)ツリーにおいて、現在のノードの「次の兄弟ノード」を取得するために使用されます。このプロパティはDom\Nodeクラスに属し、引数は受け取りません。戻り値は、次の兄弟ノードが存在する場合はそのDom\Nodeオブジェクトを、存在しない場合はnullを返します。
サンプルコードでは、まずHTMLコンテンツをDom\Documentオブジェクトにロードし、DOMツリーを構築しています。次に、getElementByIdメソッドで特定の要素(IDが"container"のdiv要素)を取得し、その要素の最初の子ノードをfirstChildプロパティで取得します。その後、whileループの中でnextSiblingプロパティを繰り返し利用しています。これにより、最初のノードから順に、その次の兄弟ノード(要素ノード、テキストノード、コメントノードなどすべての種類)をたどって情報を表示しています。nextSiblingは要素だけでなく、改行や空白文字も含むテキストノードやコメントノードも兄弟ノードとして扱うため、DOMツリーの完全な構造を把握するのに役立ちます。このプロパティを使うことで、DOMツリーを効率的に走査し、ノード間の関係をプログラムで簡単に操作できます。
Dom\Node::nextSiblingプロパティは、HTMLやXMLドキュメント内の要素だけでなく、ソースコードの改行や空白などのテキストノードやコメントノードも兄弟ノードとして返します。要素ノードのみを対象としたい場合は、nodeTypeやnodeNameプロパティでノードの種類を明示的に確認する必要があります。また、兄弟ノードがもう存在しない場合、nextSiblingはnullを返しますので、ループ処理などでは必ずnullチェックを行い、適切に処理を終了させてください。特に、改行やインデントによる空白のみのテキストノードは、trim()関数などを用いて内容を検証しないと、意図しないノードを処理してしまうことがありますので注意が必要です。この機能を利用するには、PHPのDOM拡張が有効になっていることを確認してください。
PHP DOM nextSibling で兄弟ノードを辿る
1<?php 2 3/** 4 * ノードタイプIDに対応する名前を返すヘルパー関数 5 * 6 * @param int $type ノードタイプID (例: XML_ELEMENT_NODE, XML_TEXT_NODE など) 7 * @return string ノードタイプ名 8 */ 9function getNodeTypeName(int $type): string 10{ 11 return match ($type) { 12 XML_ELEMENT_NODE => '要素ノード', 13 XML_ATTRIBUTE_NODE => '属性ノード', 14 XML_TEXT_NODE => 'テキストノード', 15 XML_CDATA_SECTION_NODE => 'CDATAセクションノード', 16 XML_ENTITY_REF_NODE => 'エンティティ参照ノード', 17 XML_ENTITY_NODE => 'エンティティノード', 18 XML_PI_NODE => '処理命令ノード', 19 XML_COMMENT_NODE => 'コメントノード', 20 XML_DOCUMENT_NODE => 'ドキュメントノード', 21 XML_DOCUMENT_TYPE_NODE => 'ドキュメントタイプノード', 22 XML_DOCUMENT_FRAG_NODE => 'ドキュメントフラグメントノード', 23 XML_NOTATION_NODE => '記法ノード', 24 default => '不明なノード', 25 }; 26} 27 28/** 29 * PHPのDom\Node::nextSibling プロパティの使用方法をデモンストレーションします。 30 * 31 * nextSibling プロパティは、現在のノードの次の兄弟ノードを返します。 32 * 兄弟ノードが存在しない場合は null を返します。 33 */ 34function demonstrateDomNextSibling(): void 35{ 36 // 1. サンプルとなるHTML文字列を作成します。 37 // 異なる種類のノード (要素、コメント、テキスト) が含まれるようにします。 38 // 要素間の改行やインデントもテキストノードとして扱われる点に注意してください。 39 $html = <<<HTML 40 <div id="container"> 41 <p>最初のパラグラフ</p> 42 <!-- これはコメントノードです --> 43 <span>スパン要素</span> 44 これは要素ではないテキストノードです。 45 <a href="https://example.com">リンク</a> 46 </div> 47 HTML; 48 49 echo "--- Dom\\Node::nextSibling プロパティのデモンストレーション ---" . PHP_EOL . PHP_EOL; 50 51 // 2. DOMDocument オブジェクトを作成し、HTMLをロードします。 52 $dom = new DOMDocument(); 53 // HTML5互換でないHTMLのパースエラーを抑制するために、エラーハンドリングを一時的に無効にします。 54 libxml_use_internal_errors(true); 55 $dom->loadHTML($html); 56 libxml_clear_errors(); // エラーをクリアします。 57 58 // 3. ID 'container' を持つ親要素を取得します。 59 // この要素の子ノードを辿って nextSibling を使用します。 60 $container = $dom->getElementById('container'); 61 62 if (!$container) { 63 echo "エラー: 'container' 要素が見つかりませんでした。" . PHP_EOL; 64 return; 65 } 66 67 echo "親要素: '{$container->nodeName}' の子ノードを辿ります。" . PHP_EOL; 68 echo "----------------------------------------------------" . PHP_EOL; 69 70 // 4. 親要素の最初の子ノードから開始し、nextSibling プロパティを使って兄弟ノードを順に辿ります。 71 // PHP 8 では、DOMノード関連のクラスは Dom\Node インターフェースを実装しています。 72 // nextSibling は Dom\Node または null を返します。 73 $currentNode = $container->firstChild; 74 $nodeIndex = 0; 75 76 while ($currentNode instanceof Dom\Node) { 77 $nodeIndex++; 78 echo "ノード {$nodeIndex}:" . PHP_EOL; 79 echo " - タイプ: " . getNodeTypeName($currentNode->nodeType) . " ({$currentNode->nodeType})" . PHP_EOL; 80 echo " - 名前: " . ($currentNode->nodeName === '#text' ? '#text (テキストノード)' : $currentNode->nodeName) . PHP_EOL; 81 // テキストノードの場合、nodeValueには内容が含まれます。 82 // 要素ノードの場合、nodeValueは通常空です。 83 $nodeValueDisplay = trim($currentNode->nodeValue); 84 if (mb_strlen($nodeValueDisplay) > 50) { 85 $nodeValueDisplay = mb_substr($nodeValueDisplay, 0, 50) . '...'; 86 } 87 echo " - 値: " . (empty($nodeValueDisplay) ? '[なし]' : $nodeValueDisplay) . PHP_EOL; 88 echo "----------------------------------------------------" . PHP_EOL; 89 90 // 次の兄弟ノードへ移動します。兄弟ノードがなければ null を返します。 91 $currentNode = $currentNode->nextSibling; 92 } 93 94 if ($nodeIndex === 0) { 95 echo "子ノードが見つかりませんでした。" . PHP_EOL; 96 } 97} 98 99// スクリプトの実行 100demonstrateDomNextSibling(); 101 102?>
Dom\NodeクラスのnextSiblingプロパティは、現在のノードの次の兄弟ノードを取得するために使用します。兄弟ノードが存在する場合、Dom\Nodeオブジェクトが返されます。もし次の兄弟ノードが存在しない場合、nullが返されます。
サンプルコードでは、まずHTML文字列をDOMDocumentオブジェクトに読み込みます。次に、getElementByIdメソッドで特定のIDを持つ要素(この例ではcontainer)を取得します。container要素が存在すれば、firstChildプロパティで最初の子ノードを取得し、そこからnextSiblingプロパティを使って兄弟ノードを順番に辿ります。各ノードについて、ノードタイプ、ノード名、ノード値を表示します。この処理をnextSiblingがnullを返すまで繰り返すことで、container要素の子ノードをすべて列挙しています。この例では、要素ノード、テキストノード、コメントノードが混在したHTMLを扱うことで、nextSiblingプロパティの挙動を理解しやすくしています。
nextSiblingは、指定した要素の直後の兄弟要素を取得するプロパティです。兄弟要素が存在しない場合、nullを返します。HTMLを扱う際、改行やインデントもテキストノードとして認識されるため、予期せぬノードが返されることがあります。loadHTML関数を使用する際は、HTML5非互換のエラーが発生することがあります。libxml_use_internal_errors(true)でエラーを抑制し、libxml_clear_errors()でエラーをクリアすることで、安全に処理できます。取得したノードのタイプをnodeTypeで確認し、テキストノードの場合はnodeValueで内容を取得できます。要素ノードの場合は、nodeValueは通常空です。