【PHP8.x】DOMAttr::DOCUMENT_POSITION_CONTAINED_BY定数の使い方
DOCUMENT_POSITION_CONTAINED_BY定数の使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『DOCUMENT_POSITION_CONTAINED_BY定数は、DOMドキュメント内における2つのノードの位置関係を示すために用いられる定数です。この定数は、DOMNode::compareDocumentPosition() メソッドが返すビットマスク値の一つとして定義されています。このメソッドは、あるノードが別のノードに対してどのような位置にあるか、例えば先行しているか、後続しているか、あるいは内包しているかなどを判定するために使用されます。compareDocumentPosition() の実行結果のビットマスクに DOCUMENT_POSITION_CONTAINED_BY が含まれている場合、それはメソッドを呼び出したノードが、引数で渡されたノードに内包されている、つまり子孫ノードであることを意味します。例えば、$childNode->compareDocumentPosition($parentNode) という形式で呼び出した際に、この定数が結果に含まれていれば $childNode は $parentNode の子や孫にあたります。この定数は他の位置関係を示す定数と組み合わせて返されることがあるため、特定の関係を判定するにはビット演算子 & を用いて、返り値にこの定数のビットが含まれているかを確認する必要があります。これにより、DOMツリーの複雑な階層構造をプログラムで正確に把握することが可能になります。』
構文(syntax)
1<?php 2 3$dom = new DOMDocument(); 4$dom->loadXML('<book><chapter title="Introduction" /></book>'); 5 6// 親要素ノードを取得 7$bookNode = $dom->getElementsByTagName('book')->item(0); 8 9// 属性ノードを取得 10$titleAttr = $dom->getElementsByTagName('chapter')->item(0)->getAttributeNode('title'); 11 12// 属性ノードが親要素ノードに含まれているか比較 13$position = $bookNode->compareDocumentPosition($titleAttr); 14 15// 比較結果が「含まれている」状態かビットマスクで判定 16if ($position & DOMAttr::DOCUMENT_POSITION_CONTAINED_BY) { 17 // 属性は要素に含まれているため、この条件は true になる 18 // DOMAttr::DOCUMENT_POSITION_CONTAINED_BY の値は 16 19 echo "The title attribute is contained by the book element."; 20} 21 22?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
戻り値なし
戻り値はありません
サンプルコード
DOMノードの相対位置をDOCUMENT_POSITION_PRECEDINGで比較する
1<?php 2 3/** 4 * 2つのDOMノードのドキュメント内での相対位置を比較するサンプルコード。 5 * 6 * DOMNode::compareDocumentPosition メソッドは、呼び出し元のノードと引数で指定されたノードが 7 * ドキュメント内でどのような関係にあるかを示すビットマスクを返します。 8 * このサンプルでは、主に DOMNode::DOCUMENT_POSITION_PRECEDING と 9 * DOMNode::DOCUMENT_POSITION_CONTAINED_BY 定数を使用して、ノードの位置関係を判別する方法を示します。 10 * 11 * @return void 12 */ 13function demonstrateDomNodePositionComparison(): void 14{ 15 // 1. 新しいDOMドキュメントを作成します。 16 $dom = new DOMDocument(); 17 $dom->formatOutput = true; // 生成されるXMLを見やすく整形するための設定 18 19 // 2. ルート要素 'root' を作成し、ドキュメントに追加します。 20 $root = $dom->createElement('root'); 21 $dom->appendChild($root); 22 23 // 3. 比較対象となる複数のノードを作成し、ドキュメント構造に追加します。 24 // これらのノードの位置関係を比較します。 25 $nodeA = $dom->createElement('elementA', 'これは要素Aです。'); 26 $root->appendChild($nodeA); 27 28 $nodeB = $dom->createElement('elementB', 'これは要素Bです。'); 29 $root->appendChild($nodeB); 30 31 $nodeC = $dom->createElement('elementC', 'これは要素Cです (Aの子要素)。'); 32 $nodeA->appendChild($nodeC); // ノードCをノードAの子として追加 33 34 echo "--- ノードの位置関係の比較結果 ---\n"; 35 echo " (左側のノードが '自身', 右側のノードが '比較対象' です)\n\n"; 36 37 // --- ケース1: 並列ノードの比較 (ノードA vs ノードB) --- 38 // ドキュメント構造: <root><elementA/><elementB/></root> 39 // 自身(A)は比較対象(B)の前に位置しています。 40 echo "1. ノードA (自身) と ノードB (比較対象) の比較:\n"; 41 $positionAB = $nodeA->compareDocumentPosition($nodeB); 42 43 // DOMNode::DOCUMENT_POSITION_FOLLOWING は、自身が比較対象の後に続くことを示します。 44 // この場合、ノードBはノードAの後に続くため、ノードAから見るとノードBは「後に続く」関係です。 45 if (($positionAB & DOMNode::DOCUMENT_POSITION_FOLLOWING) !== 0) { 46 echo " - 結果: 自身 (ノードA) は比較対象 (ノードB) の「前に」位置しています。\n"; 47 echo " (補足: 比較対象が自身より後に続くことを示します)\n"; 48 } 49 // DOCUMENT_POSITION_PRECEDING は、比較対象が自身より前に位置することを示します。 50 // このケースでは当てはまりません。 51 if (($positionAB & DOMNode::DOCUMENT_POSITION_PRECEDING) !== 0) { 52 // このブロックは実行されません 53 } 54 echo "\n"; 55 56 // --- ケース2: 並列ノードの逆方向比較 (ノードB vs ノードA) --- 57 // ドキュメント構造: <root><elementA/><elementB/></root> 58 // 自身(B)は比較対象(A)の後に位置しています。 59 echo "2. ノードB (自身) と ノードA (比較対象) の比較:\n"; 60 $positionBA = $nodeB->compareDocumentPosition($nodeA); 61 62 // DOMNode::DOCUMENT_POSITION_PRECEDING は、比較対象が自身より前に位置することを示します。 63 // この場合、ノードAはノードBの前に位置するため、この条件が当てはまります。 64 if (($positionBA & DOMNode::DOCUMENT_POSITION_PRECEDING) !== 0) { 65 echo " - 結果: 自身 (ノードB) より比較対象 (ノードA) が「前に」位置しています。\n"; 66 } 67 echo "\n"; 68 69 // --- ケース3: 親子ノードの比較 (ノードA vs ノードC) --- 70 // ドキュメント構造: <root><elementA><elementC/></elementA><elementB/></root> 71 // 自身(A)は比較対象(C)を含んでいます。 72 echo "3. ノードA (自身) と ノードC (比較対象) の比較:\n"; 73 $positionAC = $nodeA->compareDocumentPosition($nodeC); 74 75 // DOMNode::DOCUMENT_POSITION_CONTAINS は、自身が比較対象を含んでいることを示します。 76 if (($positionAC & DOMNode::DOCUMENT_POSITION_CONTAINS) !== 0) { 77 echo " - 結果: 自身 (ノードA) は比較対象 (ノードC) を「含んでいます」。\n"; 78 } 79 // DOCUMENT_POSITION_CONTAINED_BY は、自身が比較対象に含まれていることを示します。 80 // このケースでは当てはまりません (逆の関係)。 81 if (($positionAC & DOMNode::DOCUMENT_POSITION_CONTAINED_BY) !== 0) { 82 // このブロックは実行されません 83 } 84 echo "\n"; 85 86 // --- ケース4: 子親ノードの比較 (ノードC vs ノードA) --- 87 // ドキュメント構造: <root><elementA><elementC/></elementA><elementB/></root> 88 // 自身(C)は比較対象(A)に含まれています。 89 echo "4. ノードC (自身) と ノードA (比較対象) の比較:\n"; 90 $positionCA = $nodeC->compareDocumentPosition($nodeA); 91 92 // DOMNode::DOCUMENT_POSITION_CONTAINED_BY は、自身が比較対象に含まれていることを示します。 93 // この場合、ノードCはノードAの子であるため、ノードAに含まれています。 94 if (($positionCA & DOMNode::DOCUMENT_POSITION_CONTAINED_BY) !== 0) { 95 echo " - 結果: 自身 (ノードC) は比較対象 (ノードA) に「含まれています」。\n"; 96 } 97 echo "\n"; 98 99 // 参考情報として、この関数内で生成されたXMLドキュメントの内容を表示します。 100 echo "--- 生成されたXMLドキュメント (参考) ---\n"; 101 echo $dom->saveXML(); 102} 103 104// 上記で定義した関数を実行し、DOMノードの比較結果を出力します。 105demonstrateDomNodePositionComparison(); 106
PHPのDOMNode::compareDocumentPositionメソッドは、HTMLやXML文書のツリー構造内で、二つのノードがどのような相対的な位置関係にあるかを判断するために使用されます。このメソッドは、呼び出し元のノード(自身)と引数で指定されたノード(比較対象)を比較し、その関係性を示す整数値のビットマスクを戻り値として返します。
このビットマスクは複数の状態を同時に表現でき、特定の定数と論理AND演算子(&)を用いて比較することで、具体的な関係性を判別します。例えば、DOMNode::DOCUMENT_POSITION_CONTAINED_BY定数は、呼び出し元のノードが比較対象のノードに「含まれている」場合に、ビットマスクに含まれます。これは、子要素が親要素と比較されるような親子関係で成立します。一方、DOMNode::DOCUMENT_POSITION_PRECEDING定数は、比較対象のノードが呼び出し元のノードより「前に位置している」場合に、ビットマスクに含まれます。これは、同じ階層のノード間で、比較対象の方が文書内で先に登場する場合に当てはまります。
他にも、DOMNode::DOCUMENT_POSITION_CONTAINS定数は、自身が比較対象のノードを「含んでいる」場合を示し、DOMNode::DOCUMENT_POSITION_FOLLOWING定数は、比較対象のノードが自身より「後に位置している」場合を示します。これらの定数を活用することで、複雑な文書構造の中から特定のノードを検索したり、ノード間の依存関係を処理したりする際に、正確な位置関係に基づいたプログラミングが可能になります。
PHPのDOMノード位置比較では、compareDocumentPositionメソッドの戻り値がビットマスクであるため、&演算子でDOMNode::DOCUMENT_POSITION_PRECEDINGなどの定数フラグを確認する点が重要です。DOMAttrとDOMNodeでは同じ名前の定数でも所属クラスが異なる場合があり、誤用すると予期せぬ動作を招くため、使用する際は必ず公式ドキュメントで意図する定数の正確な定義と所属を確認してください。また、比較の際にメソッド呼び出し元のノード(自身)と引数で渡されたノード(比較対象)どちらの視点から関係を見ているのかを明確に理解することが、DOMNode::DOCUMENT_POSITION_CONTAINED_BYなどの結果を正しく解釈する上で不可欠です。
PHP DOMノードの包含関係を判定する
1<?php 2 3/** 4 * 2つのDOMノードの親子関係を比較し、 5 * 最初のノードが2番目のノードに含まれているかどうかを判定する関数です。 6 * 7 * DOMAttr::DOCUMENT_POSITION_CONTAINED_BY 定数は、DOMツリーにおけるノードの位置関係を示すために使われます。 8 * この定数は、DOMNode::compareDocumentPosition() メソッドの戻り値と組み合わせて使用し、 9 * 呼び出し元のノード($node1)が、引数で渡されたノード($node2)に 10 * 含まれている(つまり、$node2が$node1の祖先である)場合にセットされるビットフラグを示します。 11 * 12 * @param DOMNode $node1 比較する最初のノード(子ノード候補) 13 * @param DOMNode $node2 比較する2番目のノード(親ノード候補) 14 * @return bool $node1が$node2に含まれている($node2が$node1の祖先である)場合にtrue、それ以外はfalseを返します。 15 */ 16function isContainedBy(DOMNode $node1, DOMNode $node2): bool 17{ 18 // DOMNode::compareDocumentPosition() メソッドは、2つのノード間の位置関係を示すビットマスクを返します。 19 // ビットマスクとは、複数の情報を1つの数値にまとめたものです。 20 $position = $node1->compareDocumentPosition($node2); 21 22 // ビットAND演算子 (&) を使用して、戻り値に DOMAttr::DOCUMENT_POSITION_CONTAINED_BY が含まれているかを確認します。 23 // PHPのリファレンス情報に従い、DOMAttr:: を使用しています。 24 // 結果が定数自身と等しい場合、その関係性($node1が$node2に含まれている)が存在することを示します。 25 return ($position & DOMAttr::DOCUMENT_POSITION_CONTAINED_BY) === DOMAttr::DOCUMENT_POSITION_CONTAINED_BY; 26} 27 28// === サンプルコードの実行部分 === 29 30// DOMDocumentオブジェクトを作成し、HTMLコンテンツをロードします。 31// これはHTML文書をPHPで操作するための準備です。 32$dom = new DOMDocument(); 33// loadHTML() は完全なHTML文書としてパースするため、<html><body>タグを含めています。 34$htmlContent = ' 35 <html> 36 <body> 37 <div id="container"> 38 <p id="paragraph">これは段落です。</p> 39 <span id="spanElement">これはスパン要素です。</span> 40 </div> 41 <section id="anotherSection"> 42 <a href="#">リンク</a> 43 </section> 44 </body> 45 </html> 46'; 47$dom->loadHTML($htmlContent); 48 49// 操作対象となるDOMノードをHTMLのタグ名で取得します。 50// item(0) は最初に見つかった要素、item(1) は次に見つかった要素を指します。 51$containerDiv = $dom->getElementsByTagName('div')->item(0); // id="container" のdiv要素 52$paragraph = $dom->getElementsByTagName('p')->item(0); // id="paragraph" のp要素 53$spanElement = $dom->getElementsByTagName('span')->item(0); // id="spanElement" のspan要素 54$anotherSection = $dom->getElementsByTagName('section')->item(0); // id="anotherSection" のsection要素 55 56echo "--- DOMAttr::DOCUMENT_POSITION_CONTAINED_BY の使用例 ---\n\n"; 57 58// 例1: 子ノード(paragraph)が親ノード(containerDiv)に含まれているか? 59// 期待される結果: true (p要素はdiv要素の内部にあるため) 60if (isContainedBy($paragraph, $containerDiv)) { 61 echo "✔ 'paragraph' ノードは 'containerDiv' ノードに含まれています。\n"; 62} else { 63 echo "❌ 'paragraph' ノードは 'containerDiv' ノードに含まれていません。\n"; 64} 65 66// 例2: 親ノード(containerDiv)が子ノード(paragraph)に含まれているか? 67// 期待される結果: false (親ノードが子ノードに含まれることはありません) 68if (isContainedBy($containerDiv, $paragraph)) { 69 echo "❌ 'containerDiv' ノードは 'paragraph' ノードに含まれています。\n"; 70} else { 71 echo "✔ 'containerDiv' ノードは 'paragraph' ノードに含まれていません。\n"; 72} 73 74// 例3: 兄弟ノード(spanElement)が別の兄弟ノード(paragraph)に含まれているか? 75// 期待される結果: false (兄弟ノードは互いに含まれていません) 76if (isContainedBy($spanElement, $paragraph)) { 77 echo "❌ 'spanElement' ノードは 'paragraph' ノードに含まれています。\n"; 78} else { 79 echo "✔ 'spanElement' ノードは 'paragraph' ノードに含まれていません。\n"; 80} 81 82// 例4: 異なる親を持つノード(paragraph)が別のセクションノード(anotherSection)に含まれているか? 83// 期待される結果: false (異なる祖先を持つため) 84if (isContainedBy($paragraph, $anotherSection)) { 85 echo "❌ 'paragraph' ノードは 'anotherSection' ノードに含まれています。\n"; 86} else { 87 echo "✔ 'paragraph' ノードは 'anotherSection' ノードに含まれていません。\n"; 88}
このサンプルコードは、ウェブページのHTML構造(DOMツリー)において、あるDOMノードが別のDOMノードの中に含まれているかどうかを判定する方法を示しています。DOMAttr::DOCUMENT_POSITION_CONTAINED_BY定数は、あるDOMノードが別のDOMノードの中に含まれている(つまり、その子孫である)状態を示すビットフラグです。
コード内のisContainedBy関数は、最初のノード($node1)が2番目のノード($node2)の子孫であるかを確認します。この関数は引数として二つのDOMNodeオブジェクトを受け取り、$node1が$node2に含まれている場合にtrueを、それ以外の場合はfalseをブール値として返します。
関数内部では、DOMNode::compareDocumentPosition()メソッドが使用され、二つのノード間の相対的な位置関係を示すビットマスク(複数の情報をまとめた数値)が取得されます。その後、ビットAND演算子&を用いて、取得したビットマスクにDOMAttr::DOCUMENT_POSITION_CONTAINED_BY定数が含まれているかをチェックします。この定数がビットマスクに含まれていれば、$node1は$node2の子孫であると判断されます。
実際の実行部分では、サンプルHTMLをロードして複数のDOMノードを取得し、isContainedBy関数を使って様々なノード間の関係性を比較しています。例えば、段落ノードがその親であるdivノードに含まれているか(true)、あるいは親ノードが子ノードに含まれているか(false)、異なる親を持つノード同士の関係性(false)などが具体的に示されており、この定数とメソッドの動作を理解することができます。
DOMAttr::DOCUMENT_POSITION_CONTAINED_BY定数は、その名前からDOMAttrクラスに直接関連すると誤解されがちですが、実際にはDOMNode::compareDocumentPosition()メソッドが返すビットマスクを解析するための汎用的なビットフラグとして利用されます。この定数は、呼び出し元のノード($node1)が引数で渡されたノード($node2)に「含まれている」、つまり$node2が$node1の祖先である関係を判定するものです。ノードの包含関係の方向を間違えないよう、引数の順序と定数の意味をしっかり理解することが重要です。また、複数の情報をまとめたビットマスクから特定の状態を検出するために&(ビットAND)演算子が使用されており、この演算の仕組みを理解しておくと、より安全にコードを利用できます。getElementsByTagName()は常にDOMNodeListを返すため、目的のノードを取得するにはitem(0)のようにインデックスを指定する必要がある点も覚えておきましょう。