Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【PHP8.x】DOMEntityReference::DOCUMENT_POSITION_FOLLOWING定数の使い方

DOCUMENT_POSITION_FOLLOWING定数の使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

『DOCUMENT_POSITION_FOLLOWING定数は、2つのDOMノードの位置関係を比較した結果、対象ノードが基準ノードよりも文書内で後に続くことを表す定数です』 この定数は、主にDOMNode::compareDocumentPosition()メソッドの戻り値として使用されるビットマスク値の一つです。DOM(Document Object Model)は、HTMLやXML文書をプログラムが操作できるツリー構造として表現する仕組みであり、文書内の各要素やテキストは「ノード」というオブジェクトとして扱われます。compareDocumentPosition()メソッドは、あるノードが別のノードに対して、文書の読み取り順でどのような位置にあるか(前か、後か、内包しているかなど)を判定するために利用されます。このメソッドを呼び出した際、比較対象のノードが基準となるノードの後に位置している場合に、戻り値にDOCUMENT_POSITION_FOLLOWINGのフラグが含まれます。戻り値は複数の状態を同時に示す場合があるため、この定数と完全一致(==)で比較するのではなく、ビット単位のAND演算子(&)を用いてフラグの有無を確認することが推奨されます。これにより、ノード間の正確な順序関係をプログラムで確実に判断できます。

構文(syntax)

1<?php
2
3echo DOMEntityReference::DOCUMENT_POSITION_FOLLOWING;

引数(parameters)

引数なし

引数はありません

戻り値(return)

int

DOMEntityReference::DOCUMENT_POSITION_FOLLOWING は、ノードが指定されたノードの後に続くことを示す整数値 1 を返します。

サンプルコード

PHP DOMノード位置比較と定数利用

