【PHP8.x】Dom\Element::previousSiblingプロパティの使い方
previousSiblingプロパティの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
previousSiblingプロパティは、現在のDom\Elementオブジェクトの直前にある兄弟ノードを保持するプロパティです。このプロパティは、PHPのDom拡張機能の一部であり、HTMLやXMLのような構造化されたドキュメントの内部をプログラムで探索し、操作するために利用されます。
DOM(Document Object Model)ツリーにおいて、ドキュメント内のすべての要素やテキストなどは「ノード」として表現され、これらは親子関係や兄弟関係を持って階層的に配置されています。previousSiblingプロパティは、現在のDom\Elementオブジェクトが表す要素と「同じ親要素を持つ」ノードの中から、ドキュメントの構造上で「現在の要素の直前に位置する」ノードへの参照を提供します。
このプロパティが返す値は、直前の兄弟ノードそのものであるDom\Nodeオブジェクトです。もし、現在の要素の直前に兄弟ノードが存在しない場合、つまり現在の要素が親要素の最初の子ノードである場合には、このプロパティはnullを返します。
このプロパティを使用することで、例えば、特定のHTML要素が見つかった際に、その直前にあるテキストや他の要素の内容を読み取ったり、それらを操作したりすることが可能になります。Webコンテンツの解析、動的なページ生成、既存のドキュメント構造の変更など、DOM操作を必要とする様々なシナリオで、要素間のナビゲーションを効率的に行うための重要な手段となります。
構文(syntax)
1<?php 2// Dom\Element クラスのインスタンスを $element と仮定します。 3// 例として、HTML文字列からDOM要素を生成します。 4$dom = new DOMDocument(); 5$dom->loadHTML('<div><span>Sibling 1</span><p id="current">Current Element</p><span>Sibling 2</span></div>'); 6 7// ID 'current' を持つ要素(<p>タグ)を取得します。 8// この $element は Dom\Element のインスタンスです。 9$element = $dom->getElementById('current'); 10 11// $element の直前の兄弟ノードを取得する構文 12// 取得される $previousSiblingNode は Dom\Node クラスのインスタンス、 13// もしくは直前の兄弟ノードが存在しない場合は null になります。 14$previousSiblingNode = $element->previousSibling; 15 16// 取得したノードが実際にDom\Elementのインスタンスであるかを確認し、そのプロパティにアクセスする例 17if ($previousSiblingNode instanceof Dom\Element) { 18 echo "直前の兄弟要素のタグ名: " . $previousSiblingNode->tagName; // 例: span 19} elseif ($previousSiblingNode !== null) { 20 // 要素ではないが、テキストノードなどの兄弟ノードが存在する場合 21 echo "直前の兄弟ノードは要素ではありません。ノード名: " . $previousSiblingNode->nodeName; 22} else { 23 // 直前の兄弟ノードが存在しない場合 24 echo "直前の兄弟ノードはありません。"; 25} 26?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
Dom\Node|null
このプロパティは、現在の要素の直前の兄弟ノードを返します。直前の兄弟ノードが存在しない場合は、null を返します。
サンプルコード
PHP8 Dom\Element::previousSiblingで直前の兄弟ノードを取得する
1<?php 2 3// Dom\Element::previousSibling プロパティの使用例 4// このプロパティは、現在の要素の直前の兄弟ノード(要素、テキスト、コメントなど)を取得します。 5// PHP 8以降の Dom\Element クラスを使用します。 6 7function demonstratePreviousSibling(): void 8{ 9 // 処理対象となるHTML文字列を定義します。 10 // 要素間に改行やスペースを挟むことで、テキストノードが兄弟として扱われる可能性があることを示します。 11 $html = <<<HTML 12<!DOCTYPE html> 13<html> 14<head> 15 <title>previousSibling Example</title> 16</head> 17<body> 18 <div id="container"> 19 <!-- コメントノード --> 20 <p id="first-p">最初の段落</p> 21 これはテキストノードです。 22 <span id="target-span">ターゲットの要素</span> 23 <p id="last-p">最後の段落</p> 24 </div> 25</body> 26</html> 27HTML; 28 29 // DOMDocument オブジェクトを作成し、HTMLをロードします。 30 // PHP 8では既存のDOM拡張機能と新しいDom名前空間のクラスは相互運用可能です。 31 $dom = new DOMDocument(); 32 // HTMLパースエラーが表示されないように抑制します。 33 @$dom->loadHTML($html); 34 35 // XPath を使用して、特定のIDを持つ 'span' 要素を取得します。 36 $xpath = new DOMXPath($dom); 37 $targetNodeList = $xpath->query('//*[@id="target-span"]'); 38 39 // 目的の要素が見つかった場合のみ処理を進めます。 40 if ($targetNodeList instanceof DOMNodeList && $targetNodeList->length > 0) { 41 // 最初の要素を取得します。これは DOMNode のインスタンスです。 42 $targetNode = $targetNodeList->item(0); 43 44 // 取得したノードが Dom\Element のインスタンスであることを確認します。 45 // PHP 8では DOMElement は Dom\Element と互換性があります。 46 if ($targetNode instanceof Dom\Element) { 47 echo "--- ターゲット要素: '{$targetNode->tagName}' (id='{$targetNode->id}') ---\n"; 48 49 // previousSibling プロパティを使用して、直前の兄弟ノードを取得します。 50 $previousSibling = $targetNode->previousSibling; 51 52 if ($previousSibling === null) { 53 echo "直前の兄弟ノードは見つかりませんでした。\n"; 54 } else { 55 echo "直前の兄弟ノードが見つかりました。\n"; 56 echo " ノード名: '{$previousSibling->nodeName}'\n"; 57 // ノードタイプは定数で定義されています (例: XML_ELEMENT_NODE, XML_TEXT_NODE, XML_COMMENT_NODE)。 58 echo " ノードタイプ: {$previousSibling->nodeType}\n"; 59 60 // ノードタイプによって追加情報を表示します。 61 if ($previousSibling->nodeType === XML_ELEMENT_NODE) { // 要素ノード 62 echo " 要素名: '{$previousSibling->tagName}'\n"; 63 echo " 要素テキスト内容: '" . trim($previousSibling->textContent) . "'\n"; 64 } elseif ($previousSibling->nodeType === XML_TEXT_NODE) { // テキストノード 65 // テキストノードの場合、nodeValue に内容が含まれます。 66 // 余分な空白や改行をtrimして表示します。 67 echo " テキスト内容 (空白、改行含む): '" . trim($previousSibling->nodeValue) . "'\n"; 68 } elseif ($previousSibling->nodeType === XML_COMMENT_NODE) { // コメントノード 69 echo " コメント内容: '" . trim($previousSibling->nodeValue) . "'\n"; 70 } 71 } 72 } else { 73 echo "取得したノードは Dom\Element のインスタンスではありません。\n"; 74 } 75 } else { 76 echo "ターゲット要素が見つかりませんでした。\n"; 77 } 78 79 echo "\n--- 最初の段落の前の兄弟ノード --- \n"; 80 // 別の例:最初の段落の前の兄弟ノードを試す 81 $firstPNodeList = $xpath->query('//*[@id="first-p"]'); 82 if ($firstPNodeList instanceof DOMNodeList && $firstPNodeList->length > 0) { 83 $firstP = $firstPNodeList->item(0); 84 if ($firstP instanceof Dom\Element) { 85 echo "ターゲット要素: '{$firstP->tagName}' (id='{$firstP->id}')\n"; 86 $prev = $firstP->previousSibling; 87 if ($prev === null) { 88 echo "直前の兄弟ノードは見つかりませんでした。\n"; 89 } else { 90 echo "直前の兄弟ノードが見つかりました。\n"; 91 echo " ノード名: '{$prev->nodeName}'\n"; 92 echo " ノードタイプ: {$prev->nodeType}\n"; 93 if ($prev->nodeType === XML_COMMENT_NODE) { 94 echo " コメント内容: '" . trim($prev->nodeValue) . "'\n"; 95 } elseif ($prev->nodeType === XML_TEXT_NODE) { 96 echo " テキスト内容 (空白、改行含む): '" . trim($prev->nodeValue) . "'\n"; 97 } 98 } 99 } 100 } 101} 102 103// サンプルコードを実行します。 104demonstratePreviousSibling(); 105 106?>
PHP 8で利用できるDom\Element::previousSiblingプロパティは、HTMLやXML文書内で、ある要素の直前に位置する兄弟ノードを取得するために使用されます。兄弟ノードとは、同じ親要素を持つ隣接するノードのことで、タグで構成される要素ノードだけでなく、改行や空白文字を含むテキストノード、コメントノードなども含まれます。
このプロパティは引数を取らず、Dom\Elementクラスのインスタンスから直接アクセスして使用します。戻り値としては、直前の兄弟ノードが存在する場合、そのノードをDom\Nodeのインスタンスとして返します。もし直前に兄弟ノードが存在しない場合はnullが返されます。
サンプルコードでは、まずHTML文字列を読み込み、特定のIDを持つ<span>要素をターゲットとしています。このターゲット要素のpreviousSiblingプロパティにアクセスすると、直前の兄弟ノードが「これはテキストノードです。」というテキストノードであることが取得され、その内容が表示されます。さらに、最初の<p>要素の直前の兄弟ノードとしてコメントノードが取得される例も示されており、要素の直前にあるのがタグだけでなくテキストやコメントであっても、それが兄弟ノードとして正確に認識される様子が確認できます。このプロパティは、DOMツリーを探索し、隣接するノードの情報を取得する際に大変便利です。
Dom\Element::previousSiblingプロパティは、現在の要素の直前にある兄弟ノードを取得します。この際、要素ノードだけでなく、HTMLソース上の要素間の改行や空白がテキストノードとして、またコメントもコメントノードとして取得される点に注意が必要です。ノードが見つからない場合はnullが返されるため、必ず利用前にnullチェックを行ってください。取得したノードの具体的な内容にアクセスするには、nodeTypeプロパティ(XML_ELEMENT_NODEなど)でノードの種類を判別し、tagNameやnodeValueといった適切なプロパティを使用します。特にテキストノードの場合、nodeValueには余分な空白や改行が含まれることがあるため、必要に応じてtrim()関数で整形するとよいでしょう。PHP 8では、従来のDOMElementと新しいDom\Elementは互換性があります。
Dom\Element::previousSiblingで前の兄弟ノードを取得する
1<?php 2 3/** 4 * Dom\Element::previousSibling プロパティの使用例をデモンストレーションします。 5 * 6 * この関数は、HTMLドキュメントをパースし、特定のDom\Elementから開始して、 7 * その前の兄弟ノードを順にたどることで、previousSiblingプロパティの動作を示します。 8 * previousSiblingはDom\Node型のインスタンス、または前の兄弟ノードが存在しない場合はnullを返します。 9 * 10 * PHP 8.1 以降で Dom\Namespace のクラスが導入されています。 11 */ 12function demonstrateDomElementPreviousSibling(): void 13{ 14 // サンプルHTMLドキュメント 15 // 様々な種類の兄弟ノード(コメントノード、テキストノード、要素ノード)を含むように設計し、 16 // previousSiblingが異なるタイプのDom\Nodeを返すことを示します。 17 $html = <<<HTML 18<!DOCTYPE html> 19<html> 20<body> 21 <div id="container"> 22 <!-- これはコメントノードです --> 23 <p id="first-paragraph">最初のパラグラフ</p> 24 これはテキストノードです。 25 <span id="middle-span">スパン要素</span> 26 <p id="target-paragraph">ターゲットのパラグラフ</p> 27 </div> 28</body> 29</html> 30HTML; 31 32 // Dom\Documentオブジェクトを作成し、HTMLをロードします。 33 $dom = new Dom\Document(); 34 // loadHTMLはHTML5の標準に厳密ではないため、パースエラーが発生することがあります。 35 // そのようなエラーを抑制するために@演算子を使用しています。 36 @$dom->loadHTML($html); 37 38 echo "--- Dom\\Element::previousSibling のデモンストレーション --- \n\n"; 39 40 // 1. `id="target-paragraph"` の<p>要素を取得し、開始ノードとします。 41 $currentNode = $dom->getElementById('target-paragraph'); 42 43 // 取得したノードがDom\Elementのインスタンスであることを確認します。 44 if (!$currentNode instanceof Dom\Element) { 45 echo "エラー: `id=\"target-paragraph\"` を持つ要素が見つかりませんでした。\n"; 46 return; 47 } 48 49 echo "開始ノード: <" . $currentNode->nodeName . " id='" . $currentNode->getAttribute('id') . "'>\n"; 50 echo " 内容: \"" . trim($currentNode->textContent) . "\"\n\n"; 51 52 $step = 1; 53 // previousSiblingを順にたどっていきます。 54 // 無限ループ防止のため、最大5ステップでループを終了します。 55 while ($currentNode !== null && $step <= 5) { 56 echo "--- ステップ " . $step++ . " ---\n"; 57 58 // 現在のノードの前の兄弟ノードを取得します。 59 // これは Dom\Node 型または null を返します。 60 $previousSibling = $currentNode->previousSibling; 61 62 if ($previousSibling instanceof Dom\Node) { 63 echo " 見つかった previousSibling:\n"; 64 echo " ノード名: " . $previousSibling->nodeName . "\n"; 65 echo " ノードタイプ: " . $previousSibling->nodeType . " (" . Dom\Node::lookupNodeType($previousSibling->nodeType) . ")\n"; 66 // ノードの種類によってコンテンツの取得方法が変わります。 67 // `textContent` は要素内のすべてのテキストを返します。 68 // `nodeValue` はテキストノードやコメントノードで直接その内容を返しますが、 69 // 要素ノードの場合は通常空です。ここでは、一般的に使える `textContent` を表示します。 70 echo " コンテンツ: \"" . trim($previousSibling->textContent) . "\"\n\n"; 71 72 // 次のループのために現在のノードを更新し、さらに前の兄弟ノードを検索できるようにします。 73 $currentNode = $previousSibling; 74 } else { 75 echo " previousSiblingは見つかりませんでした。これ以上前の兄弟ノードはありません。\n"; 76 $currentNode = null; // ループを終了します。 77 } 78 } 79 80 echo "--- デモンストレーション終了 ---\n"; 81} 82 83// 関数を実行して、previousSiblingプロパティの動作を確認します。 84demonstrateDomElementPreviousSibling(); 85
Dom\Element::previousSiblingは、HTMLやXMLドキュメント内で特定の要素(Dom\Element)の直前にある兄弟ノードを取得するためのプロパティです。このプロパティは引数を持ちません。戻り値は、直前の兄弟ノードが存在する場合はDom\Node型のオブジェクト、存在しない場合はnullとなります。Dom\Nodeは、HTMLやXMLを構成する要素、テキスト、コメントなど、すべての部品を表現する汎用的な型です。
提供されたサンプルコードでは、id="target-paragraph"を持つ<p>要素を起点として、previousSiblingプロパティを繰り返し使用し、その直前の兄弟ノードを順にたどる様子をデモンストレーションしています。具体的には、<p>要素の前の兄弟である「スパン要素」の<span>要素、その前の「これはテキストノードです。」というテキストノード、さらにその前の「<!-- これはコメントノードです -->」というコメントノードなど、様々な種類のノードが取得されることを示しています。このように、previousSiblingは単に他の要素だけでなく、テキストやコメントといった非要素ノードも兄弟として扱うため、より詳細なドキュメント構造の探索が可能になります。このプロパティは、ドキュメント内の要素間の関係性を分析したり、特定の要素の直前にあるコンテンツを操作したりする際に大変有用です。
Dom\Element::previousSiblingプロパティは、現在の要素の直前の兄弟ノードを返しますが、存在しない場合はnullを返します。そのため、取得後は必ずnullチェック、またはinstanceof Dom\Nodeでノード型を確認してから利用してください。
返されるノードは、要素ノードだけでなく、テキストノードやコメントノードなど、様々な種類のDom\Nodeとなり得ます。特定のノードのみを扱いたい場合は、instanceof演算子で型を判定する処理が必要です。
ノードの内容取得にはtextContentやnodeValueプロパティを用いますが、種類により取得内容が異なります。textContentは汎用的ですが、テキストノードの直接の内容はnodeValueで取得可能です。
Dom\Document::loadHTML()のエラーを@演算子で抑制していますが、本番環境ではエラーを隠蔽せず、適切なエラーハンドリングの実装を推奨します。