【PHP8.x】Dom\Document::previousSiblingプロパティの使い方
previousSiblingプロパティの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『previousSiblingプロパティは、現在のノードの直前に位置する兄弟ノードを保持するプロパティです。このプロパティは、DOMツリーを構成するすべてのノードの基本となるDom\Nodeクラスで定義されており、Dom\Documentクラスはこれを継承しています。しかし、Dom\DocumentオブジェクトはXMLやHTML文書全体を表すルートノードであり、DOMツリーの最上位に位置するという特別な役割を持ちます。定義上、ルートノードには親ノードが存在しません。兄弟ノードとは、同じ親ノードを持つノード同士の関係を指すため、親を持たないDom\Documentオブジェクトには兄弟ノードも存在し得ません。したがって、Dom\DocumentオブジェクトのpreviousSiblingプロパティにアクセスした場合、その値は常にnullとなります。他の種類の子ノード、例えばDom\ElementやDom\Textなどでは、このプロパティを使って文書構造内での前の要素を辿ることができますが、文書全体を表すDom\Documentにおいては、このプロパティが具体的なノードを指すことはありません。
構文(syntax)
1<?php 2 3$doc = new Dom\Document(); 4$doc->loadXML('<root><item-a/><item-b/></root>'); 5 6$itemB = $doc->getElementsByTagName('item-b')->item(0); 7 8// item-b の直前の兄弟ノードを取得します 9$previousNode = $itemB->previousSibling; 10 11// 取得したノードのノード名を出力します 12echo $previousNode->nodeName; // 出力: item-a 13 14?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
Dom\Node|null
このプロパティは、現在のノードの直前の兄弟ノードを返します。兄弟ノードがない場合は null を返します。
サンプルコード
PHP Dom\Node::$previousSibling で兄弟ノードを取得する
1<?php 2 3/** 4 * Dom\Node::$previousSibling プロパティの使用方法をデモンストレーションします。 5 * 6 * このプロパティは、現在のノードの親の childNodes リストにおける直前の兄弟ノードを返します。 7 * 直前の兄弟ノードが存在しない場合 (つまり、現在のノードが最初の子である場合) は null を返します。 8 * 9 * システムエンジニアを目指す初心者の方へ: 10 * ウェブページやXMLデータなどの構造化されたドキュメントを扱う際、 11 * 各要素(ノード)には親子関係や兄弟関係があります。 12 * previousSibling は「自分の一つ前にある兄弟ノード」を取得するために使われます。 13 * 例えば、HTMLリストの2番目の項目から1番目の項目に移動する、といった場合に役立ちます。 14 */ 15function demonstratePreviousSibling(): void 16{ 17 // 1. 新しい DOMDocument オブジェクトを作成します。 18 // PHP 8 の DOM 拡張を使用します。 19 $dom = new DOMDocument('1.0', 'UTF-8'); 20 // 出力時に整形を有効にします。 21 $dom->formatOutput = true; 22 23 // 2. 簡単なXML構造を読み込みます。 24 // 要素間に空白(改行やインデント)を入れないことで、 25 // previousSibling が意図した要素ノードを直接返すようにします。 26 // (通常、XML/HTMLをファイルから読み込むと、空白もテキストノードとして扱われることがあります。) 27 $xmlString = '<root><item id="first">最初の要素</item><item id="second">二番目の要素</item><item id="third">三番目の要素</item></root>'; 28 $dom->loadXML($xmlString); 29 30 echo "--- DOM構造 ---\n"; 31 echo $dom->saveXML(); 32 echo "\n"; 33 34 // 3. IDが "second" の要素(<item id="second">)を取得します。 35 // getElementsByTagName を使用し、ID属性でフィルタリングします。 36 $targetNode = null; 37 $items = $dom->getElementsByTagName('item'); 38 foreach ($items as $item) { 39 if ($item->getAttribute('id') === 'second') { 40 $targetNode = $item; 41 break; 42 } 43 } 44 45 if ($targetNode instanceof DOMElement) { 46 echo "現在の要素 (ID: " . $targetNode->getAttribute('id') . "): " . $targetNode->nodeName . " (内容: " . $targetNode->textContent . ")\n"; 47 48 // 4. previousSibling プロパティを使って直前の兄弟ノードを取得します。 49 // この場合、<item id="first"> が返されるはずです。 50 $previousSibling = $targetNode->previousSibling; 51 52 if ($previousSibling instanceof DOMNode) { 53 echo " 直前の兄弟ノード: " . $previousSibling->nodeName . 54 " (タイプ: " . $previousSibling->nodeType . 55 ", 内容: " . $previousSibling->textContent . ")\n"; 56 } else { 57 echo " 直前の兄弟ノードは見つかりませんでした。\n"; 58 } 59 echo "\n"; 60 } else { 61 echo "ID 'second' の要素が見つかりませんでした。\n\n"; 62 } 63 64 // 5. IDが "first" の要素(<item id="first">)を取得します。 65 $firstNode = null; 66 foreach ($items as $item) { 67 if ($item->getAttribute('id') === 'first') { 68 $firstNode = $item; 69 break; 70 } 71 } 72 73 if ($firstNode instanceof DOMElement) { 74 echo "現在の要素 (ID: " . $firstNode->getAttribute('id') . "): " . $firstNode->nodeName . " (内容: " . $firstNode->textContent . ")\n"; 75 76 // 6. firstNode の previousSibling を取得します。 77 // firstNode は親ノード(<root>)の最初の子であるため、 78 // 直前の兄弟ノードは存在せず、null を返します。 79 $previousSiblingOfFirst = $firstNode->previousSibling; 80 81 if ($previousSiblingOfFirst instanceof DOMNode) { 82 echo " 直前の兄弟ノード: " . $previousSiblingOfFirst->nodeName . 83 " (タイプ: " . $previousSiblingOfFirst->nodeType . 84 ", 内容: " . $previousSiblingOfFirst->textContent . ")\n"; 85 } else { 86 echo " 直前の兄弟ノードは見つかりませんでした (期待通り)。\n"; 87 } 88 } else { 89 echo "ID 'first' の要素が見つかりませんでした。\n"; 90 } 91} 92 93// 関数を実行してデモンストレーションを開始します。 94demonstratePreviousSibling(); 95
PHP 8のDom\Node::previousSiblingプロパティは、XMLやHTMLなどの構造化されたドキュメントを扱う際に、現在選択しているノードの直前にある兄弟ノードを取得するために使用されます。このプロパティに引数はなく、戻り値は直前の兄弟ノードを表すDom\Nodeオブジェクト、または直前の兄弟ノードが存在しない場合はnullとなります。
サンプルコードでは、まずシンプルなXML文字列を解析してDOM構造を構築しています。次に、IDが"second"の<item>要素を取得し、そのpreviousSiblingプロパティにアクセスすることで、直前のIDが"first"の<item>要素を取得する具体的な例を示しています。これにより、現在のノードから前のノードへ移動する仕組みを理解できます。
また、親ノードの最初の子であるIDが"first"の<item>要素に対してpreviousSiblingを使用すると、直前の兄弟ノードが存在しないためnullが返される挙動も確認できます。これは、ドキュメント構造の端に到達したことを示します。previousSiblingは、要素間の空白や改行もテキストノードとして認識することがあるため、目的のノードタイプであることをinstanceof DOMElementなどで確認することが重要です。
previousSiblingプロパティは、直前の兄弟ノードが存在しない場合、nullを返します。そのため、取得した値がnullでないかを、サンプルコードのようにif ($previousSibling instanceof DOMNode)で必ずチェックしてから利用してください。この確認を怠ると、予期せぬエラーが発生する可能性があります。また、HTMLやXMLをファイルから読み込む際、要素間の改行やインデントが「テキストノード」として扱われることがあります。その場合、previousSiblingが意図しないテキストノードを返す可能性があるため、期待する要素ノードを確実に取得できないことがあります。必要に応じて、ノードのnodeTypeを確認するなどの対応を検討してください。
Dom\Node::previousSibling を使って直前の兄弟ノードを取得する
1<?php 2 3/** 4 * Dom\Node::previousSibling プロパティの動作を示すサンプルコード。 5 * 6 * Dom\Node::previousSibling は、現在のノードの直前の兄弟ノードを返します。 7 * 直前の兄弟ノードが存在しない場合は null を返します。 8 * このプロパティは Dom\Node クラスに属しますが、Dom\Document クラスを使用して 9 * DOMツリーを構築し、その中のノードに対してアクセスする形で利用します。 10 * 11 * システムエンジニアを目指す初心者向けに、簡潔かつ分かりやすく解説しています。 12 */ 13function demonstratePreviousSibling(): void 14{ 15 // 1. Dom\Document オブジェクトを作成します。 16 // これはHTMLやXMLドキュメント全体を表すための基盤となるオブジェクトです。 17 $dom = new Dom\Document(); 18 19 // 2. HTMLコンテンツをロードします。 20 // この例では、`loadHTML` メソッドを使って簡単なHTML文字列をDOMツリーに変換します。 21 // 要素間に改行や空白を入れないことで、余分なテキストノードが兄弟として扱われることによる 22 // 初心者の混乱を避けています。 23 $htmlContent = <<<HTML 24<div id="container"> 25 <p>最初の段落</p><span>最初のスパン</span><p id="target">ターゲット段落</p><span>最後のスパン</span> 26</div> 27HTML; 28 $dom->loadHTML($htmlContent); 29 30 // 3. 特定のノード(ターゲットノード)を取得します。 31 // ここではIDが "target" の <p> 要素をターゲットノードとして取得します。 32 // getElementById メソッドは、該当する Dom\Element オブジェクトまたは null を返します。 33 $targetNode = $dom->getElementById('target'); 34 35 if ($targetNode instanceof Dom\Element) { 36 echo "=== ターゲットノード情報 ===\n"; 37 echo "ノード名: {$targetNode->nodeName}\n"; // ノードのタグ名 (例: p) 38 echo "ノードタイプ: {$targetNode->nodeType} (要素ノード: " . XML_ELEMENT_NODE . ")\n"; // ノードのタイプ (1: 要素ノード) 39 echo "ノード内容 (textContent): '{$targetNode->textContent}'\n\n"; // ノードに含まれるテキスト 40 41 // 4. ターゲットノードの直前の兄弟ノードを取得します。 42 // previousSibling プロパティは、現在のノードの直前の兄弟ノードを返します。 43 // ここでは、'<p id="target">' の直前にある '<span>最初のスパン</span>' が取得されることを期待します。 44 $previousSibling = $targetNode->previousSibling; 45 46 echo "=== 直前の兄弟ノード情報 ===\n"; 47 if ($previousSibling instanceof Dom\Node) { 48 echo "直前の兄弟ノードが見つかりました。\n"; 49 echo " ノード名: {$previousSibling->nodeName}\n"; 50 echo " ノードタイプ: {$previousSibling->nodeType} (要素ノード: " . XML_ELEMENT_NODE . ", テキストノード: " . XML_TEXT_NODE . ")\n"; 51 echo " ノード内容 (textContent): '{$previousSibling->textContent}'\n"; 52 53 // ノードタイプによる補足説明 54 if ($previousSibling->nodeType === XML_ELEMENT_NODE) { 55 echo " これは要素ノード(例: <div>, <p>, <span>など)です。\n"; 56 } elseif ($previousSibling->nodeType === XML_TEXT_NODE) { 57 echo " これはテキストノードです(要素間の改行や空白文字がこれにあたることがあります)。\n"; 58 } elseif ($previousSibling->nodeType === XML_COMMENT_NODE) { 59 echo " これはコメントノードです(<!-- ... -->)。\n"; 60 } 61 } else { 62 // 直前の兄弟ノードが存在しない場合(例: ターゲットノードが最初の子ノードである場合など) 63 echo "直前の兄弟ノードは見つかりませんでした。\n"; 64 echo " (現在のノードが親の最初の子ノードであるか、または兄弟ノードが存在しない可能性があります。)\n"; 65 } 66 } else { 67 echo "IDが 'target' のノードが見つかりませんでした。\n"; 68 } 69} 70 71// サンプルコードの関数を実行して、動作を確認します。 72demonstratePreviousSibling(); 73 74?>
「Dom\Node::previousSibling」プロパティは、現在のノードの直前にある兄弟ノードを取得するために使用されます。このプロパティは引数を取らず、直前の兄弟ノードを「Dom\Node」オブジェクトとして返しますが、直前の兄弟ノードが存在しない場合は「null」を返します。
サンプルコードでは、「Dom\Document」クラスを使用してHTML文字列からDOMツリーを構築しています。そして、「getElementById」メソッドで特定のIDを持つノード(ここではIDが「target」の<p>要素)を取得し、このノードに対して「previousSibling」プロパティを適用しています。これにより、ターゲットノードの直前にある兄弟ノードが取得されます。具体的には、<p id="target">要素の直前に位置する<span>要素(「最初のスパン」)が対象となります。取得されたノードが存在すればその名前や内容が表示され、存在しない場合はその旨がメッセージとして出力されます。このコードを通じて、DOMツリーにおけるノード間の関係性と、「previousSibling」プロパティの働きを具体的に確認できます。
Dom\Node::previousSibling プロパティは、現在のノードの直前の兄弟ノードを取得しますが、直前の兄弟ノードが存在しない場合は null を返します。そのため、取得した結果が null でないか(instanceof Dom\Nodeなどで)必ず確認してから利用してください。
また、HTMLやXMLをパースする際、要素間の空白や改行もDOMツリー上ではテキストノード(XML_TEXT_NODE)として扱われることがあります。サンプルコードではその影響を避けるよう記述していますが、一般的なHTML構造では意図しないテキストノードが取得される可能性があるため、取得したノードのタイプを常に確認する習慣をつけると安全です。このプロパティは Dom\Document オブジェクト自体ではなく、そこから取得した個別の Dom\Node オブジェクトに対して使用する点も理解しておきましょう。