1<?php
2
3/**
4 * DOMNode::compareDocumentPosition() メソッドと、その戻り値で使用される定数
5 * (DOMNode::DOCUMENT_POSITION_FOLLOWING, DOMNode::DOCUMENT_POSITION_PRECEDING など)
6 * の使い方を示すサンプルコードです。
7 *
8 * DOMEntityReference は DOMNode を継承しているため、DOMEntityReference のインスタンスも
9 * compareDocumentPosition() メソッドを呼び出し、これらの定数を結果として受け取ることができます。
10 *
11 * この関数は、2つのDOMノードのドキュメント内での相対位置を比較し、結果を出力します。
12 */
13function compareDomNodesPosition(): void
14{
15    // 比較対象となるシンプルなHTML文字列を定義
16    $html = <<<HTML
17<!DOCTYPE html>
18<html>
19<body>
20    <div id="container">
21        <p id="firstParagraph">最初の段落。</p>
22        <span id="targetSpan">ターゲットスパン。</span>
23        <p id="secondParagraph">2番目の段落。</p>
24    </div>
25</body>
26</html>
27HTML;
28
29    // DOMDocument オブジェクトを作成し、HTMLをロード
30    $dom = new DOMDocument();
31    // HTMLのパースエラーや警告を抑制します
32    @$dom->loadHTML($html);
33
34    // 比較するDOMノードをIDで取得
35    // エラーチェックのため、nullの場合は処理を中断
36    $firstParagraph = $dom->getElementById('firstParagraph');
37    $targetSpan = $dom->getElementById('targetSpan');
38    $secondParagraph = $dom->getElementById('secondParagraph');
39    $container = $dom->getElementById('container');
40
41    if (!$firstParagraph || !$targetSpan || !$secondParagraph || !$container) {
42        echo "エラー: 必要なDOMノードが見つかりませんでした。\n";
43        return;
44    }
45
46    echo "--- DOMノードの相対位置比較 --- \n\n";
47
48    // ---------------------------------------------------------------------
49    // 例1: targetSpan と firstParagraph の比較
50    // targetSpan (後) と firstParagraph (前)
51    // compareDocumentPosition() は、呼び出し元のノード (targetSpan) から見て
52    // 引数で渡されたノード (firstParagraph) がどの位置にあるかを返します。
53    // ---------------------------------------------------------------------
54    echo "比較: 'targetSpan' を基準に 'firstParagraph' の位置を確認\n";
55    $result1 = $targetSpan->compareDocumentPosition($firstParagraph);
56
57    // DOMNode::DOCUMENT_POSITION_PRECEDING (値: 2)
58    // 比較対象のノードが参照ノードの「前」に位置することを示します。
59    // targetSpan から見て firstParagraph は「前」にあります。
60    if ($result1 & DOMNode::DOCUMENT_POSITION_PRECEDING) {
61        echo "  - 'firstParagraph' は 'targetSpan' の前に位置します。\n";
62    }
63
64    // DOMNode::DOCUMENT_POSITION_FOLLOWING (値: 4)
65    // 比較対象のノードが参照ノードの「後」に位置することを示します。
66    // targetSpan から見て firstParagraph は「後」ではありません。
67    if ($result1 & DOMNode::DOCUMENT_POSITION_FOLLOWING) {
68        echo "  - 'firstParagraph' は 'targetSpan' の後に位置します。(これは誤り)\n";
69    } else {
70        echo "  - 'firstParagraph' は 'targetSpan' の後に位置しません。\n";
71    }
72    echo "  (ビットマスクの生の値: " . $result1 . ")\n\n";
73
74    // ---------------------------------------------------------------------
75    // 例2: firstParagraph と targetSpan の比較 (逆順)
76    // firstParagraph (前) と targetSpan (後)
77    // ---------------------------------------------------------------------
78    echo "比較: 'firstParagraph' を基準に 'targetSpan' の位置を確認\n";
79    $result2 = $firstParagraph->compareDocumentPosition($targetSpan);
80
81    // firstParagraph から見て targetSpan は「前」ではありません。
82    if ($result2 & DOMNode::DOCUMENT_POSITION_PRECEDING) {
83        echo "  - 'targetSpan' は 'firstParagraph' の前に位置します。(これは誤り)\n";
84    } else {
85        echo "  - 'targetSpan' は 'firstParagraph' の前に位置しません。\n";
86    }
87
88    // firstParagraph から見て targetSpan は「後」にあります。
89    if ($result2 & DOMNode::DOCUMENT_POSITION_FOLLOWING) {
90        echo "  - 'targetSpan' は 'firstParagraph' の後に位置します。\n";
91    }
92    echo "  (ビットマスクの生の値: " . $result2 . ")\n\n";
93
94    // ---------------------------------------------------------------------
95    // 例3: 親子関係の比較
96    // container と firstParagraph
97    // ---------------------------------------------------------------------
98    echo "比較: 'container' を基準に 'firstParagraph' の位置を確認 (親子関係)\n";
99    $result3 = $container->compareDocumentPosition($firstParagraph);
100
101    // DOMNode::DOCUMENT_POSITION_CONTAINS (値: 8)
102    // 比較対象のノードが参照ノードを「内包」していることを示します。
103    // container は firstParagraph を内包しています。
104    if ($result3 & DOMNode::DOCUMENT_POSITION_CONTAINS) {
105        echo "  - 'container' は 'firstParagraph' を内包しています。\n";
106    }
107
108    // DOMNode::DOCUMENT_POSITION_CONTAINED_BY (値: 16)
109    // 比較対象のノードが参照ノードに「内包されている」ことを示します。
110    // container は firstParagraph に内包されていません。
111    if ($result3 & DOMNode::DOCUMENT_POSITION_CONTAINED_BY) {
112        echo "  - 'container' は 'firstParagraph' に内包されています。(これは誤り)\n";
113    } else {
114        echo "  - 'container' は 'firstParagraph' に内包されていません。\n";
115    }
116    echo "  (ビットマスクの生の値: " . $result3 . ")\n\n";
117}
118
119// 関数を実行してDOMノードの比較結果を表示
120compareDomNodesPosition();
121

このサンプルコードは、PHPのDOM拡張機能において、DOMノードのドキュメント内での相対位置を比較する方法を示しています。具体的には、DOMNodeクラスが提供するcompareDocumentPosition()メソッドと、その戻り値で使用される定数(例えばDOMNode::DOCUMENT_POSITION_FOLLOWINGDOMNode::DOCUMENT_POSITION_PRECEDINGなど)の利用方法を紹介しています。

