【PHP8.x】childNodesプロパティの使い方

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

作成日: 更新日:

基本的な使い方

childNodesプロパティは、Dom\CharacterDataクラスに属する、読み取り専用のプロパティです。このプロパティは、ノードの子ノードをNodeListオブジェクトとして保持します。Dom\CharacterDataは、XMLドキュメントやHTMLドキュメントにおけるテキストノードなどの文字データを扱うための抽象クラスです。childNodesプロパティは、この文字データノードが持つ子ノードのリストへのアクセスを提供します。

NodeListオブジェクトは、ノードの順序付きコレクションを表します。コレクション内の個々のノードには、インデックス番号(0から始まる)を使用してアクセスできます。childNodesプロパティを通じて取得されるNodeListは、ライブリストであるため、ドキュメントの構造が変更されると、リストの内容も自動的に更新されます。

Dom\CharacterDataクラスは、テキストノード、コメントノード、CDATAセクションノードなどの特殊なノードタイプを扱うために使用されます。これらのノードは、テキストデータや特殊な文字データを含んでいるため、childNodesプロパティを通じてアクセスできる子ノードを持つ可能性があります。ただし、テキストノード自体は、通常子ノードを持ちません。childNodesプロパティは、ノードの子ノードを反復処理したり、特定のノードを検索したりするために役立ちます。例えば、ループ処理を使用して、NodeList内のすべてのノードに対して特定の操作を実行できます。このプロパティを使用することで、ドキュメントの構造をプログラム的に解析し、操作することが可能になります。

構文(syntax)

1Dom\CharacterData::$childNodes;

引数(parameters)

引数なし

引数はありません

戻り値(return)

DOMNodeList|null

このプロパティは、指定されたノードの子ノードのリスト、または子ノードがない場合は null を返します。

サンプルコード

PHP DOM childNodesによるCharacterDataの振る舞いを理解する

1<?php
2
3/**
4 * Dom\CharacterDataクラスのchildNodesプロパティの使用例です。
5 *
6 * Dom\CharacterDataは、テキストノード(DOMText)やコメントノード(DOMComment)などの
7 * 親クラスにあたります。これらのノードは構造上、子ノードを持つことができません。
8 * そのため、childNodesプロパティにアクセスすると、常にnullが返されます。
9 *
10 * このサンプルコードでは、要素が持つテキストノードとコメントノードを取得し、
11 * それぞれのchildNodesプロパティがnullであることを確認します。
12 */
13function demonstrateCharacterDataChildNodes(): void
14{
15    // HTML文字列を定義します。
16    // この<p>タグは、子ノードとして「テキストノード」と「コメントノード」を持っています。
17    $html = '<p>サンプルテキスト<!-- サンプルコメント --></p>';
18
19    // DOMDocumentオブジェクトを生成し、HTMLを読み込みます。
20    $dom = new \DOMDocument();
21
22    // HTMLの断片として正しく解析するためのオプションを指定し、libxmlのエラー出力を抑制します。
23    libxml_use_internal_errors(true);
24    $dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
25    libxml_clear_errors();
26
27    // 最初の<p>要素を取得します。
28    /** @var \DOMElement $pElement */
29    $pElement = $dom->getElementsByTagName('p')->item(0);
30
31    // 比較のため、<p>要素が持つ子ノードの数を表示します (2と表示されるはずです)。
32    echo "Parent <p> element has {$pElement->childNodes->length} child nodes." . PHP_EOL;
33    echo '----------------------------------' . PHP_EOL;
34
35    // <p>要素の子ノード(テキストノードとコメントノード)をループ処理します。
36    foreach ($pElement->childNodes as $childNode) {
37        echo 'Node Type: ' . get_class($childNode) . PHP_EOL;
38
39        // ノードがDom\CharacterDataのインスタンスであるかを確認します。
40        if ($childNode instanceof \Dom\CharacterData) {
41            // CharacterData型ノードのchildNodesプロパティを取得します。
42            $grandChildNodes = $childNode->childNodes;
43
44            // プロパティがnullであることを確認し、結果を表示します。
45            if ($grandChildNodes === null) {
46                echo "  -> childNodes is null, as expected for CharacterData." . PHP_EOL;
47            } else {
48                // このブロックは通常は実行されません。
49                echo "  -> childNodes is not null. (Unexpected)" . PHP_EOL;
50            }
51        }
52        echo PHP_EOL;
53    }
54}
55
56// 関数を実行します。
57demonstrateCharacterDataChildNodes();

PHPのDom\CharacterDataクラスが持つchildNodesプロパティについて解説します。このプロパティは、あるノードが持つ子ノードの一覧を取得するために使用されます。Dom\CharacterDataは、テキストノード(DOMText)やコメントノード(DOMComment)といった、文字データを表現するノードの親となるクラスです。

DOMの構造上、テキストやコメントのようなノードは、それ自体が子ノードを持つことはできません。このルールに基づき、Dom\CharacterDataクラス、およびそれを継承するクラスのインスタンスからchildNodesプロパティにアクセスすると、戻り値は常にnullとなります。引数は必要ありません。

サンプルコードでは、まず<p>タグとその中に含まれるテキストノード、コメントノードを読み込んでいます。次に、ループ処理でこれらのテキストノードとコメントノードを一つずつ取り出し、それぞれのchildNodesプロパティを調べています。どちらのノードもDom\CharacterData型であるため、childNodesプロパティは期待通りnullとなり、その結果が出力されます。この挙動は、要素ノードが子ノードのリスト(DOMNodeList)を返すのとは対照的です。

