【PHP8.x】Dom\Node::previousSiblingプロパティの使い方
previousSiblingプロパティの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『previousSiblingプロパティは、DOMツリーにおいて、現在のノードの直前に位置する兄弟ノードを保持するプロパティです。兄弟ノードとは、同じ親ノードを共有するノード群のことを指します。このプロパティにアクセスすると、現在のノードの一つ前に存在するノードをDom\Nodeオブジェクトとして取得することができます。もし直前に兄弟ノードが存在しない場合、つまり現在のノードがその親における最初の子ノードである場合には、このプロパティはnullを返します。このnullが返される特性は、特定のノードから先頭に向かって、すべての兄弟ノードを順番にたどるようなループ処理の終了条件として頻繁に利用されます。このプロパティが返すノードは、要素ノードに限らず、要素間の空白や改行を含むテキストノードや、コメントノードなども対象となります。そのため、HTMLドキュメントを操作する際には、意図しないテキストノードが取得される可能性がある点に注意が必要です。なお、このプロパティは読み取り専用であり、値の代入によってノードの順序を変更することはできません。
構文(syntax)
1<?php 2 3$html = '<ul><li>First</li><li>Second</li></ul>'; 4 5$dom = new \Dom\Document(); 6@$dom->loadHTML($html); 7 8// 2番目の <li> 要素を取得します 9$secondLi = $dom->getElementsByTagName('li')[1]; 10 11// 2番目の <li> 要素の直前の兄弟ノード (最初の <li> 要素) を取得します 12$previousNode = $secondLi->previousSibling; 13 14// 取得したノードのテキスト内容を出力します 15echo $previousNode->textContent; // "First" 16 17?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
?Dom\Node
現在のノードの直前の兄弟ノード、または兄弟ノードが存在しない場合は null を返します。
サンプルコード
PHP Dom\Node::previousSibling で直前兄弟ノードを取得する
1<?php 2 3/** 4 * 指定されたHTML文字列から特定のIDを持つ要素を見つけ、 5 * その直前の兄弟ノードの情報を表示します。 6 * 7 * Dom\Node::previousSibling プロパティは、現在のノードの直前にある兄弟ノードを返します。 8 * 直前の兄弟ノードが存在しない場合(例: 親ノードの最初の子要素である場合)は null を返します。 9 * 10 * @param string $html HTML文字列 11 * @param string $id 対象ノードのID 12 * @return void 13 */ 14function demonstrateDomNodePreviousSibling(string $html, string $id): void 15{ 16 // Dom\Document オブジェクトを初期化 17 $dom = new Dom\Document(); 18 19 // HTML文字列のロード時に発生する警告を抑制し、後でエラー情報をクリア 20 // これにより、不完全なHTMLでもスクリプトが中断せずに続行できます 21 libxml_use_internal_errors(true); 22 $dom->loadHTML($html); 23 libxml_clear_errors(); // 読み込みエラー情報をクリア 24 25 // 指定されたIDを持つ要素ノードを検索 26 $targetNode = $dom->getElementById($id); 27 28 if (!$targetNode) { 29 echo "エラー: ID '{$id}' を持つノードが見つかりませんでした。\n"; 30 return; 31 } 32 33 echo "--- 対象ノードの情報 ---\n"; 34 echo "ノード名: " . $targetNode->nodeName . "\n"; 35 echo "内容: '" . trim($targetNode->textContent) . "'\n"; // ノードの内容をトリムして表示 36 37 // Dom\Node::previousSibling プロパティを使用して直前の兄弟ノードを取得 38 // 戻り値は ?Dom\Node で、存在しない場合は null になります。 39 $previousSiblingNode = $targetNode->previousSibling; 40 41 echo "\n--- previousSibling の結果 ---\n"; 42 if ($previousSiblingNode) { 43 echo "直前の兄弟ノードが見つかりました。\n"; 44 echo " ノードの種類 (Type): " . $previousSiblingNode->nodeType . " (Dom\Node::ELEMENT_NODE は 1)\n"; 45 echo " ノード名: " . $previousSiblingNode->nodeName . "\n"; 46 echo " ノード内容: '" . trim($previousSiblingNode->textContent) . "'\n"; 47 } else { 48 echo "直前の兄弟ノードは見つかりませんでした。\n"; 49 echo "対象ノードが親ノードの最初の子要素である可能性があります。\n"; 50 } 51} 52 53// サンプルHTML文字列 54// 要素間に余計な改行や空白を入れないことで、previousSibling が意図した要素ノードを返しやすくなります。 55$sampleHtml = <<<HTML 56<!DOCTYPE html> 57<html> 58<body> 59 <div> 60 <p>最初の段落</p><span>中間要素</span><a id="target_link">ターゲットリンク</a><span>最後の要素</span> 61 </div> 62 <hr> 63 <div> 64 <span id="first_child_target">最初の子要素</span><p>続く段落</p> 65 </div> 66 <div> 67 <!-- コメントノード --><span id="target_after_comment">コメント後の要素</span> 68 </div> 69</body> 70</html> 71HTML; 72 73// 実行例 1: 直前の要素兄弟ノードがある場合 74echo "=== 実行例 1: 直前の要素兄弟ノードがある場合 ===\n"; 75demonstrateDomNodePreviousSibling($sampleHtml, 'target_link'); 76echo "\n\n"; 77 78// 実行例 2: 直前の兄弟ノードがない(親の最初の子要素)の場合 79echo "=== 実行例 2: 直前の兄弟ノードがない場合 ===\n"; 80demonstrateDomNodePreviousSibling($sampleHtml, 'first_child_target'); 81echo "\n\n"; 82 83// 実行例 3: 直前にコメントノードがある場合 (コメントノードも兄弟ノードとして扱われる) 84echo "=== 実行例 3: 直前にコメントノードがある場合 ===\n"; 85demonstrateDomNodePreviousSibling($sampleHtml, 'target_after_comment'); 86echo "\n\n"; 87 88// 実行例 4: 対象ノードが存在しない場合 89echo "=== 実行例 4: 対象ノードが存在しない場合 ===\n"; 90demonstrateDomNodePreviousSibling($sampleHtml, 'non_existent_id'); 91 92?>
PHP 8のDom\Node::previousSiblingプロパティは、ウェブページなどのHTML構造をプログラムで操作する際に役立つ機能です。このプロパティは、特定のノード(要素やテキストなど)の「直前にある兄弟ノード」を取得するために使用します。兄弟ノードとは、同じ親ノードを持つ隣接するノードのことです。
このプロパティは引数を取らず、戻り値として?Dom\Node型を返します。これは、直前の兄弟ノードが存在すればそのDom\Nodeオブジェクトが返され、存在しない場合(例えば、対象ノードが親ノードの最初の子要素である場合)にはnullが返されることを意味します。
サンプルコードでは、まずHTML文字列から指定されたIDを持つ要素を検索し、そのノードが持つpreviousSiblingプロパティを利用して直前の兄弟ノードを取得しています。実行例を通じて、直前の兄弟ノードが要素ノードである場合、直前の兄弟ノードが存在しない場合、さらにはコメントノードのような要素以外のノードも兄弟として扱われる様子が示されています。これにより、HTML構造内の要素の相対的な位置関係を簡単に把握し、プログラムでアクセスできるようになります。特に、テキストノードやコメントノードも兄弟として認識される点に注意が必要です。
previousSiblingは現在のノードの直前にある兄弟ノードを返しますが、HTMLタグだけでなく、改行や空白などもテキストノードとして認識される点に注意が必要です。HTMLソースの整形によっては、意図しないテキストノードが返されることがありますので、要素間の余分な空白や改行をなくすことで、期待する要素ノードを得やすくなります。直前の兄弟ノードが存在しない場合はnullを返すため、取得した値を利用する前には必ずnullチェックを行ってください。また、Dom\Document::loadHTMLでHTMLを読み込む際は、libxml_use_internal_errorsを使用してエラーを一時的に抑制することで、不完全なHTMLでもスクリプトが中断せずに処理を続行できます。
PHP Dom\Node::previousSiblingで前の兄弟ノードを取得する
1<?php 2 3/** 4 * Dom\Node::previousSibling プロパティの使用例を示します。 5 * このプロパティは、現在のノードの直前の兄弟ノード(同じ親を持つ直前のノード)を返します。 6 * 直前の兄弟ノードが存在しない場合は null を返します。 7 * 8 * システムエンジニアを目指す初心者にも分かりやすいよう、基本的な使い方に焦点を当てています。 9 */ 10function demonstratePreviousSibling(): void 11{ 12 // サンプルXMLデータを作成します。 13 // 要素間に改行やインデントがありますが、DOMDocumentの設定でこれらをテキストノードとして扱わないようにします。 14 $xmlString = <<<XML 15 <root> 16 <item id="item1">First Item</item> 17 <item id="item2">Second Item</item> 18 <item id="item3">Third Item</item> 19 </root> 20 XML; 21 22 // DOMドキュメントを作成し、XMLをロードします。 23 $dom = new Dom\Document(); 24 // preserveWhiteSpace を false に設定することで、要素間の空白文字(改行やスペース)を 25 // テキストノードとしてパースしないようにします。 26 // これにより、previousSibling が要素ノードを返すことを期待できます。 27 $dom->preserveWhiteSpace = false; 28 $dom->loadXML($xmlString); 29 30 echo "--- 'item3' ノードの前の兄弟ノードを確認 ---" . PHP_EOL; 31 32 // idが 'item3' のノードを取得します。 33 $targetNodeItem3 = $dom->getElementById('item3'); 34 35 if ($targetNodeItem3 instanceof Dom\Node) { 36 echo "現在のターゲットノード: <" . $targetNodeItem3->nodeName . "> (id: " . $targetNodeItem3->getAttribute('id') . ")" . PHP_EOL; 37 echo "このノードの 'previousSibling' を取得します..." . PHP_EOL; 38 39 // previousSibling プロパティにアクセスし、直前の兄弟ノードを取得します。 40 $previousNode = $targetNodeItem3->previousSibling; 41 42 // 戻り値が Dom\Node のインスタンスか、null かをチェックします。 43 if ($previousNode instanceof Dom\Node) { 44 echo "-> 見つかった前の兄弟ノード: <" . $previousNode->nodeName . "> (id: " . ($previousNode->hasAttribute('id') ? $previousNode->getAttribute('id') : 'なし') . ")" . PHP_EOL; 45 } else { 46 echo "-> 前の兄弟ノードは見つかりませんでした (null が返されました)。" . PHP_EOL; 47 } 48 } else { 49 echo "エラー: 'item3' ノードが見つかりませんでした。" . PHP_EOL; 50 } 51 52 echo PHP_EOL; 53 54 echo "--- 'item1' ノードの前の兄弟ノードを確認 (最初の兄弟ノードのケース) ---" . PHP_EOL; 55 56 // idが 'item1' のノードを取得します。 57 // これは親要素の子の中で最初のノードです。 58 $targetNodeItem1 = $dom->getElementById('item1'); 59 60 if ($targetNodeItem1 instanceof Dom\Node) { 61 echo "現在のターゲットノード: <" . $targetNodeItem1->nodeName . "> (id: " . $targetNodeItem1->getAttribute('id') . ")" . PHP_EOL; 62 echo "このノードの 'previousSibling' を取得します..." . PHP_EOL; 63 64 // previousSibling プロパティにアクセスします。 65 $previousNodeForFirst = $targetNodeItem1->previousSibling; 66 67 // 最初の兄弟ノードには前の兄弟ノードが存在しないため、null が返されるはずです。 68 if ($previousNodeForFirst instanceof Dom\Node) { 69 echo "-> 見つかった前の兄弟ノード: <" . $previousNodeForFirst->nodeName . "> (id: " . ($previousNodeForFirst->hasAttribute('id') ? $previousNodeForFirst->getAttribute('id') : 'なし') . ")" . PHP_EOL; 70 } else { 71 echo "-> 前の兄弟ノードは見つかりませんでした (null が返されました)。これは期待される動作です。" . PHP_EOL; 72 } 73 } else { 74 echo "エラー: 'item1' ノードが見つかりませんでした。" . PHP_EOL; 75 } 76} 77 78// 関数を実行して動作を確認します。 79demonstratePreviousSibling();
PHPのDom\Node::previousSiblingプロパティは、XMLやHTMLなどのDOM構造において、現在のノードの「直前の兄弟ノード」を取得するために使用されます。兄弟ノードとは、同じ親要素を持つノードのことです。このプロパティにアクセスすると、現在のノードのすぐ前にある兄弟ノードがDom\Nodeオブジェクトとして返されます。もし直前の兄弟ノードが存在しない場合(例えば、現在のノードが親要素の最初の子ノードである場合など)は、nullが返されます。このプロパティに引数は必要ありません。
サンプルコードでは、まずXMLデータを作成し、Dom\Documentに読み込んでいます。ここで重要なのは、$dom->preserveWhiteSpace = false;を設定している点です。これにより、XML中の改行やインデントといった空白文字が余計なテキストノードとして扱われず、期待通りに要素ノードのみを兄弟として取得できるようになります。
コードでは、まずitem3ノードを取得し、そのpreviousSiblingプロパティで直前のitem2ノードが取得できることを示しています。次に、最初の兄弟ノードであるitem1のpreviousSiblingを試すと、直前のノードが存在しないためnullが返される動作を確認しています。このように、previousSiblingプロパティはDOMツリーの特定のノードから前方向のノードを辿る際に役立ちます。
Dom\Node::previousSiblingは、直前の兄弟ノードが存在しない場合や、現在のノードが親の子ノードリストの最初である場合はnullを返します。そのため、プロパティにアクセスした後は、必ず戻り値がDom\Nodeのインスタンスであるかnullであるかをif ($node instanceof Dom\Node)のようにチェックし、適切に処理を分岐させてください。
特に注意が必要なのは、XML/HTMLソースの要素間に含まれる改行やインデントなどの空白文字です。これらはデフォルトではテキストノードとして扱われるため、previousSiblingが期待する要素ノードではなく、空白文字のテキストノードを返すことがあります。要素ノードのみを処理したい場合は、サンプルコードのように$dom->preserveWhiteSpace = false;を設定し、空白文字を無視するよう明示的に指示すると安全です。この設定をしないと、意図しないテキストノードが返される可能性があるため、実装時には十分ご注意ください。