compareDocumentPosition()メソッドは、引数として別のDOMノードを受け取り、呼び出し元のノード(参照ノード)から見て引数で渡されたノードがドキュメント内でどのような位置関係にあるかを整数値(ビットマスク)で返します。この戻り値は複数の状態を同時に示すことがあり、それぞれの状態は専用の定数によって表現されます。例えば、DOMNode::DOCUMENT_POSITION_FOLLOWINGは、比較対象のノードが参照ノードの「後」に位置することを示し、DOMNode::DOCUMENT_POSITION_PRECEDINGは「前」に位置することを示します。また、DOMNode::DOCUMENT_POSITION_CONTAINSは、参照ノードが比較対象ノードを「内包」している関係を示します。

コードでは、シンプルなHTML文字列からDOMツリーを構築し、異なるノード間の位置関係を比較しています。そして、compareDocumentPosition()の戻り値に対してビット演算子(&)を使用することで、これらの定数を用いてノード間の詳細な関係性を判定できることを示しています。なお、DOMEntityReferenceクラスはDOMNodeを継承しているため、DOMEntityReferenceのインスタンスもこのcompareDocumentPosition()メソッドを呼び出し、同様にこれらの定数を結果として利用できることを理解するのに役立ちます。この機能は、複雑なDOM構造をプログラムで操作する際に、ノード間の関係性を正確に把握するために非常に有用です。

このサンプルコードは、DOMノード間の相対位置を比較する compareDocumentPosition() メソッドの使い方を示しています。このメソッドは、呼び出し元ノードを基準として引数ノードの位置関係を判断しますので、どちらから見ているかを意識しましょう。戻り値は単一の値ではなく、複数の位置関係を示すビットマスクの組み合わせです。定数との比較には & (ビットAND演算子) を必ず使用してください。DOCUMENT_POSITION_FOLLOWING は引数ノードが後、DOCUMENT_POSITION_PRECEDING は前にあることを意味します。DOMDocument::loadHTML()でエラー抑制をしていても、getElementById()nullを返すことがあるため、取得したノードの存在チェックは必ず行い、安全なコード作成を心がけてください。

PHP POSTリクエストでのDOMノード位置比較

