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

【PHP8.x】Dom\Node::previousSiblingプロパティの使い方

previousSiblingプロパティの使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

『previousSiblingプロパティは、DOMツリーにおいて、現在のノードの直前に位置する兄弟ノードを保持するプロパティです。兄弟ノードとは、同じ親ノードを共有するノード群のことを指します。このプロパティにアクセスすると、現在のノードの一つ前に存在するノードをDom\Nodeオブジェクトとして取得することができます。もし直前に兄弟ノードが存在しない場合、つまり現在のノードがその親における最初の子ノードである場合には、このプロパティはnullを返します。このnullが返される特性は、特定のノードから先頭に向かって、すべての兄弟ノードを順番にたどるようなループ処理の終了条件として頻繁に利用されます。このプロパティが返すノードは、要素ノードに限らず、要素間の空白や改行を含むテキストノードや、コメントノードなども対象となります。そのため、HTMLドキュメントを操作する際には、意図しないテキストノードが取得される可能性がある点に注意が必要です。なお、このプロパティは読み取り専用であり、値の代入によってノードの順序を変更することはできません。

構文(syntax)

1<?php
2
3$html = '<ul><li>First</li><li>Second</li></ul>';
4
5$dom = new \Dom\Document();
6@$dom->loadHTML($html);
7
8// 2番目の <li> 要素を取得します
9$secondLi = $dom->getElementsByTagName('li')[1];
10
11// 2番目の <li> 要素の直前の兄弟ノード (最初の <li> 要素) を取得します
12$previousNode = $secondLi->previousSibling;
13
14// 取得したノードのテキスト内容を出力します
15echo $previousNode->textContent; // "First"
16
17?>

引数(parameters)

引数なし

引数はありません

戻り値(return)

?Dom\Node

現在のノードの直前の兄弟ノード、または兄弟ノードが存在しない場合は null を返します。

サンプルコード

PHP Dom\Node::previousSibling で直前兄弟ノードを取得する

1<?php
2
3/**
4 * 指定されたHTML文字列から特定のIDを持つ要素を見つけ、
5 * その直前の兄弟ノードの情報を表示します。
6 *
7 * Dom\Node::previousSibling プロパティは、現在のノードの直前にある兄弟ノードを返します。
8 * 直前の兄弟ノードが存在しない場合(例: 親ノードの最初の子要素である場合)は null を返します。
9 *
10 * @param string $html HTML文字列
11 * @param string $id 対象ノードのID
12 * @return void
13 */
14function demonstrateDomNodePreviousSibling(string $html, string $id): void
15{
16    // Dom\Document オブジェクトを初期化
17    $dom = new Dom\Document();
18
19    // HTML文字列のロード時に発生する警告を抑制し、後でエラー情報をクリア
20    // これにより、不完全なHTMLでもスクリプトが中断せずに続行できます
21    libxml_use_internal_errors(true);
22    $dom->loadHTML($html);
23    libxml_clear_errors(); // 読み込みエラー情報をクリア
24
25    // 指定されたIDを持つ要素ノードを検索
26    $targetNode = $dom->getElementById($id);
27
28    if (!$targetNode) {
29        echo "エラー: ID '{$id}' を持つノードが見つかりませんでした。\n";
30        return;
31    }
32
33    echo "--- 対象ノードの情報 ---\n";
34    echo "ノード名: " . $targetNode->nodeName . "\n";
35    echo "内容: '" . trim($targetNode->textContent) . "'\n"; // ノードの内容をトリムして表示
36
37    // Dom\Node::previousSibling プロパティを使用して直前の兄弟ノードを取得
38    // 戻り値は ?Dom\Node で、存在しない場合は null になります。
39    $previousSiblingNode = $targetNode->previousSibling;
40
41    echo "\n--- previousSibling の結果 ---\n";
42    if ($previousSiblingNode) {
43        echo "直前の兄弟ノードが見つかりました。\n";
44        echo "  ノードの種類 (Type): " . $previousSiblingNode->nodeType . " (Dom\Node::ELEMENT_NODE は 1)\n";
45        echo "  ノード名: " . $previousSiblingNode->nodeName . "\n";
46        echo "  ノード内容: '" . trim($previousSiblingNode->textContent) . "'\n";
47    } else {
48        echo "直前の兄弟ノードは見つかりませんでした。\n";
49        echo "対象ノードが親ノードの最初の子要素である可能性があります。\n";
50    }
51}
52
53// サンプルHTML文字列
54// 要素間に余計な改行や空白を入れないことで、previousSibling が意図した要素ノードを返しやすくなります。
55$sampleHtml = <<<HTML
56<!DOCTYPE html>
57<html>
58<body>
59    <div>
60        <p>最初の段落</p><span>中間要素</span><a id="target_link">ターゲットリンク</a><span>最後の要素</span>
61    </div>
62    <hr>
63    <div>
64        <span id="first_child_target">最初の子要素</span><p>続く段落</p>
65    </div>
66    <div>
67        <!-- コメントノード --><span id="target_after_comment">コメント後の要素</span>
68    </div>
69</body>
70</html>
71HTML;
72
73// 実行例 1: 直前の要素兄弟ノードがある場合
74echo "=== 実行例 1: 直前の要素兄弟ノードがある場合 ===\n";
75demonstrateDomNodePreviousSibling($sampleHtml, 'target_link');
76echo "\n\n";
77
78// 実行例 2: 直前の兄弟ノードがない(親の最初の子要素)の場合
79echo "=== 実行例 2: 直前の兄弟ノードがない場合 ===\n";
80demonstrateDomNodePreviousSibling($sampleHtml, 'first_child_target');
81echo "\n\n";
82
83// 実行例 3: 直前にコメントノードがある場合 (コメントノードも兄弟ノードとして扱われる)
84echo "=== 実行例 3: 直前にコメントノードがある場合 ===\n";
85demonstrateDomNodePreviousSibling($sampleHtml, 'target_after_comment');
86echo "\n\n";
87
88// 実行例 4: 対象ノードが存在しない場合
89echo "=== 実行例 4: 対象ノードが存在しない場合 ===\n";
90demonstrateDomNodePreviousSibling($sampleHtml, 'non_existent_id');
91
92?>

