【PHP8.x】Dom\ProcessingInstruction::nextSiblingプロパティの使い方
nextSiblingプロパティの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『nextSiblingプロパティは、DOMツリーにおいて、現在の処理命令ノードの直後に位置する兄弟ノードを保持するプロパティです。』
PHPのDom拡張機能は、XMLやHTML文書をプログラムで操作するために、文書全体を階層的なツリー構造として表現します。このツリーは、要素、テキスト、コメントなど、文書を構成する様々な部分に対応する「ノード」の集まりで成り立っています。同じ親ノードに属するノード同士は「兄弟ノード」と呼ばれ、文書に記述された順序で並んでいます。このnextSiblingプロパティは、現在のノードのすぐ隣、つまり直後に存在する兄弟ノードを取得するために使用されます。もし現在のノードが親にとって最後の子ノードであり、次に続く兄弟ノードが存在しない場合は、このプロパティはnullを返します。したがって、このプロパティを使って取得したノードを操作する前には、nullでないことを確認する処理が一般的に必要です。このプロパティは読み取り専用であり、特定のノードから順に兄弟ノードをたどっていくような処理を実装する際に役立ちます。
構文(syntax)
1<?php 2 3// 処理命令を含むXML文字列を準備します。 4$xml = '<?xml version="1.0" encoding="UTF-8"?> 5<?php-instruction data?> 6<root>Hello</root>'; 7 8// DOMDocumentオブジェクトを作成し、XMLを読み込みます。 9$doc = new DOMDocument(); 10$doc->loadXML($xml); 11 12// 最初の処理命令ノードを取得します (<?php-instruction data?>)。 13$pi = $doc->getElementsByTagName('php-instruction')->item(0); 14 15// ProcessingInstructionノードの次の兄弟ノードを取得します。 16// この場合、改行とインデントのテキストノードが取得されます。 17// さらにその次の兄弟ノードが <root> 要素になります。 18$next = $pi->nextSibling->nextSibling; 19 20// 次の兄弟ノードのノード名を出力します。 21// 出力: root 22echo $next->nodeName; 23 24?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
?Dom\Node
このプロパティは、現在のノードの後に続く兄弟ノードを返します。後続の兄弟ノードが存在しない場合は、null を返します。
サンプルコード
PHP DOM: processing-instruction の nextSibling を取得する
1<?php 2 3declare(strict_types=1); 4 5/** 6 * Dom\ProcessingInstruction の nextSibling プロパティの使用例を示します。 7 * 8 * XMLドキュメント内の処理命令ノードを探し、 9 * その直後にある兄弟ノードを取得して情報を表示します。 10 */ 11function demonstratePiNextSibling(): void 12{ 13 // サンプルXML文字列を定義します。 14 // `<?php-pi ...?>` が処理命令(Processing Instruction)ノードです。 15 // 整形のための改行やインデントは、テキストノードとして解釈される点に注意してください。 16 $xmlString = <<<XML 17<?xml version="1.0" encoding="UTF-8"?> 18<root> 19 <item>First Item</item> 20 <?php-pi process-data?> 21 <item>Second Item</item> 22</root> 23XML; 24 25 // DOMDocumentオブジェクトを作成し、XMLを読み込みます。 26 // PHP 8.0以降で推奨される新しい `Dom` 名前空間のクラスを使用します。 27 $dom = new \Dom\Document(); 28 $dom->loadXML($xmlString); 29 30 // XPathを使用して処理命令ノードを特定します。 31 // `processing-instruction('php-pi')` は、ターゲットが 'php-pi' のノードを選択します。 32 $xpath = new \Dom\XPath($dom); 33 $piNodeList = $xpath->query("//processing-instruction('php-pi')"); 34 35 // 処理命令ノードが存在するか確認します。 36 if ($piNodeList->length > 0) { 37 /** @var \Dom\ProcessingInstruction $piNode */ 38 $piNode = $piNodeList[0]; 39 40 echo "処理命令ノードが見つかりました:\n"; 41 echo " ターゲット: " . $piNode->target . "\n"; // php-pi 42 echo " データ: " . $piNode->data . "\n\n"; // process-data 43 44 // nextSibling プロパティを使って、次の兄弟ノードを取得します。 45 $sibling = $piNode->nextSibling; 46 47 // 次の兄弟ノードが存在するかどうかを確認します。 48 if ($sibling) { 49 echo "nextSibling プロパティで取得したノードの情報:\n"; 50 51 // nextSiblingは要素だけでなく、テキスト(改行や空白)もノードとして取得します。 52 if ($sibling->nodeType === XML_TEXT_NODE) { 53 echo " 種類: テキストノード\n"; 54 echo " ノード名: " . $sibling->nodeName . "\n"; // #text 55 echo " 値(改行とスペース): " . json_encode($sibling->nodeValue) . "\n\n"; 56 57 // テキストノードのさらに次の兄弟ノードを取得することもできます。 58 $nextElement = $sibling->nextSibling; 59 if ($nextElement) { 60 echo "テキストノードのさらに次の兄弟ノードの情報:\n"; 61 echo " 種類: 要素ノード\n"; 62 echo " ノード名: " . $nextElement->nodeName . "\n"; // item 63 echo " テキスト: " . $nextElement->textContent . "\n"; // Second Item 64 } 65 } else { 66 echo " 種類: 要素ノードまたはその他\n"; 67 echo " ノード名: " . $sibling->nodeName . "\n"; 68 } 69 } else { 70 echo "この処理命令ノードには次の兄弟ノードがありません。\n"; 71 } 72 } else { 73 echo "対象の処理命令ノードが見つかりませんでした。\n"; 74 } 75} 76 77// 関数を実行します。 78demonstratePiNextSibling();
このPHPコードは、XML文書内にある特定のノードの「すぐ隣の兄弟ノード」を取得する方法を示します。ここでは Dom\ProcessingInstruction クラスの nextSibling プロパティを使用します。
nextSibling は、あるノードと同じ階層にあり、その直後に現れるノード(兄弟ノード)を取得するためのプロパティです。引数は必要なく、プロパティにアクセスするだけで値を取得できます。戻り値は、次の兄弟ノードが存在すればそのノードオブジェクト (Dom\Node) を、存在しなければ null を返します。
サンプルコードでは、まず <?php-pi process-data?> という処理命令ノードをXML文書から探し出します。そして、このノードの nextSibling プロパティを使って、次の兄弟ノードを取得しています。
ここで重要なのは、XML内の改行や空白も「テキストノード」として扱われる点です。そのため、処理命令ノードの直後にあるのは、次の <item> 要素ではなく、その間にある改行とスペースで構成されたテキストノードになります。コードでは、このテキストノードを取得したあと、さらにそのノードの nextSibling を調べることで、目的の <item> 要素を取得しています。このように nextSibling を使うと、ノードを順番にたどることができます。
nextSiblingプロパティは、XML内の改行や空白も「テキストノード」として取得する点に注意が必要です。サンプルコードのように要素間に改行があると、次の要素ではなく、その改行文字を含むテキストノードが返されます。期待する要素ノードを取得するには、取得したノードの種類をnodeTypeプロパティで確認し、テキストノードならさらにnextSiblingを呼び出すといった処理が必要になる場合があります。また、対象ノードが最後の子である場合、次の兄弟は存在しないためnextSiblingはnullを返します。プロパティを利用する際は、必ずnullでないことを確認してから操作を行うようにしてください。
PHP DOM ProcessingInstructionのnextSiblingを取得する
1<?php 2 3/** 4 * Dom\ProcessingInstruction の nextSibling プロパティの使用例を示します。 5 * 6 * この関数は、XMLドキュメント内の処理命令ノードを特定し、 7 * その直後にある兄弟ノード(nextSibling)を取得して情報を表示します。 8 */ 9function showProcessingInstructionNextSibling(): void 10{ 11 // 処理命令 (<?xml-stylesheet ...?>) を含むHTML文字列を定義 12 $htmlString = <<<HTML 13<!DOCTYPE html> 14<html> 15<head> 16 <meta charset="UTF-8"> 17 <?xml-stylesheet type="text/css" href="style.css"?> 18 <title>サンプルページ</title> 19</head> 20<body> 21 <h1>こんにちは</h1> 22</body> 23</html> 24HTML; 25 26 // DOMDocument オブジェクトを作成 27 $dom = new DOMDocument(); 28 29 // HTML を安全に読み込む 30 // LIBXML_NOERROR を指定して、HTML5 の警告などを抑制 31 @$dom->loadHTML($htmlString, LIBXML_NOERROR); 32 33 // 処理命令ノード (ProcessingInstruction) を見つける 34 $processingInstructionNode = null; 35 36 // <head> タグの子ノードを順番に調べる 37 $head = $dom->getElementsByTagName('head')->item(0); 38 if ($head) { 39 foreach ($head->childNodes as $node) { 40 // ノードが処理命令 (DOMProcessingInstruction) かどうかを判定 41 if ($node instanceof DOMProcessingInstruction) { 42 $processingInstructionNode = $node; 43 break; // 最初に見つかったものを使用 44 } 45 } 46 } 47 48 // 処理命令ノードが見つかった場合 49 if ($processingInstructionNode) { 50 echo "処理命令ノードが見つかりました。\n"; 51 echo "ターゲット: " . $processingInstructionNode->target . "\n"; // "xml-stylesheet" 52 echo "データ: " . $processingInstructionNode->data . "\n\n"; // 'type="text/css" href="style.css"' 53 54 // nextSibling プロパティで、処理命令の「次の兄弟ノード」を取得します。 55 // HTML構造上、改行などがテキストノードとして解釈されることがあるため、 56 // 要素ノードが見つかるまで次の兄弟をたどります。 57 $nextSiblingNode = $processingInstructionNode->nextSibling; 58 while ($nextSiblingNode && $nextSiblingNode->nodeType === XML_TEXT_NODE) { 59 $nextSiblingNode = $nextSiblingNode->nextSibling; 60 } 61 62 // 次の兄弟ノードが存在するかどうかを確認 63 if ($nextSiblingNode) { 64 echo "nextSibling プロパティで次の兄弟ノードを取得しました。\n"; 65 // このサンプルでは <title> タグが該当します。 66 echo "ノード名: " . $nextSiblingNode->nodeName . "\n"; // "title" 67 echo "ノードの内容: " . $nextSiblingNode->textContent . "\n"; // "サンプルページ" 68 } else { 69 echo "次の兄弟ノードは見つかりませんでした。\n"; 70 } 71 } else { 72 echo "処理命令ノードが見つかりませんでした。\n"; 73 } 74} 75 76// 関数を実行して結果を表示 77showProcessingInstructionNextSibling();
Dom\ProcessingInstructionクラスのnextSiblingプロパティは、XMLやHTMLドキュメント内にある特定の処理命令ノードを基準として、その直後にある兄弟ノードを取得するために使用します。兄弟ノードとは、同じ親要素を持つ、隣同士のノード(要素、テキストなど)のことです。
このサンプルコードでは、まずHTMLドキュメントの中から<?xml-stylesheet ...?>という処理命令ノードを探し出します。そして、見つかった処理命令ノードに対してnextSiblingプロパティを呼び出すことで、そのすぐ後ろに位置するノードの取得を試みています。
HTMLでは、要素と要素の間にある改行や空白文字も「テキストノード」として解釈される場合があります。そのため、このコードでは目的の要素ノード(この場合は<title>タグ)が見つかるまで、不要なテキストノードを読み飛ばす処理を行っています。最終的に、処理命令の次に配置されている<title>ノードを取得し、そのタグ名と内部のテキスト(「サンプルページ」)を表示します。
このプロパティに引数はありません。戻り値として、直後に兄弟ノードが存在する場合はそのノードを表すDom\Nodeオブジェクトが返されます。もし対象が最後のノードで次に兄弟ノードが存在しない場合はnullが返されます。
nextSiblingプロパティを利用する際、HTMLソースコード上の改行や空白文字も「テキストノード」として取得される点に注意が必要です。このため、次のタグ(要素ノード)を期待していても、間に存在するテキストノードが返されることがあります。サンプルコードのように、取得したノードの種類(nodeType)を確認し、意図しないノードであればさらに次の兄弟ノードをたどる処理を実装するのが安全です。また、次の兄弟ノードが存在しない場合、このプロパティはnullを返します。nullに対して操作を行うとエラーが発生するため、利用前には必ずnullでないことを確認してください。