【PHP8.x】DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY定数の使い方
DOCUMENT_POSITION_CONTAINED_BY定数の使い方について、初心者にもわかりやすく解説します。
基本的な使い方
DOCUMENT_POSITION_CONTAINED_BY定数は、DOMDocumentTypeクラスに所属する定数で、ノード間の関係を表す際に使用されます。具体的には、あるノードが別のノードによって包含されているかどうかを判断するためのフラグとして機能します。DOM(Document Object Model)は、HTMLやXMLドキュメントをプログラムから操作するためのインターフェースであり、ノードはドキュメントを構成する要素、属性、テキストなどを指します。
この定数は、compareDocumentPositionメソッドの結果として返される値の一部として利用されます。compareDocumentPositionメソッドは、2つのノード間の文書順序を比較し、それらの関係を示すビットマスクを返します。DOCUMENT_POSITION_CONTAINED_BY定数がそのビットマスクに含まれている場合、一方のノードが他方のノードに包含されていることを意味します。包含とは、一方のノードが他方のノードの子孫である状態を指します。
例えば、ある要素ノードが別の要素ノードの子要素である場合、compareDocumentPositionメソッドの結果にはDOCUMENT_POSITION_CONTAINED_BY定数が含まれます。この定数を利用することで、DOMツリー内におけるノード間の親子関係や包含関係をプログラムで容易に判断できるようになります。システムエンジニアがDOMを操作する際、ノード間の関係性を正確に把握し、適切な処理を行うために、この定数の理解は重要となります。
構文(syntax)
1DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY
引数(parameters)
引数なし
引数はありません
戻り値(return)
int
DOCUMENT_POSITION_CONTAINED_BY は、あるノードが別のノードに含まれていることを示す整数定数です。
サンプルコード
PHP DOMノード位置比較:DOCUMENT_POSITION_PRECEDING
1<?php 2 3/** 4 * DOMNode::compareDocumentPosition メソッドを使ってノードの位置関係を比較する関数。 5 * DOMNode::DOCUMENT_POSITION_PRECEDING と DOMNode::DOCUMENT_POSITION_CONTAINED_BY 定数、 6 * および関連する他の定数の使用例を初心者にも分かりやすく示します。 7 * 8 * 注: 参照情報では所属クラスが DOMDocumentType となっていますが、 9 * compareDocumentPosition メソッドとこれらの定数は DOMNode クラスに属します。 10 * DOMDocumentType は DOMNode を継承しているため、理論上は使用可能ですが、 11 * 一般的な要素ノードでの比較がより分かりやすい利用例です。 12 */ 13function compareDomNodePositions(): void 14{ 15 // DOMDocument を初期化し、簡単なHTML構造をロードします。 16 $dom = new DOMDocument(); 17 // HTMLのパースエラーを抑制するために内部エラーハンドリングを有効にします。 18 libxml_use_internal_errors(true); 19 // 比較するHTMLコンテンツ。 20 $htmlContent = ' 21 <div id="container"> 22 <p id="firstParagraph">最初の段落です。</p> 23 <p id="secondParagraph">二番目の段落です。</p> 24 <span id="childSpan">これはスパン要素です。</span> 25 </div> 26 <div id="anotherContainer"> 27 <p id="thirdParagraph">三番目の段落です。</p> 28 </div>'; 29 $dom->loadHTML($htmlContent); 30 // エラーハンドリングを無効に戻し、累積されたエラーをクリアします。 31 libxml_clear_errors(); 32 33 // 比較対象となるノードを DOMXPath を使って取得します。 34 // getElementById は DTD がないHTMLでは常に機能するとは限らないため、XPathがより確実です。 35 $xpath = new DOMXPath($dom); 36 37 $container = $xpath->query("//*[@id='container']")->item(0); 38 $firstParagraph = $xpath->query("//*[@id='firstParagraph']")->item(0); 39 $secondParagraph = $xpath->query("//*[@id='secondParagraph']")->item(0); 40 $childSpan = $xpath->query("//*[@id='childSpan']")->item(0); 41 $anotherContainer = $xpath->query("//*[@id='anotherContainer']")->item(0); 42 // 取得したノードがnullでないか確認します。 43 if (!$container || !$firstParagraph || !$secondParagraph || !$childSpan || !$anotherContainer) { 44 echo "必要なDOMノードの一部が見つかりませんでした。HTML構造を確認してください。" . PHP_EOL; 45 return; 46 } 47 48 echo "--- DOMノードの位置関係の比較 ---" . PHP_EOL . PHP_EOL; 49 50 // 例1: firstParagraph と secondParagraph の比較 (後のノード) 51 // secondParagraph は firstParagraph の後に続くノードです。 52 $position1 = $firstParagraph->compareDocumentPosition($secondParagraph); 53 echo "1. firstParagraph と secondParagraph の比較:" . PHP_EOL; 54 echo " \$firstParagraph->compareDocumentPosition(\$secondParagraph) の結果: " . $position1 . PHP_EOL; 55 if ($position1 & DOMNode::DOCUMENT_POSITION_FOLLOWING) { 56 echo " -> secondParagraph は firstParagraph の後に位置します (DOMNode::DOCUMENT_POSITION_FOLLOWING)。" . PHP_EOL; 57 } 58 echo PHP_EOL; 59 60 // 例2: secondParagraph と firstParagraph の比較 (前のノード - キーワード: DOCUMENT_POSITION_PRECEDING) 61 // firstParagraph は secondParagraph の前に存在するノードです。 62 $position2 = $secondParagraph->compareDocumentPosition($firstParagraph); 63 echo "2. secondParagraph と firstParagraph の比較 (キーワード: DOCUMENT_POSITION_PRECEDING):" . PHP_EOL; 64 echo " \$secondParagraph->compareDocumentPosition(\$firstParagraph) の結果: " . $position2 . PHP_EOL; 65 if ($position2 & DOMNode::DOCUMENT_POSITION_PRECEDING) { 66 echo " -> firstParagraph は secondParagraph の前に位置します (DOMNode::DOCUMENT_POSITION_PRECEDING)。" . PHP_EOL; 67 } 68 echo PHP_EOL; 69 70 // 例3: childSpan と container の比較 (含まれるノード - キーワード: DOCUMENT_POSITION_CONTAINED_BY) 71 // container は childSpan を含んでいる親要素です。 72 // この場合、childSpan は container に「含まれています」。 73 $position3 = $childSpan->compareDocumentPosition($container); 74 echo "3. childSpan と container の比較 (キーワード: DOCUMENT_POSITION_CONTAINED_BY):" . PHP_EOL; 75 echo " \$childSpan->compareDocumentPosition(\$container) の結果: " . $position3 . PHP_EOL; 76 if ($position3 & DOMNode::DOCUMENT_POSITION_CONTAINED_BY) { 77 echo " -> container は childSpan を含んでおり、childSpan は container に含まれています (DOMNode::DOCUMENT_POSITION_CONTAINED_BY)。" . PHP_EOL; 78 } 79 // また、container は childSpan の前に位置するため、PRECEDING も含まれる場合があります。 80 if ($position3 & DOMNode::DOCUMENT_POSITION_PRECEDING) { 81 echo " -> container は childSpan の論理的な前に位置します (DOMNode::DOCUMENT_POSITION_PRECEDING)。" . PHP_EOL; 82 } 83 echo PHP_EOL; 84 85 // 例4: container と childSpan の比較 (含むノード) 86 // container は childSpan を含んでいる要素です。 87 // この場合、container は childSpan を「含んでいます」。 88 $position4 = $container->compareDocumentPosition($childSpan); 89 echo "4. container と childSpan の比較 (含むノード):" . PHP_EOL; 90 echo " \$container->compareDocumentPosition(\$childSpan) の結果: " . $position4 . PHP_EOL; 91 if ($position4 & DOMNode::DOCUMENT_POSITION_CONTAINS) { 92 echo " -> container は childSpan を含んでいます (DOMNode::DOCUMENT_POSITION_CONTAINS)。" . PHP_EOL; 93 } 94 // また、childSpan は container の後に位置するため、FOLLOWING も含まれる場合があります。 95 if ($position4 & DOMNode::DOCUMENT_POSITION_FOLLOWING) { 96 echo " -> childSpan は container の論理的な後に位置します (DOMNode::DOCUMENT_POSITION_FOLLOWING)。" . PHP_EOL; 97 } 98 echo PHP_EOL; 99 100 // 例5: container と anotherContainer の比較 (兄弟ノード) 101 // anotherContainer は container の後に続く兄弟ノードです。 102 $position5 = $container->compareDocumentPosition($anotherContainer); 103 echo "5. container と anotherContainer の比較:" . PHP_EOL; 104 echo " \$container->compareDocumentPosition(\$anotherContainer) の結果: " . $position5 . PHP_EOL; 105 if ($position5 & DOMNode::DOCUMENT_POSITION_FOLLOWING) { 106 echo " -> anotherContainer は container の後に位置します (DOMNode::DOCUMENT_POSITION_FOLLOWING)。" . PHP_EOL; 107 } 108 echo PHP_EOL; 109} 110 111// 関数を実行して、ノードの位置関係の比較結果を表示します。 112compareDomNodePositions();
PHPのDOMNode::compareDocumentPositionメソッドは、HTMLやXMLドキュメント内の2つのノードの相対的な位置関係を比較するために使用されます。このメソッドは、比較したい別のDOMNodeオブジェクトを引数にとり、両ノードの関係性を表す整数値を戻り値として返します。この戻り値は、複数の関係性を同時に示すビットマスクとなっており、例えばDOMNode::DOCUMENT_POSITION_CONTAINED_BY定数との論理積(&)を評価することで、呼び出し元のノードが引数のノード内に含まれているかを確認できます。
リファレンスではDOMDocumentTypeクラスの定数として挙げられていますが、実際にはDOMNodeクラス(DOMDocumentTypeもDOMNodeを継承)の定数と共に、DOMNode::compareDocumentPositionメソッドと組み合わせて利用されることが一般的です。キーワードのDOMNode::DOCUMENT_POSITION_PRECEDING定数は、呼び出し元のノードが引数のノードよりもドキュメントツリー上で物理的に前に位置することを示します。
サンプルコードでは、まずHTMLコンテンツをロードしてDOMツリーを構築し、XPathを使って様々なノードを取得します。その後、取得したノード同士をcompareDocumentPositionメソッドで比較し、その戻り値に対してDOCUMENT_POSITION_FOLLOWING、DOCUMENT_POSITION_PRECEDING、DOCUMENT_POSITION_CONTAINED_BY、DOCUMENT_POSITION_CONTAINSなどの定数を使って具体的な関係性を判定し、結果を表示しています。これにより、ノードの親子関係や順序関係をプログラムで効率的に判断する方法を学ぶことができます。
このサンプルコードの注意点として、DOMNode::compareDocumentPositionメソッドの戻り値は、単一の状態を示すものではなく、複数の位置関係を示すビットフラグの組み合わせであるため、特定の定数との比較にはビットAND演算子 (&) を用いる必要があります。リファレンスで定数の所属クラスがDOMDocumentTypeと示されていますが、これらの位置関係定数はDOMNodeクラスに属し、DOMDocumentTypeはDOMNodeを継承しているため利用可能です。ノードの取得はDOMXPathを使用すると確実で、取得したノードがnullでないか常に確認する習慣をつけましょう。DOCUMENT_POSITION_PRECEDINGやDOCUMENT_POSITION_CONTAINED_BYといった定数は、比較対象ノードからの相対的な位置関係を表し、状況によっては複数の状態が同時に返されることを理解しておくことが大切です。また、HTMLをロードする際にはlibxml_use_internal_errors関数を使ってエラー処理を行うと、より安全にコードを運用できます。
PHP DOM: document_position_contains で要素包含関係を判定する
1<?php 2 3/** 4 * DOMNode::compareDocumentPosition() メソッドと 5 * DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY 定数の使用例を示します。 6 * 7 * この定数は、あるDOMノードが、比較対象の別のDOMノードの子孫である(内部に含まれる)場合に、 8 * compareDocumentPosition() メソッドの戻り値に含まれるビットマスク値です。 9 * システムエンジニアを目指す初心者が、DOMツリーにおける要素の包含関係をプログラムで 10 * どのように判定するかを理解するのに役立ちます。 11 */ 12function demonstrateDocumentPositionContainedBy(): void 13{ 14 // DOMDocument オブジェクトを作成し、XML出力を整形するように設定します。 15 // これにより、出力されるXMLが見やすくなります。 16 $dom = new DOMDocument('1.0', 'UTF-8'); 17 $dom->formatOutput = true; 18 19 // DOMツリーの要素を作成し、階層構造を構築します。 20 // <root> 21 // <div> (parentDiv) 22 // <p> (childP) 23 // <span> (grandchildSpan) 24 // </p> 25 // </div> 26 // <div> (siblingDiv) 27 // </root> 28 29 // ルート要素を作成し、DOMドキュメントに追加します。 30 $root = $dom->createElement('root'); 31 $dom->appendChild($root); 32 33 // 親要素を作成し、ルート要素に追加します。 34 $parentDiv = $dom->createElement('div', '親要素'); 35 $root->appendChild($parentDiv); 36 37 // 子要素を作成し、親要素に追加します。 38 $childP = $dom->createElement('p', '子要素'); 39 $parentDiv->appendChild($childP); 40 41 // 孫要素を作成し、子要素に追加します。 42 $grandchildSpan = $dom->createElement('span', '孫要素'); 43 $childP->appendChild($grandchildSpan); 44 45 // 別の兄弟要素を作成し、ルート要素に追加します。 46 $siblingDiv = $dom->createElement('div', '兄弟要素'); 47 $root->appendChild($siblingDiv); 48 49 echo "--- 現在のDOM構造 ---\n"; 50 // 作成したDOM構造をXML形式で出力します。 51 echo $dom->saveXML() . "\n"; 52 echo "-----------------------\n\n"; 53 54 echo "--- ノードの位置関係の比較 (DOCUMENT_POSITION_CONTAINED_BY の判定) ---\n"; 55 56 // 比較ケース1: 子要素 ($childP) が親要素 ($parentDiv) に含まれているか? 57 // DOMNode::compareDocumentPosition() は、呼び出し元のノード ($childP) と 58 // 引数で渡されたノード ($parentDiv) の位置関係を示すビットマスクを返します。 59 $position1 = $childP->compareDocumentPosition($parentDiv); 60 echo "1. <p> (子要素) と <div> (親要素) の比較:\n"; 61 // ビットAND演算子 (&) を使用して、返されたビットマスクに 62 // DOCUMENT_POSITION_CONTAINED_BY が含まれているかを確認します。 63 if (($position1 & DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY) === DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY) { 64 echo " -> 結果: YES. <p> は <div> に含まれています (DOCUMENT_POSITION_CONTAINED_BY).\n"; 65 } else { 66 echo " -> 結果: NO. <p> は <div> に含まれていません (DOCUMENT_POSITION_CONTAINED_BY ではない).\n"; 67 } 68 echo " (compareDocumentPosition() の戻り値: " . $position1 . ")\n\n"; 69 70 // 比較ケース2: 孫要素 ($grandchildSpan) が親要素 ($parentDiv) に含まれているか? 71 // 孫も子孫であるため、DOCUMENT_POSITION_CONTAINED_BY が真となります。 72 $position2 = $grandchildSpan->compareDocumentPosition($parentDiv); 73 echo "2. <span> (孫要素) と <div> (親要素) の比較:\n"; 74 if (($position2 & DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY) === DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY) { 75 echo " -> 結果: YES. <span> は <div> に含まれています (DOCUMENT_POSITION_CONTAINED_BY).\n"; 76 } else { 77 echo " -> 結果: NO. <span> は <div> に含まれていません.\n"; 78 } 79 echo " (compareDocumentPosition() の戻り値: " . $position2 . ")\n\n"; 80 81 // 比較ケース3: 親要素 ($parentDiv) が子要素 ($childP) に含まれているか? (逆の比較) 82 // 親が子に含まれることはないので、DOCUMENT_POSITION_CONTAINED_BY は偽となります。 83 $position3 = $parentDiv->compareDocumentPosition($childP); 84 echo "3. <div> (親要素) と <p> (子要素) の比較:\n"; 85 if (($position3 & DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY) === DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY) { 86 echo " -> 結果: NO. <div> は <p> に含まれていません (DOCUMENT_POSITION_CONTAINED_BY は真ではない).\n"; 87 } else { 88 echo " -> 結果: YES. <div> は <p> に含まれていません (これは正しい結果です).\n"; 89 } 90 echo " (compareDocumentPosition() の戻り値: " . $position3 . ")\n\n"; 91 92 // 比較ケース4: 兄弟要素 ($childP と $siblingDiv) が互いに含まれているか? 93 // 兄弟要素は互いに含まれる関係ではないため、DOCUMENT_POSITION_CONTAINED_BY は偽となります。 94 $position4 = $childP->compareDocumentPosition($siblingDiv); 95 echo "4. <p> (子要素) と <div> (兄弟要素) の比較:\n"; 96 if (($position4 & DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY) === DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY) { 97 echo " -> 結果: NO. <p> は 兄弟<div> に含まれていません (DOCUMENT_POSITION_CONTAINED_BY は真ではない).\n"; 98 } else { 99 echo " -> 結果: YES. <p> は 兄弟<div> に含まれていません (これは正しい結果です).\n"; 100 } 101 echo " (compareDocumentPosition() の戻り値: " . $position4 . ")\n\n"; 102} 103 104// 上記の関数を実行し、サンプルコードを動作させます。 105demonstrateDocumentPositionContainedBy();
このサンプルコードは、PHPのDOM拡張機能において、DOMツリー内のノード間の包含関係を判定する基本的な方法を、DOMNode::compareDocumentPosition()メソッドとDOMDocumentType::DOCUMENT_POSITION_CONTAINED_BY定数を用いて示しています。DOMDocumentType::DOCUMENT_POSITION_CONTAINED_BYは、あるDOMノードが、比較対象の別のDOMノードの子孫、つまり内部に含まれる関係にある場合に、compareDocumentPosition()メソッドが返す整数値(ビットマスク)の一部として現れる定数です。
コードではまず、DOMDocumentオブジェクトを生成し、<root>、<div>、<p>、<span>といった階層的な要素を構築してDOMツリーを作成しています。その後、様々なノードのペアに対してcompareDocumentPosition()メソッドを呼び出し、その戻り値とDOCUMENT_POSITION_CONTAINED_BY定数をビットAND演算子(&)で比較することで、それぞれのノードが互いに含まれる関係にあるかを判定しています。
具体的には、子要素や孫要素が親要素に含まれるケースでは、DOCUMENT_POSITION_CONTAINED_BYの条件が真となります。一方で、親要素が子要素に含まれることはなく、また兄弟ノード同士も含まれる関係ではないため、これらのケースでは条件が偽となることが示されています。compareDocumentPosition()メソッドは、ノード間の位置関係を複合的なビットマスクで表現するため、特定の関係を抽出するにはビットAND演算が不可欠です。システムエンジニアを目指す初心者の方にとって、このサンプルはDOMツリーの構造を理解し、要素の包含関係をプログラムで判定する実用的なスキルを学ぶ良い機会となるでしょう。
DOCUMENT_POSITION_CONTAINED_BY 定数は、DOMツリーにおけるノード間の包含関係、特に「あるノードが別のノードの子孫であるか」を判定する際に、DOMNode::compareDocumentPosition() メソッドの戻り値と組み合わせて使用するビットマスクです。このメソッドを利用する際は、比較の方向性に注意が必要です。例えば、A->compareDocumentPosition(B) はノードAがノードBの子孫であるかを判定するため、比較対象の順番を間違えると意図しない結果となる場合があります。また、compareDocumentPosition() の戻り値は複数の状態を示すビットマスクのため、特定の包含関係を正確に判定するには、ビットAND演算子 (&) を用いて『($戻り値 & 定数) === 定数』の形式で比較を行う必要があります。