PHP 8のDom\Node::previousSiblingプロパティは、ウェブページなどのHTML構造をプログラムで操作する際に役立つ機能です。このプロパティは、特定のノード(要素やテキストなど)の「直前にある兄弟ノード」を取得するために使用します。兄弟ノードとは、同じ親ノードを持つ隣接するノードのことです。

このプロパティは引数を取らず、戻り値として?Dom\Node型を返します。これは、直前の兄弟ノードが存在すればそのDom\Nodeオブジェクトが返され、存在しない場合(例えば、対象ノードが親ノードの最初の子要素である場合)にはnullが返されることを意味します。

サンプルコードでは、まずHTML文字列から指定されたIDを持つ要素を検索し、そのノードが持つpreviousSiblingプロパティを利用して直前の兄弟ノードを取得しています。実行例を通じて、直前の兄弟ノードが要素ノードである場合、直前の兄弟ノードが存在しない場合、さらにはコメントノードのような要素以外のノードも兄弟として扱われる様子が示されています。これにより、HTML構造内の要素の相対的な位置関係を簡単に把握し、プログラムでアクセスできるようになります。特に、テキストノードやコメントノードも兄弟として認識される点に注意が必要です。

previousSiblingは現在のノードの直前にある兄弟ノードを返しますが、HTMLタグだけでなく、改行や空白などもテキストノードとして認識される点に注意が必要です。HTMLソースの整形によっては、意図しないテキストノードが返されることがありますので、要素間の余分な空白や改行をなくすことで、期待する要素ノードを得やすくなります。直前の兄弟ノードが存在しない場合はnullを返すため、取得した値を利用する前には必ずnullチェックを行ってください。また、Dom\Document::loadHTMLでHTMLを読み込む際は、libxml_use_internal_errorsを使用してエラーを一時的に抑制することで、不完全なHTMLでもスクリプトが中断せずに処理を続行できます。

PHP Dom\Node::previousSiblingで前の兄弟ノードを取得する

