【PHP8.x】Dom\XMLDocument::previousSiblingプロパティの使い方
previousSiblingプロパティの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
previousSiblingプロパティは、PHP 8のDom拡張機能において、Dom\XMLDocumentクラスに属するプロパティです。このプロパティは、現在のノードの直前に位置する兄弟ノードを保持します。DOM(Document Object Model)ツリーにおいて、ノードは親ノードの子として階層的に配置されており、同じ親ノードを持つノード同士を兄弟ノードと呼びます。
Dom\XMLDocumentクラスは、XMLドキュメント全体、つまりDOMツリーの最上位に位置するドキュメントノードを表します。このドキュメントノードは通常、DOMツリーのルートであり、その上位に親ノードや、同じレベルに兄弟ノードを持つことはありません。そのため、Dom\XMLDocumentオブジェクトに対してpreviousSiblingプロパティを参照した場合、通常はその値はnullを返します。これは、ドキュメントノードの直前に兄弟ノードが存在しないことを意味します。
このプロパティは、主に特定の要素ノードやテキストノードなど、DOMツリー内で他のノードと並列に存在するノードに対して、その直前の兄弟ノードを取得するために使用されます。例えば、XMLドキュメント内の特定の子要素に対してpreviousSiblingプロパティを使用することで、その要素の直前にある別の要素やテキストノードを取得することができます。しかし、Dom\XMLDocumentオブジェクト自体はドキュメント全体を指すため、このプロパティが直接的に有用となる場面は稀であることを理解しておく必要があります。
構文(syntax)
1<?php 2$document = new Dom\Document(); 3$node = $document->previousSibling; 4?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
Dom\Node|null
このプロパティは、現在のノードの直前の兄弟ノードを返します。直前の兄弟ノードが存在しない場合は null を返します。
サンプルコード
PHP Dom\XMLDocument::previousSiblingで前の兄弟ノードを取得する
1<?php 2 3/** 4 * Dom\Node の previousSibling プロパティの使用例を示す関数。 5 * XML ドキュメント内の特定の子ノードの前の兄弟ノードを取得します。 6 * 7 * @return void 8 */ 9function demonstratePreviousSibling(): void 10{ 11 // 1. サンプルとなる XML 文字列を定義します。 12 // <root> の下に <item1>, <item2>, <item3> という要素があります。 13 // この例では、空白文字もテキストノードとして扱われる可能性があるため、 14 // コンパクトな形式で記述しています。 15 $xmlString = '<root><item1 id="a"/><item2 id="b"/><item3 id="c"/></root>'; 16 17 // 2. Dom\XMLDocument オブジェクトを作成し、XML を読み込みます。 18 // PHP 8 の新しい DOM 拡張では、クラス名に名前空間が追加されています。 19 $doc = new Dom\XMLDocument(); 20 $doc->loadXML($xmlString); 21 22 // 3. XPath を使用して、特定のノード(例: <item2>)を取得します。 23 // Dom\XPath はドキュメント内のノードを検索するための便利なツールです。 24 $xpath = new Dom\XPath($doc); 25 $item2Node = $xpath->query('//item2[@id="b"]')->item(0); 26 27 // 4. <item2> ノードが見つかった場合の処理。 28 if ($item2Node instanceof Dom\Node) { 29 echo "現在のノード: " . $item2Node->nodeName . "\n"; 30 31 // previousSibling プロパティを使用して、前の兄弟ノードを取得します。 32 // このプロパティは Dom\Node を継承するすべてのノードで使用できます。 33 $previousNode = $item2Node->previousSibling; 34 35 // 5. 取得した前の兄弟ノードの情報を表示します。 36 // previousSibling が存在しない場合は null を返します。 37 if ($previousNode instanceof Dom\Node) { 38 echo " 前の兄弟ノード: " . $previousNode->nodeName . "\n"; 39 } else { 40 echo " 前の兄弟ノードは存在しません。\n"; 41 } 42 } else { 43 echo "<item2> ノードが見つかりませんでした。\n"; 44 } 45 46 echo "\n"; // 出力の区切り 47 48 // 6. 最初のノードの前の兄弟ノードを試す例。 49 // 例として <item1> ノードを取得します。 50 $item1Node = $xpath->query('//item1[@id="a"]')->item(0); 51 52 if ($item1Node instanceof Dom\Node) { 53 echo "現在のノード: " . $item1Node->nodeName . "\n"; 54 55 // <item1> は <root> の最初の子ノードであるため、前の兄弟ノードは存在しません。 56 $previousNodeOfItem1 = $item1Node->previousSibling; 57 58 if ($previousNodeOfItem1 instanceof Dom\Node) { 59 echo " 前の兄弟ノード: " . $previousNodeOfItem1->nodeName . "\n"; 60 } else { 61 echo " 前の兄弟ノードは存在しません。\n"; 62 } 63 } else { 64 echo "<item1> ノードが見つかりませんでした。\n"; 65 } 66} 67 68// 関数を実行して、previousSibling プロパティの動作を確認します。 69demonstratePreviousSibling();
PHP 8のDom\XMLDocumentクラスをはじめとするDom\Nodeを継承するオブジェクトが持つpreviousSiblingプロパティは、XMLやHTMLドキュメントのDOMツリーにおいて、現在のノードの直前にある兄弟ノードを取得するためのものです。このプロパティは引数を一切必要とせず、現在のノードの前に兄弟ノードが存在すればDom\Nodeオブジェクトを、存在しない場合はnullを戻り値として返します。
サンプルコードでは、まずXML文字列からDom\XMLDocumentを生成し、XPathを用いて<item2>ノードを特定しています。この<item2>ノードに対してpreviousSiblingプロパティを利用することで、その直前の兄弟ノードである<item1>が取得され、そのノード名が出力されます。
また、<item1>のように、親ノードの最初の子ノードである場合には、前の兄弟ノードが存在しないため、previousSiblingプロパティはnullを返します。この挙動は、コード内でif ($previousNode instanceof Dom\Node)として適切に処理されていることが示されています。このプロパティは、DOMツリー内の隣接するノード関係を簡単にたどる際に非常に便利で、データ構造の解析や操作において重要な役割を果たします。
このpreviousSiblingプロパティは、直前の兄弟ノードを取得しますが、そのノードが存在しない場合はnullを返します。そのため、取得した結果は必ずnullチェック(if ($previousNode instanceof Dom\Node)など)を行ってから利用してください。特に、親ノードの最初の子ノードに対してこのプロパティを使用すると、常にnullが返ります。また、XMLドキュメント内の要素間に改行や空白文字があると、それらもDom\Textノードとして認識され、previousSiblingがテキストノードを返す可能性がありますので注意が必要です。PHP 8からはDOMクラスにDom\名前空間が付いている点も覚えておきましょう。
previousSibling で兄弟ノードを取得する
1<?php 2 3/** 4 * Dom\XMLDocument クラスの previousSibling プロパティの動作を示すサンプルコードです。 5 * 6 * previousSibling プロパティは、現在のノードの直前の兄弟ノード (同じ親を持つノード) を返します。 7 * 直前の兄弟ノードが存在しない場合 (現在のノードが最初の兄弟ノードである場合) は null を返します。 8 * 9 * @return void 10 */ 11function demonstratePreviousSibling(): void 12{ 13 // 1. サンプルとなるXML文字列を定義します。 14 // テキストノードや改行もノードとして扱われる点に注意してください。 15 $xmlString = <<<XML 16<root> 17 <item id="item1">First Item</item> 18 <item id="item2">Second Item</item> 19 <item id="item3">Third Item</item> 20</root> 21XML; 22 23 // 2. Dom\XMLDocument オブジェクトを作成し、XMLをロードします。 24 $dom = new Dom\XMLDocument(); 25 if (!$dom->loadXML($xmlString)) { 26 echo "Error loading XML.\n"; 27 return; 28 } 29 30 echo "--- Original XML Structure ---\n"; 31 echo $xmlString . "\n\n"; 32 33 // 3. 特定のノードを選択し、previousSibling を確認します。 34 // ここでは、全ての <item> 要素を取得します。 35 $items = $dom->getElementsByTagName('item'); 36 37 echo "--- Demonstrating previousSibling ---\n"; 38 39 // 最初の item ノード (<item id="item1">) を取得します。 40 // このノードには直前の兄弟ノードがないため、previousSibling は null になります。 41 if ($items->count() > 0) { 42 $firstItem = $items->item(0); 43 echo "Current Node (first item): " . ($firstItem ? $firstItem->nodeName . ' (ID: ' . $firstItem->getAttribute('id') . ')' : 'N/A') . "\n"; 44 $prevSiblingOfFirst = $firstItem->previousSibling; 45 echo " -> previousSibling: " . ($prevSiblingOfFirst ? $prevSiblingOfFirst->nodeName . ' (Type: ' . $prevSiblingOfFirst->nodeType . ')' : 'null') . "\n\n"; 46 // 注意: <root>要素と<item id="item1">の間の改行や空白もテキストノードとしてカウントされることがあります。 47 // PHP 8のDom拡張では、デフォルトで空白のみのテキストノードも扱われます。 48 } 49 50 // 3番目の item ノード (<item id="item3">) を取得します。 51 // このノードの直前の兄弟ノードは <item id="item2"> です。 52 if ($items->count() > 2) { 53 $thirdItem = $items->item(2); // 0-indexed なので 2 は3番目の要素 54 echo "Current Node (third item): " . ($thirdItem ? $thirdItem->nodeName . ' (ID: ' . $thirdItem->getAttribute('id') . ')' : 'N/A') . "\n"; 55 $prevSiblingOfThird = $thirdItem->previousSibling; 56 echo " -> previousSibling: " . ($prevSiblingOfThird ? $prevSiblingOfThird->nodeName . ' (ID: ' . $prevSiblingOfThird->getAttribute('id') . ')' : 'null') . "\n\n"; 57 } 58 59 // 4. より正確な動作確認のために、空白ノードを無視する設定で再実行してみます。 60 // DOMDocument::loadXML() や parse() メソッドに LIBXML_NOBLANKS フラグを渡すか、 61 // DOMDocument の preserveWhiteSpace プロパティを false に設定することで制御できます。 62 // Dom\XMLDocument のコンストラクタで XML を直接渡す場合は、その後の設定で対応します。 63 echo "--- Demonstrating previousSibling (ignoring whitespace) ---\n"; 64 $domNoWhitespace = new Dom\XMLDocument(); 65 // preserveWhiteSpace を false に設定すると、空白のみのテキストノードを無視します。 66 $domNoWhitespace->preserveWhiteSpace = false; 67 if (!$domNoWhitespace->loadXML($xmlString)) { 68 echo "Error loading XML (no whitespace).\n"; 69 return; 70 } 71 72 $itemsNoWhitespace = $domNoWhitespace->getElementsByTagName('item'); 73 74 if ($itemsNoWhitespace->count() > 0) { 75 $firstItemNoWhitespace = $itemsNoWhitespace->item(0); 76 echo "Current Node (first item, no whitespace): " . ($firstItemNoWhitespace ? $firstItemNoWhitespace->nodeName . ' (ID: ' . $firstItemNoWhitespace->getAttribute('id') . ')' : 'N/A') . "\n"; 77 $prevSiblingOfFirstNoWhitespace = $firstItemNoWhitespace->previousSibling; 78 echo " -> previousSibling: " . ($prevSiblingOfFirstNoWhitespace ? $prevSiblingOfFirstNoWhitespace->nodeName . ' (Type: ' . $prevSiblingOfFirstNoWhitespace->nodeType . ')' : 'null') . "\n\n"; 79 } 80 81 if ($itemsNoWhitespace->count() > 2) { 82 $thirdItemNoWhitespace = $itemsNoWhitespace->item(2); 83 echo "Current Node (third item, no whitespace): " . ($thirdItemNoWhitespace ? $thirdItemNoWhitespace->nodeName . ' (ID: ' . $thirdItemNoWhitespace->getAttribute('id') . ')' : 'N/A') . "\n"; 84 $prevSiblingOfThirdNoWhitespace = $thirdItemNoWhitespace->previousSibling; 85 echo " -> previousSibling: " . ($prevSiblingOfThirdNoWhitespace ? $prevSiblingOfThirdNoWhitespace->nodeName . ' (ID: ' . $prevSiblingOfThirdNoWhitespace->getAttribute('id') . ')' : 'null') . "\n\n"; 86 } 87} 88 89// 関数を実行します。 90demonstratePreviousSibling();
PHP 8のDom\XMLDocumentクラスに属するpreviousSiblingプロパティは、XML文書内の特定のノードにおいて、その直前にある兄弟ノードを取得するために使用されます。兄弟ノードとは、同じ親要素を持つノードのことです。このプロパティは引数を取らず、戻り値として直前の兄弟ノードを表すDom\Nodeオブジェクト、または直前の兄弟ノードが存在しない場合はnullを返します。
サンプルコードでは、まずXML文字列からDom\XMLDocumentオブジェクトを作成し、複数の<item>要素を持つ構造を準備しています。最初の<item>ノードに対してpreviousSiblingを使用すると、直前の兄弟ノードが存在しないためnullが返されます。一方、3番目の<item>ノードに対して使用すると、その直前にある2番目の<item>ノードが返される様子が示されています。
ここで重要な点として、XML文書をパースする際、要素間の改行や空白もテキストノードとして扱われる場合があります。そのため、期待とは異なるテキストノードがpreviousSiblingとして返されることがあります。この挙動を制御するには、Dom\XMLDocumentオブジェクトのpreserveWhiteSpaceプロパティをfalseに設定することで、空白のみのテキストノードを無視させることができます。この設定を行うと、より意図した要素ノードを取得しやすくなります。
previousSiblingプロパティは、現在のノードの直前の兄弟ノードを返します。XMLの改行やインデントも#textノードとして扱われるため、意図しないテキストノードが返されることがあります。直前の兄弟ノードがない場合はnullを返しますので、必ずnullチェックを行ってください。空白ノードを無視し要素ノードのみ扱いたい場合、XMLロード前にDom\XMLDocumentのpreserveWhiteSpaceをfalseに設定すると、XML構造を明確に扱えます。