【PHP8.x】Dom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY定数の使い方
DOCUMENT_POSITION_CONTAINED_BY定数の使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『DOCUMENT_POSITION_CONTAINED_BY定数は、DOMドキュメント内における2つのノードの相対的な位置関係を示すために使用される、あらかじめ定義された整数値を表す定数です。この定数は、主に Dom\Node::compareDocumentPosition() メソッドの返り値に含まれるビットマスク値の一つとして利用されます。このメソッドは、あるノードが別のノードに対してどのような位置にあるかを判定し、その結果を数値で返します。返された数値に DOCUMENT_POSITION_CONTAINED_BY のビットが含まれている場合、それはメソッドを呼び出したノードが、引数として渡されたノードに内包されている、つまり子孫ノードであることを意味します。例えば、$childNode->compareDocumentPosition($parentNode) を実行した際にこの定数が返されたなら、$childNode は $parentNode の直接的または間接的な子要素であると判断できます。この値は他の位置関係を示す定数と組み合わせて返されることがあるため、ビット演算を用いて特定の包含関係を判定する際に使用します。これにより、XMLやHTML文書の複雑な階層構造の中から、ノード間の親子関係や包含関係をプログラムで正確に把握することが可能になります。
構文(syntax)
1Dom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY;
引数(parameters)
引数なし
引数はありません
戻り値(return)
int
DOCUMENT_POSITION_CONTAINED_BY は、このノードが別のノードに含まれていることを示す整数値 1 を返します。
サンプルコード
PHP: Domノード位置比較 - DOCUMENT_POSITION_PRECEDING
1<?php 2 3/** 4 * 2つのDom\ProcessingInstructionノードの相対的な位置を比較し、 5 * キーワードに関連する定数の意味を説明するサンプル関数。 6 * 7 * @param Dom\ProcessingInstruction $nodeA 比較対象となる最初の処理命令ノード 8 * @param Dom\ProcessingInstruction $nodeB 比較対象となる2番目の処理命令ノード 9 * @return void 10 */ 11function compareProcessingInstructionPositions( 12 Dom\ProcessingInstruction $nodeA, 13 Dom\ProcessingInstruction $nodeB 14): void { 15 // compareDocumentPosition() メソッドは、参照ノード (nodeA) と 16 // 引数ノード (nodeB) の相対的な位置を示すビットマスクを返します。 17 $position = $nodeA->compareDocumentPosition($nodeB); 18 19 echo " ノードAとノードBの比較結果 (ビットマスク): {$position}\n"; 20 21 // キーワードに関連する定数: Dom\Node::DOCUMENT_POSITION_PRECEDING 22 // nodeB が nodeA の前に出現する場合に設定されるビット。 23 // 例えば、ドキュメント内で nodeA が nodeB の後に位置する場合に true となります。 24 if (($position & Dom\Node::DOCUMENT_POSITION_PRECEDING) === Dom\Node::DOCUMENT_POSITION_PRECEDING) { 25 echo " - ノードBはノードAの前に位置します (Dom\\Node::DOCUMENT_POSITION_PRECEDING).\n"; 26 } 27 28 // リファレンス情報で指定された定数: Dom\Node::DOCUMENT_POSITION_CONTAINED_BY 29 // nodeA が nodeB に含まれる (nodeB が nodeA の親または祖先である) 場合に設定されるビット。 30 // ProcessingInstructionノード同士の比較では通常発生しませんが、他のノードタイプとの比較では有効です。 31 if (($position & Dom\Node::DOCUMENT_POSITION_CONTAINED_BY) === Dom\Node::DOCUMENT_POSITION_CONTAINED_BY) { 32 echo " - ノードAはノードBに含まれます (Dom\\Node::DOCUMENT_POSITION_CONTAINED_BY).\n"; 33 } 34 35 // その他の主要な位置関係の確認 (参考情報) 36 if (($position & Dom\Node::DOCUMENT_POSITION_FOLLOWING) === Dom\Node::DOCUMENT_POSITION_FOLLOWING) { 37 echo " - ノードBはノードAの後に位置します (Dom\\Node::DOCUMENT_POSITION_FOLLOWING).\n"; 38 } 39 if (($position & Dom::DOCUMENT_POSITION_CONTAINS) === Dom::DOCUMENT_POSITION_CONTAINS) { 40 echo " - ノードAはノードBを含みます (Dom\\Node::DOCUMENT_POSITION_CONTAINS).\n"; 41 } 42 if (($position & Dom\Node::DOCUMENT_POSITION_DISCONNECTED) === Dom\Node::DOCUMENT_POSITION_DISCONNECTED) { 43 echo " - ノードAとノードBは接続されていません (Dom\\Node::DOCUMENT_POSITION_DISCONNECTED).\n"; 44 } 45 if ($position === 0) { 46 echo " - ノードAとノードBは同じノードです。\n"; 47 } 48} 49 50// スクリプトがCLIから実行された場合にのみ、サンプルコードを実行 51if (php_sapi_name() === 'cli') { 52 echo "--- Dom\\ProcessingInstruction ノードの位置比較サンプル ---\n\n"; 53 54 // 新しいDom\Documentを作成 55 $doc = new Dom\Document(); 56 57 // 処理命令ノードを3つ作成し、ドキュメントに追加 58 // ノードは追加された順序でドキュメントツリー内に配置されます 59 $pi1 = $doc->createProcessingInstruction('php', 'echo "Hello, world!";'); 60 $doc->appendChild($pi1); 61 62 $pi2 = $doc->createProcessingInstruction('xml-stylesheet', 'href="style.css" type="text/css"'); 63 $doc->appendChild($pi2); 64 65 // 間に要素ノードを挟むが、ProcessingInstruction同士の比較には直接影響しない 66 $el = $doc->createElement('root'); 67 $doc->appendChild($el); 68 69 $pi3 = $doc->createProcessingInstruction('debug', 'mode="verbose"'); 70 $doc->appendChild($pi3); 71 72 73 echo "ケース1: pi1 と pi2 の比較 (pi2 が pi1 の後に位置)\n"; 74 compareProcessingInstructionPositions($pi1, $pi2); 75 echo "\n"; 76 77 echo "ケース2: pi2 と pi1 の比較 (pi1 が pi2 の前に位置 -> DOCUMENT_POSITION_PRECEDINGが検出されます)\n"; 78 compareProcessingInstructionPositions($pi2, $pi1); 79 echo "\n"; 80 81 echo "ケース3: pi1 と pi3 の比較 (pi3 が pi1 の後に位置)\n"; 82 compareProcessingInstructionPositions($pi1, $pi3); 83 echo "\n"; 84 85 echo "ケース4: pi3 と pi1 の比較 (pi1 が pi3 の前に位置 -> DOCUMENT_POSITION_PRECEDINGが検出されます)\n"; 86 compareProcessingInstructionPositions($pi3, $pi1); 87 echo "\n"; 88 89 echo "ケース5: pi1 と pi1 の比較 (同じノード)\n"; 90 compareProcessingInstructionPositions($pi1, $pi1); 91 echo "\n"; 92}
このサンプルコードは、PHPのDOM拡張機能を利用して、XMLやHTMLドキュメント内の「処理命令ノード」(Dom\ProcessingInstruction)の相対的な位置関係を比較する方法を説明しています。compareProcessingInstructionPositions関数は、比較対象となる2つの処理命令ノード($nodeAと$nodeB)を引数として受け取り、それらの位置関係を判定し、結果を標準出力に表示します。この関数は特に値を返しません(void)。
関数内では、$nodeA->compareDocumentPosition($nodeB)メソッドが使用されます。このメソッドは、参照ノード$nodeAと引数ノード$nodeBの相対的な位置を示す整数値(ビットマスク)を返します。このビットマスクに対して、ビット論理AND演算子&と特定の定数を用いることで、具体的な位置関係を判別できます。
リファレンス情報で挙げられているDom\Node::DOCUMENT_POSITION_CONTAINED_BYは、「$nodeAが$nodeBに含まれる($nodeBが$nodeAの親または祖先である)」場合に設定されるビットです。しかし、処理命令ノード同士では親子関係が発生しないため、この定数が直接検出されることは通常ありません。一方、キーワードとして指定されたDom\Node::DOCUMENT_POSITION_PRECEDINGは、「$nodeBが$nodeAの前に位置する」場合に設定されるビットで、ドキュメント内での出現順序を判断するのに役立ちます。サンプルコードの実行部分では、これらの定数やその他の位置関係がどのように判定されるかを具体的なノードの配置で示しています。
compareDocumentPosition() メソッドは、複数のノードの位置関係をビットマスクとして返します。そのため、特定の定数で位置を判定する際は、ビット論理積(&)演算子を必ず使用してください。Dom\Node::DOCUMENT_POSITION_PRECEDING は、メソッドの引数に指定したノードが、基準となるノードよりドキュメントツリー上で先に位置する場合に有効となります。一方、Dom\Node::DOCUMENT_POSITION_CONTAINED_BY は、通常 Dom\ProcessingInstruction ノードのような包含関係を持たないノード同士の比較では設定されません。この定数は、主に親要素と子要素のような包含関係を持つノード間で有効です。これらの定数は Dom\Node クラスに定義されており、クラス名を用いてアクセスします。
DOMノードの包含関係を判定する
1<?php 2 3/** 4 * Dom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY 定数を使用して、 5 * 2つのDOMノード間の包含関係を判定するサンプルコードです。 6 * 7 * この定数は、DOMNode::compareDocumentPosition() メソッドの戻り値の一部として使われ、 8 * あるノードが別のノードに含まれている状態(子孫である、または同じノードである)を示します。 9 */ 10function checkNodeContainment(): void 11{ 12 // 1. DOM ドキュメントと要素の準備 13 // XML構造をロードし、ノード間の親子・兄弟関係を定義します。 14 $dom = new Dom\DomDocument(); 15 $dom->loadXML('<root><parent><child/></parent><sibling/></root>'); 16 17 // 比較対象となるノードを取得します。 18 // PHP 8ではDom名前空間のクラスを使用します。 19 $parent = $dom->getElementsByTagName('parent')->item(0); 20 $child = $dom->getElementsByTagName('child')->item(0); 21 $sibling = $dom->getElementsByTagName('sibling')->item(0); 22 23 echo "--- ノードの包含関係の確認 ---" . PHP_EOL; 24 25 // ケース1: 子ノードが親ノードに含まれるか? 26 // $child ノードから $parent ノードを比較し、 27 // $child が $parent に「含まれている (CONTAINED_BY)」かを判定します。 28 // ($child は $parent の子孫なので、結果は true になります) 29 if ($child && $parent) { 30 $position = $child->compareDocumentPosition($parent); 31 if (($position & Dom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY) === Dom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY) { 32 echo "✅ 'child' は 'parent' に含まれています (CONTAINED_BY)." . PHP_EOL; 33 } else { 34 echo "❌ 'child' は 'parent' に含まれていません (CONTAINED_BY)." . PHP_EOL; 35 } 36 } 37 38 // ケース2: 親ノードが子ノードに含まれるか? (逆の関係) 39 // $parent ノードから $child ノードを比較し、 40 // $parent が $child に「含まれている (CONTAINED_BY)」かを判定します。 41 // ($parent は $child の親であり、子孫ではないため、結果は false になります) 42 if ($parent && $child) { 43 $position = $parent->compareDocumentPosition($child); 44 if (($position & Dom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY) === Dom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY) { 45 echo "✅ 'parent' は 'child' に含まれています (CONTAINED_BY)." . PHP_EOL; 46 } else { 47 echo "❌ 'parent' は 'child' に含まれていません (CONTAINED_BY)." . PHP_EOL; 48 } 49 } 50 51 // ケース3: 兄弟ノードが互いに含まれるか? 52 // $child ノードから $sibling ノードを比較し、 53 // $child が $sibling に「含まれている (CONTAINED_BY)」かを判定します。 54 // (これらは兄弟関係であり、一方が他方に含まれることはないため、結果は false になります) 55 if ($child && $sibling) { 56 $position = $child->compareDocumentPosition($sibling); 57 if (($position & Dom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY) === Dom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY) { 58 echo "✅ 'child' は 'sibling' に含まれています (CONTAINED_BY)." . PHP_EOL; 59 } else { 60 echo "❌ 'child' は 'sibling' に含まれていません (CONTAINED_BY)." . PHP_EOL; 61 } 62 } 63} 64 65// 上記の関数を実行し、結果を表示します。 66checkNodeContainment();
PHP 8におけるDom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY定数は、2つのDOMノード間の包含関係を判定する際に使用されます。この定数自体は引数を取らず、整数型の値を持ちます。主にDOMNode::compareDocumentPosition()メソッドの戻り値と組み合わせて利用されます。
DOMNode::compareDocumentPosition()メソッドは、呼び出し元のノードと引数で指定したノードの相対的な位置関係を示す整数値を返します。この戻り値は複数の位置関係を示すビットフラグの組み合わせであり、DOCUMENT_POSITION_CONTAINED_BY定数は、呼び出し元のノードが引数で指定されたノードに「含まれている」状態、つまり呼び出し元が引数の子孫であるか、または両者が同じノードであることを示します。
サンプルコードでは、まず<root><parent><child/></parent><sibling/></root>というXML構造を持つDOMドキュメントを準備し、比較対象となるparent、child、siblingノードを取得しています。
次に、$child->compareDocumentPosition($parent)のようにメソッドを呼び出し、$childが$parentに対してどのような位置関係にあるかを調べます。戻り値とDom\ProcessingInstruction::DOCUMENT_POSITION_CONTAINED_BY定数をビットAND演算子 (&) で比較することで、「$childが$parentに含まれているか」を正確に判定できます。例えば、$childは$parentの子孫であるため、この関係は真となります。一方で、$parentが$childに含まれるか、あるいは兄弟ノードが互いに含まれるかの判定では、包含関係がないため偽となります。このように、この定数を用いることで、DOMツリー内のノードの親子関係や包含関係をプログラムで効率的に検証することが可能です。
この定数は DOMNode::compareDocumentPosition() メソッドの戻り値の一部であり、複数の状態がビットマスクで返されるため、特定の状態を判定するにはビットAND演算子 (&) を使う必要があります。
DOCUMENT_POSITION_CONTAINED_BY は、「メソッドを呼び出したノードが、引数で渡したノードに含まれている」状態を示します。どちらのノードを起点に比較するかによって結果が逆になるため、期待する包含関係を正しく判断し、比較するノードの順序に注意してください。
また、getElementsByTagName などでノードを取得する際、該当する要素が存在しない場合は null が返されます。ノードを操作する前には、必ず取得したノードが null でないかを確認するようにしましょう。PHP 8ではDOM関連のクラスが Dom 名前空間に移動している点も補足として重要です。