1<?php
2
3/**
4 * 与えられたXML/HTML文字列をパースし、指定された2つのノードの位置関係を比較します。
5 *
6 * この関数は、HTTP POST リクエストでXML/HTMLデータを受け取った際の処理を想定しています。
7 * DOMNode::compareDocumentPosition メソッドを使用して、ノードAがノードBの後に続くか(DOCUMENT_POSITION_FOLLOWING)などを判定します。
8 *
9 * 注意:`DOCUMENT_POSITION_FOLLOWING` は `DOMNode` クラスの定数です。
10 * `DOMEntityReference` クラスも `DOMNode` を継承しているため、
11 * `DOMEntityReference` のインスタンスも `compareDocumentPosition` メソッドの比較対象となり、
12 * この定数によって返される結果を処理できます。
13 *
14 * @param string $xmlString パースするXMLまたはHTMLの文字列。通常、POSTデータとして提供されます。
15 * @param string $nodeXPath1 比較する最初のノードを特定するためのXPath式。
16 * @param string $nodeXPath2 比較する2番目のノードを特定するためのXPath式。
17 * @return string 比較結果のメッセージ。エラーの場合はエラーメッセージを返します。
18 * @post この関数は、POSTリクエストでXML/HTMLデータが送信されるシナリオを意図しています。
19 */
20function compareDomNodePositions(string $xmlString, string $nodeXPath1, string $nodeXPath2): string
21{
22    $dom = new DOMDocument();
23    // エラーを抑制し、DOMDocumentがHTMLを解析する際に出る警告を防ぎます。
24    libxml_use_internal_errors(true);
25    // HTMLとしてロードします (XMLでも動作しますが、ここではHTMLを想定)。
26    // ドキュメント構造が正しくない場合でもパースを試みるフラグも追加しています。
27    $dom->loadHTML($xmlString, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
28    libxml_clear_errors(); // エラーをクリア
29
30    $xpath = new DOMXPath($dom);
31
32    // XPathでノードを検索します
33    $nodeList1 = $xpath->query($nodeXPath1);
34    $nodeList2 = $xpath->query($nodeXPath2);
35
36    // ノードが存在しない場合のチェック
37    if ($nodeList1->length === 0 || $nodeList2->length === 0) {
38        return "エラー: 指定されたXPathに一致するノードが見つかりませんでした。";
39    }
40
41    $node1 = $nodeList1->item(0);
42    $node2 = $nodeList2->item(0);
43
44    // 取得したノードがDOMNodeのインスタンスであることを確認します。
45    // DOMNode::compareDocumentPositionはDOMNodeオブジェクトに対して機能します。
46    if (!$node1 instanceof DOMNode || !$node2 instanceof DOMNode) {
47        return "エラー: XPathで取得された要素が有効なDOMNodeではありません。";
48    }
49
50    // ノード間の位置関係を比較します。
51    // 戻り値はビットマスクであり、複数の定数を組み合わせて結果を示すことがあります。
52    $position = $node1->compareDocumentPosition($node2);
53
54    // 各ビットマスク定数と比較して、具体的な結果を判定します。
55    // `DOCUMENT_POSITION_FOLLOWING` は、最初のノードが2番目のノードの後に続く場合にセットされます。
56    if (($position & DOMNode::DOCUMENT_POSITION_FOLLOWING) === DOMNode::DOCUMENT_POSITION_FOLLOWING) {
57        return "最初のノードは2番目のノードの後に続きます。";
58    } elseif (($position & DOMNode::DOCUMENT_POSITION_PRECEDING) === DOMNode::DOCUMENT_POSITION_PRECEDING) {
59        return "最初のノードは2番目のノードの前にあります。";
60    } elseif (($position & DOMNode::DOCUMENT_POSITION_CONTAINS) === DOMNode::DOCUMENT_POSITION_CONTAINS) {
61        return "最初のノードが2番目のノードを含んでいます。";
62    } elseif (($position & DOMNode::DOCUMENT_POSITION_CONTAINED_BY) === DOMNode::DOCUMENT_POSITION_CONTAINED_BY) {
63        return "最初のノードが2番目のノードに含まれています。";
64    } elseif (($position & DOMNode::DOCUMENT_POSITION_DISCONNECTED) === DOMNode::DOCUMENT_POSITION_DISCONNECTED) {
65        return "ノードは別のドキュメントまたは関連性のないサブツリーにあります。";
66    } elseif (($position & DOMNode::DOCUMENT_POSITION_SAME_NODE) === DOMNode::DOCUMENT_POSITION_SAME_NODE) {
67        return "2つのノードは同じです。";
68    } else {
69        return "ノードの位置関係を特定できませんでした。";
70    }
71}
72
73// --- サンプルコードの実行例 ---
74// 実際のアプリケーションでは、HTTP POSTリクエストのボディからXML/HTMLデータを受け取ることを想定します。
75// 例: $htmlContent = $_POST['html_data'] ?? '';
76$htmlContent = <<<HTML
77<!DOCTYPE html>
78<html>
79<head>
80    <title>DOM Position Test</title>
81</head>
82<body>
83    <div id="container">
84        <!-- これはコメントです -->
85        <p class="first-paragraph">最初の段落</p>
86        <span>スパン要素</span>
87        <p class="second-paragraph">2番目の段落</p>
88        <a href="#">リンク</a>
89    </div>
90</body>
91</html>
92HTML;
93
94// PHPDocの `@param` タグと、上記コメントの `@post` 説明に沿って、
95// ここでは直接引数を渡し、HTMLコンテンツも変数として定義しています。
96
97echo "--- ノード位置比較テスト ---\n\n";
98
99// ケース1: 最初の<p>タグが2番目の<p>タグの後に続くか? (期待: FOLLOWING)
100// 結果は「最初のノードは2番目のノードの後に続きます。」となります。
101echo "ケース1 (DOCUMENT_POSITION_FOLLOWING): 最初の段落 と 2番目の段落\n";
102echo compareDomNodePositions($htmlContent, "//p[@class='first-paragraph']", "//p[@class='second-paragraph']") . "\n\n";
103
104// ケース2: 2番目の<p>タグが最初の<p>タグの後に続くか? (期待: PRECEDING)
105// 結果は「最初のノードは2番目のノードの前にあります。」となります。
106echo "ケース2 (DOCUMENT_POSITION_PRECEDING): 2番目の段落 と 最初の段落\n";
107echo compareDomNodePositions($htmlContent, "//p[@class='second-paragraph']", "//p[@class='first-paragraph']") . "\n\n";
108
109// ケース3: #container が最初の<p>タグを含んでいるか? (期待: CONTAINS)
110// 結果は「最初のノードが2番目のノードを含んでいます。」となります。
111echo "ケース3 (DOCUMENT_POSITION_CONTAINS): #container と 最初の段落\n";
112echo compareDomNodePositions($htmlContent, "//div[@id='container']", "//p[@class='first-paragraph']") . "\n\n";
113
114// ケース4: 最初の<p>タグが #container に含まれているか? (期待: CONTAINED_BY)
115// 結果は「最初のノードが2番目のノードに含まれています。」となります。
116echo "ケース4 (DOCUMENT_POSITION_CONTAINED_BY): 最初の段落 と #container\n";
117echo compareDomNodePositions($htmlContent, "//p[@class='first-paragraph']", "//div[@id='container']") . "\n\n";
118
119// ケース5: 同じノードの比較 (期待: SAME_NODE)
120// 結果は「2つのノードは同じです。」となります。
121echo "ケース5 (DOCUMENT_POSITION_SAME_NODE): 最初の段落 と 最初の段落\n";
122echo compareDomNodePositions($htmlContent, "//p[@class='first-paragraph']", "//p[@class='first-paragraph']") . "\n\n";
123
124// ケース6: 見つからないノード
125// 結果は「エラー: 指定されたXPathに一致するノードが見つかりませんでした。」となります。
126echo "ケース6 (ノードNotFound): 存在しないノード と 最初の段落\n";
127echo compareDomNodePositions($htmlContent, "//p[@class='non-existent']", "//p[@class='first-paragraph']") . "\n\n";

このPHPサンプルコードは、与えられたXMLまたはHTML文字列を解析し、その中に含まれる二つのノード間の位置関係を比較するものです。具体的には、compareDomNodePositions関数が、HTTP POSTリクエストで受け取ったデータを処理するシナリオを想定して設計されています。この関数は、解析対象のHTML/XML文字列と、比較するノードを特定するためのXPath式を二つ引数として受け取ります。

関数内部では、DOMDocumentDOMXPathというPHPの拡張機能を使ってドキュメントを読み込み、指定されたXPath式に基づいてノードを検索します。そして、見つかったノードに対してDOMNode::compareDocumentPositionメソッドを呼び出し、二つのノードがドキュメント内でどのように配置されているかを判断します。このメソッドの戻り値は整数値のビットマスクであり、複数の状態を組み合わせて示されます。

リファレンス情報にあるDOCUMENT_POSITION_FOLLOWINGは、最初のノードが二番目のノードの後に続く場合に結果に含まれる定数で、DOMNodeクラスの定数として広く利用されます。DOMEntityReferenceクラスもDOMNodeを継承しているため、そのインスタンスもこの比較の対象となります。関数は、この定数などを用いて具体的な位置関係を判定し、「最初のノードは2番目のノードの後に続きます」といった結果メッセージを文字列で返します。PHPDocの@paramタグにより、引数の役割が明確に記述されています。

PHPのDOM操作において、DOCUMENT_POSITION_FOLLOWING定数は、リファレンスに記載のDOMEntityReferenceだけでなく、基底クラスであるDOMNodeの定数として利用されます。compareDocumentPositionメソッドの戻り値はビットマスク形式のため、特定の状態を判定するには&演算子を使って正確な位置関係を評価する必要があります。このサンプルコードはPOSTデータ処理を想定していますが、実運用では外部からの入力値(XML/HTML文字列など)に対する厳格な検証とサニタイズを必ず行い、セキュリティ脆弱性を防いでください。libxml_use_internal_errorsでエラーを抑制している場合でも、パースエラーの検知と適切なエラーハンドリングの実装が安全な運用には不可欠です。XPathの記述ミスもノード特定失敗の原因となるため注意が必要です。

関連コンテンツ

関連IT用語

関連プログラミング言語