1<?php
2
3/**
4 * Dom\Node::previousSibling プロパティの使用例を示します。
5 * このプロパティは、現在のノードの直前の兄弟ノード(同じ親を持つ直前のノード)を返します。
6 * 直前の兄弟ノードが存在しない場合は null を返します。
7 *
8 * システムエンジニアを目指す初心者にも分かりやすいよう、基本的な使い方に焦点を当てています。
9 */
10function demonstratePreviousSibling(): void
11{
12    // サンプルXMLデータを作成します。
13    // 要素間に改行やインデントがありますが、DOMDocumentの設定でこれらをテキストノードとして扱わないようにします。
14    $xmlString = <<<XML
15    <root>
16        <item id="item1">First Item</item>
17        <item id="item2">Second Item</item>
18        <item id="item3">Third Item</item>
19    </root>
20    XML;
21
22    // DOMドキュメントを作成し、XMLをロードします。
23    $dom = new Dom\Document();
24    // preserveWhiteSpace を false に設定することで、要素間の空白文字(改行やスペース)を
25    // テキストノードとしてパースしないようにします。
26    // これにより、previousSibling が要素ノードを返すことを期待できます。
27    $dom->preserveWhiteSpace = false;
28    $dom->loadXML($xmlString);
29
30    echo "--- 'item3' ノードの前の兄弟ノードを確認 ---" . PHP_EOL;
31
32    // idが 'item3' のノードを取得します。
33    $targetNodeItem3 = $dom->getElementById('item3');
34
35    if ($targetNodeItem3 instanceof Dom\Node) {
36        echo "現在のターゲットノード: <" . $targetNodeItem3->nodeName . "> (id: " . $targetNodeItem3->getAttribute('id') . ")" . PHP_EOL;
37        echo "このノードの 'previousSibling' を取得します..." . PHP_EOL;
38
39        // previousSibling プロパティにアクセスし、直前の兄弟ノードを取得します。
40        $previousNode = $targetNodeItem3->previousSibling;
41
42        // 戻り値が Dom\Node のインスタンスか、null かをチェックします。
43        if ($previousNode instanceof Dom\Node) {
44            echo "-> 見つかった前の兄弟ノード: <" . $previousNode->nodeName . "> (id: " . ($previousNode->hasAttribute('id') ? $previousNode->getAttribute('id') : 'なし') . ")" . PHP_EOL;
45        } else {
46            echo "-> 前の兄弟ノードは見つかりませんでした (null が返されました)。" . PHP_EOL;
47        }
48    } else {
49        echo "エラー: 'item3' ノードが見つかりませんでした。" . PHP_EOL;
50    }
51
52    echo PHP_EOL;
53
54    echo "--- 'item1' ノードの前の兄弟ノードを確認 (最初の兄弟ノードのケース) ---" . PHP_EOL;
55
56    // idが 'item1' のノードを取得します。
57    // これは親要素の子の中で最初のノードです。
58    $targetNodeItem1 = $dom->getElementById('item1');
59
60    if ($targetNodeItem1 instanceof Dom\Node) {
61        echo "現在のターゲットノード: <" . $targetNodeItem1->nodeName . "> (id: " . $targetNodeItem1->getAttribute('id') . ")" . PHP_EOL;
62        echo "このノードの 'previousSibling' を取得します..." . PHP_EOL;
63
64        // previousSibling プロパティにアクセスします。
65        $previousNodeForFirst = $targetNodeItem1->previousSibling;
66
67        // 最初の兄弟ノードには前の兄弟ノードが存在しないため、null が返されるはずです。
68        if ($previousNodeForFirst instanceof Dom\Node) {
69            echo "-> 見つかった前の兄弟ノード: <" . $previousNodeForFirst->nodeName . "> (id: " . ($previousNodeForFirst->hasAttribute('id') ? $previousNodeForFirst->getAttribute('id') : 'なし') . ")" . PHP_EOL;
70        } else {
71            echo "-> 前の兄弟ノードは見つかりませんでした (null が返されました)。これは期待される動作です。" . PHP_EOL;
72        }
73    } else {
74        echo "エラー: 'item1' ノードが見つかりませんでした。" . PHP_EOL;
75    }
76}
77
78// 関数を実行して動作を確認します。
79demonstratePreviousSibling();

PHPのDom\Node::previousSiblingプロパティは、XMLやHTMLなどのDOM構造において、現在のノードの「直前の兄弟ノード」を取得するために使用されます。兄弟ノードとは、同じ親要素を持つノードのことです。このプロパティにアクセスすると、現在のノードのすぐ前にある兄弟ノードがDom\Nodeオブジェクトとして返されます。もし直前の兄弟ノードが存在しない場合(例えば、現在のノードが親要素の最初の子ノードである場合など)は、nullが返されます。このプロパティに引数は必要ありません。

サンプルコードでは、まずXMLデータを作成し、Dom\Documentに読み込んでいます。ここで重要なのは、$dom->preserveWhiteSpace = false;を設定している点です。これにより、XML中の改行やインデントといった空白文字が余計なテキストノードとして扱われず、期待通りに要素ノードのみを兄弟として取得できるようになります。

コードでは、まずitem3ノードを取得し、そのpreviousSiblingプロパティで直前のitem2ノードが取得できることを示しています。次に、最初の兄弟ノードであるitem1previousSiblingを試すと、直前のノードが存在しないためnullが返される動作を確認しています。このように、previousSiblingプロパティはDOMツリーの特定のノードから前方向のノードを辿る際に役立ちます。

Dom\Node::previousSiblingは、直前の兄弟ノードが存在しない場合や、現在のノードが親の子ノードリストの最初である場合はnullを返します。そのため、プロパティにアクセスした後は、必ず戻り値がDom\Nodeのインスタンスであるかnullであるかをif ($node instanceof Dom\Node)のようにチェックし、適切に処理を分岐させてください。

特に注意が必要なのは、XML/HTMLソースの要素間に含まれる改行やインデントなどの空白文字です。これらはデフォルトではテキストノードとして扱われるため、previousSiblingが期待する要素ノードではなく、空白文字のテキストノードを返すことがあります。要素ノードのみを処理したい場合は、サンプルコードのように$dom->preserveWhiteSpace = false;を設定し、空白文字を無視するよう明示的に指示すると安全です。この設定をしないと、意図しないテキストノードが返される可能性があるため、実装時には十分ご注意ください。

関連コンテンツ

関連IT用語

関連プログラミング言語