childNodesプロパティは、アクセスするノードの種類によって戻り値が異なります。DOMElementのような要素ノードでは子ノードのリスト(DOMNodeList)が返されますが、Dom\CharacterDataを継承するテキストノードやコメントノードでは注意が必要です。これらのノードは構造上、子ノードを持つことができないため、childNodesプロパティは常にnullを返します。そのため、このプロパティから返された値を使う前には、必ずnullでないかを確認する処理を記述してください。この確認を怠ると、nullに対してプロパティやメソッドを呼び出そうとしてしまい、プログラムが停止する致命的なエラーの原因となります。

PHP XPathでchildNodesを取得する

1<?php
2
3declare(strict_types=1);
4
5/**
6 * XPathを使用してXML要素を取得し、その子ノードリスト (childNodes) を処理するサンプル。
7 *
8 * この関数は、XMLドキュメントからXPathを使って特定の親ノードを検索し、
9 * そのchildNodesプロパティを通じて全ての子ノードを反復処理します。
10 * 子ノードには、要素ノード(例: <title>)だけでなく、
11 * テキストノード(CharacterDataの一種)やコメントノードも含まれることを示します。
12 */
13function processChildNodesWithXPath(): void
14{
15    // サンプルXMLデータ
16    $xmlString = <<<XML
17    <?xml version="1.0" encoding="UTF-8"?>
18    <bookstore>
19      <book category="cooking">
20        <title lang="en">Everyday Italian</title>
21        <!-- Author information -->
22        <author>Giada De Laurentiis</author>
23        This is a text node.
24      </book>
25    </bookstore>
26    XML;
27
28    // DOMDocumentオブジェクトを生成し、XMLを読み込む
29    $dom = new DOMDocument();
30    // 空白のテキストノードを無視しないように設定
31    $dom->preserveWhiteSpace = true;
32    $dom->loadXML($xmlString);
33
34    // DOMXPathオブジェクトを生成
35    $xpath = new DOMXPath($dom);
36
37    // XPathクエリで<book>要素を取得する
38    // query()はDOMNodeListを返す
39    $nodes = $xpath->query('//book');
40
41    // 最初にマッチした<book>要素を取得
42    $bookNode = $nodes?->item(0);
43
44    // ノードが存在し、childNodesプロパティがDOMNodeListを返すことを確認
45    if ($bookNode && $bookNode->childNodes instanceof DOMNodeList) {
46        echo "Found <{$bookNode->nodeName}> element. Processing its child nodes...\n\n";
47
48        // childNodesプロパティを使って子ノードのリストを反復処理
49        foreach ($bookNode->childNodes as $child) {
50            // ノードの種類に応じて情報を出力
51            switch ($child->nodeType) {
52                case XML_ELEMENT_NODE: // 要素ノード (例: <title>, <author>)
53                    printf(
54                        "Type: Element, Name: %s, Content: %s\n",
55                        $child->nodeName,
56                        trim($child->textContent)
57                    );
58                    break;
59                case XML_TEXT_NODE: // テキストノード (CharacterDataを継承)
60                    if (trim($child->nodeValue) !== '') {
61                        printf(
62                            "Type: Text, Content: \"%s\"\n",
63                            trim($child->nodeValue)
64                        );
65                    }
66                    break;
67                case XML_COMMENT_NODE: // コメントノード (CharacterDataを継承)
68                    printf(
69                        "Type: Comment, Content: \"%s\"\n",
70                        trim($child->nodeValue)
71                    );
72                    break;
73            }
74        }
75    } else {
76        echo "Target node not found.\n";
77    }
78}
79
80// 関数を実行して結果を表示
81processChildNodesWithXPath();

childNodesは、XMLやHTMLドキュメント内の特定のノード(要素)の直下に含まれる、すべての子ノードのリストを取得するためのプロパティです。このプロパティが返す子ノードには、<title>のような要素だけでなく、要素の間に存在する改行やスペースなどの「テキストノード」や、「コメントノード」もすべて含まれる点が特徴です。

このサンプルコードでは、まずXPathを利用してXMLデータの中から<book>要素を特定し、取得しています。次に、その<book>要素に対してchildNodesプロパティを使い、直下の子ノード群をDOMNodeListオブジェクトとして取得します。そしてforeach文でこのリストを一つずつ取り出し、nodeTypeプロパティでノードの種類を判別しています。種類に応じて、要素名やテキストの内容などを画面に出力することで、どのような子ノードが含まれているかを確認できます。

このプロパティは引数を取らず、戻り値として子ノードのリストであるDOMNodeListを返します。子ノードが存在しない場合でも、空のDOMNodeListが返されるため、安全にループ処理を行えます。ただし、テキストノードやコメントノードのように構造的に子を持てないノードでこのプロパティを参照した場合はnullが返ります。

childNodesプロパティは、要素ノード(タグ)だけでなく、改行やインデントなどの空白を含むテキストノードや、コメントノードもすべて取得します。そのため、要素ノードだけを処理したい場合は、ループ内でnodeTypeプロパティを確認して種類を判別する必要があります。特に、XMLを整形していると意図しない空白テキストノードが含まれることが多いため、サンプルコードのようにtrim関数で中身が空か判別する処理は重要です。また、XPathのqueryメソッドはノードのリストを返し、対象が見つからない場合は空のリストになります。そのため、item(0)で要素を取得する際は、結果がnullになる可能性を考慮し、必ず存在チェックを行ってください。

【PHP8.x】childNodesプロパティの使い方 | いっしー@Webエンジニア