【PHP8.x】DOMDocument::firstElementChildプロパティの使い方
firstElementChildプロパティの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
firstElementChildプロパティは、DOMDocumentオブジェクトが保持する、文書ツリーの最初の子要素ノードを指すプロパティです。
このプロパティは、ウェブページやXMLデータなどの文書構造をプログラムで操作するための仕組みであるDOM(Document Object Model)において、PHPのDOM拡張機能の一部として提供されています。DOMDocumentオブジェクトは、HTMLドキュメントやXMLドキュメント全体を表します。firstElementChildプロパティを使用すると、そのDOMDocumentオブジェクトが表現する文書の直下にある、最初の要素ノード(いわゆるHTMLの<html>タグやXMLのルート要素のような、タグで囲まれた構造を持つ部分)を簡単に取得できます。
具体的には、DOMDocumentの直接の子ノードには、要素ノードの他にもコメントノードや処理命令ノードなどが存在し得ますが、firstElementChildはそれらの中から要素ノードのみを対象とし、最初に見つかる要素ノードをDOMElementオブジェクトとして返します。
例えば、読み込んだHTMLドキュメント全体を表すDOMDocumentオブジェクトから、そのドキュメントのルート要素である<html>要素を直接取得したい場合にこのプロパティが非常に役立ちます。取得したDOMElementオブジェクトを通じて、さらにその内部の要素や属性にアクセスし、操作を進めることが可能になります。もしDOMDocumentオブジェクトが子要素ノードを全く持たない場合、このプロパティはnullを返します。これにより、対象の要素が存在しない場合の処理を適切に記述できます。
構文(syntax)
1<?php 2$dom = new DOMDocument(); 3$dom->loadHTML('<html><body><p>Hello</p></body></html>'); 4 5$firstElement = $dom->firstElementChild; 6?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
戻り値なし
戻り値はありません
サンプルコード
PHP DOMDocument firstElementChildで要素を取得する
1<?php 2 3/** 4 * DOMDocument::firstElementChild プロパティの使用例 5 * 6 * このプロパティは、DOMDocument オブジェクトの最初の子要素 (DOMElement) を取得します。 7 * HTMLドキュメントの場合、通常は <html> 要素がこれに該当します。 8 * 子要素が存在しない場合は null を返します。 9 * 10 * @see https://www.php.net/manual/class.domdocument.php 11 * @see https://www.php.net/manual/class.domparentelement.php#domparentelement.props.firstelementchild 12 */ 13function demonstrateDomDocumentFirstElementChild(): void 14{ 15 // DOMDocument オブジェクトを作成 16 // HTMLやXMLドキュメント全体を操作するための基盤となるクラスです。 17 $dom = new DOMDocument(); 18 19 // 空白ノードを無視し、出力時に整形を行う設定(オプション) 20 $dom->preserveWhiteSpace = false; 21 $dom->formatOutput = true; 22 23 // 読み込むHTMLコンテンツ 24 // ドキュメントの直下には <html> 要素が存在します。 25 $htmlContent = '<!DOCTYPE html> 26<html> 27<head> 28 <title>Sample Page</title> 29</head> 30<body> 31 <div id="container"> 32 <p>Hello, PHP!</p> 33 </div> 34</body> 35</html>'; 36 37 // HTMLコンテンツをDOMDocumentに読み込みます 38 $dom->loadHTML($htmlContent); 39 40 echo "--- DOMDocumentのfirstElementChildの例 ---\n"; 41 42 // DOMDocumentのfirstElementChildプロパティにアクセス 43 // ドキュメントの直下にある最初の DOMElement (HTMLでは通常 <html>) を取得します。 44 $firstElementOfDocument = $dom->firstElementChild; 45 46 // 取得した要素が DOMElement のインスタンスであるかを確認し、情報を表示 47 if ($firstElementOfDocument instanceof DOMElement) { 48 echo "DOMDocumentの最初の子要素のタグ名: " . $firstElementOfDocument->tagName . "\n"; 49 echo "DOMDocumentの最初の子要素のノードタイプ: " . $firstElementOfDocument->nodeType . " (ELEMENT_NODE)\n"; 50 // 取得した要素が実際に <html> タグであることを確認 51 if ($firstElementOfDocument->tagName === 'html') { 52 echo "期待通り、ドキュメントのルート要素 (<html>) が取得されました。\n"; 53 } 54 } else { 55 echo "DOMDocumentに最初の子要素が見つかりませんでした。\n"; 56 } 57 58 echo "\n--- 子要素が存在しない場合の例 ---\n"; 59 60 // 子要素を持たない可能性のあるDOMDocumentを作成 61 // ここではコメントノードのみをロードし、要素ノードが存在しない状況をシミュレート 62 $domWithoutElements = new DOMDocument(); 63 $domWithoutElements->loadHTML('<!-- ドキュメントルートに要素はありません -->'); 64 65 $firstElementOfEmptyDoc = $domWithoutElements->firstElementChild; 66 67 // この場合、firstElementChildは null を返します 68 if ($firstElementOfEmptyDoc instanceof DOMElement) { 69 echo "要素がないはずのDOMDocumentから最初の子要素のタグ名: " . $firstElementOfEmptyDoc->tagName . "\n"; 70 } else { 71 echo "要素がないDOMDocumentに最初の子要素は見つかりませんでした。これは期待される動作です。\n"; 72 } 73} 74 75// 関数を実行してサンプルコードを動作させます 76demonstrateDomDocumentFirstElementChild(); 77
PHPのDOMDocument::firstElementChildプロパティは、HTMLやXMLドキュメント全体を表現するDOMDocumentオブジェクトに格納されている要素の中で、直下にある「最初の子要素」を取得する際に利用されます。このプロパティにアクセスする際、引数は必要ありません。
このプロパティを使用すると、通常はDOMElementオブジェクトが取得されます。例えば、ウェブページのような標準的なHTMLドキュメントをDOMDocumentオブジェクトに読み込んだ場合、ドキュメントのルート要素である<html>要素がDOMElementオブジェクトとして得られます。これにより、ドキュメント全体の構造をたどる最初の起点とすることができます。
一方、もし対象のDOMDocumentオブジェクトに要素ノードの子が存在しない場合(例えば、コメントノードのみのドキュメントや完全に空のドキュメントを扱っている場合など)は、このプロパティはnullを返します。したがって、取得した値がnullでないことを確認してから、そのDOMElementオブジェクトを操作することが重要ですし、これが適切なプロパティの挙動です。
DOMDocument::firstElementChildプロパティは、ドキュメントの直下にある最初のDOMElement型の要素を取得します。要素が存在しない場合はnullを返すため、取得後には必ずinstanceof DOMElementを使って、戻り値が実際にDOMElementオブジェクトであるかを確認し、安全に処理してください。テキストノードやコメントノードは要素と見なされないため、これらはスキップされ、純粋な要素ノードのみが対象となります。HTMLドキュメントの場合、このプロパティは通常、ドキュメントのルートである<html>要素を返します。loadHTMLでコンテンツを読み込む際には、HTMLの構文エラーなどによって警告が発生する可能性もあるため、エラーハンドリングを適切に行うことも重要です。
DOMNodeのfirstElementChildを取得する
1<?php 2 3/** 4 * 指定されたDOMノードの最初の子要素ノードを取得します。 5 * これは、JavaScriptのNode.firstElementChildプロパティに相当する機能です。 6 * DOMNode::firstChildプロパティはテキストノードやコメントノードも含むため、 7 * この関数では要素ノード(DOMElement)のみをフィルタリングして返します。 8 * 9 * @param DOMNode $node 対象のDOMノード(DOMDocumentまたはDOMElementなど)。 10 * @return DOMElement|null 最初の子要素ノードが見つかった場合はそのDOMElementオブジェクト、 11 * 見つからなかった場合はnullを返します。 12 */ 13function getFirstElementChild(DOMNode $node): ?DOMElement 14{ 15 // DOMNode::firstChild は、テキストノードやコメントノードを含む、最初の直接の子ノードです。 16 $child = $node->firstChild; 17 18 // 子ノードが存在する限りループして、要素ノードを探します。 19 while ($child !== null) { 20 // nodeType が XML_ELEMENT_NODE(要素ノード)であるかチェックします。 21 // PHPのDOM拡張では、DOMElement型のノードが要素ノードです。 22 if ($child->nodeType === XML_ELEMENT_NODE) { 23 // 最初に見つかった要素ノードを返します。 24 return $child; 25 } 26 // 現在のノードが要素ノードでなければ、次の兄弟ノードに進みます。 27 $child = $child->nextSibling; 28 } 29 30 // 子要素ノードが見つからなかった場合はnullを返します。 31 return null; 32} 33 34// ---------------------------------------------------------------------------- 35// サンプルコードの実行例 36// ---------------------------------------------------------------------------- 37 38// HTMLドキュメントの文字列を準備します。 39$htmlContent = <<<HTML 40<!DOCTYPE html> 41<html> 42<head> 43 <meta charset="utf-8"> 44 <title>PHP DOMDocument サンプル</title> 45</head> 46<body> 47 <!-- ヘッダーセクション --> 48 <header> 49 <h1>メインタイトル</h1> 50 </header> 51 <main> 52 <p>最初の段落です。</p> 53 <p>2番目の段落です。</p> 54 </main> 55 <footer> 56 <p>フッター情報</p> 57 </footer> 58</body> 59</html> 60HTML; 61 62// 新しい DOMDocument オブジェクトを作成します。 63$dom = new DOMDocument(); 64 65// HTML文字列をロードします。 66// エラーが発生した場合(例: 不完全なHTML)でも処理を続けるために@でエラーを抑制しています。 67@$dom->loadHTML($htmlContent); 68 69echo "DOMDocument::documentElement の確認:\n"; 70// DOMDocument::documentElement は、HTMLドキュメントのルート要素(通常は <html>)を返します。 71// これはドキュメントの「最初の要素ノード」によく対応します。 72$documentElement = $dom->documentElement; 73if ($documentElement instanceof DOMElement) { 74 echo " ルート要素 (documentElement): <{$documentElement->nodeName}>\n"; // 出力例: ルート要素 (documentElement): <html> 75 76 echo "\nルート要素の子ノード (DOMNode::firstChild と getFirstElementChild の比較):\n"; 77 // <html> 要素の最初の直接の子ノードを取得します。 78 // このHTMLの場合、改行や空白もテキストノードとして扱われることがありえますが、 79 // HTML5パーサーでは<html>直下は<head>または<body>が直接の子となることが多いです。 80 $firstNodeOfDocumentElement = $documentElement->firstChild; 81 if ($firstNodeOfDocumentElement !== null) { 82 echo " documentElement->firstChild: " . $firstNodeOfDocumentElement->nodeName . " (Type: " . $firstNodeOfDocumentElement->nodeType . ")\n"; 83 // 出力例: documentElement->firstChild: head (Type: 1) (XML_ELEMENT_NODEは1) 84 } else { 85 echo " documentElement に最初のノードがありません。\n"; 86 } 87 88 // getFirstElementChild 関数を使用して、<html> 要素の最初の子要素ノードを取得します。 89 $firstElementOfDocumentElement = getFirstElementChild($documentElement); 90 if ($firstElementOfDocumentElement instanceof DOMElement) { 91 echo " getFirstElementChild(documentElement): <{$firstElementOfDocumentElement->nodeName}>\n"; // 出力例: getFirstElementChild(documentElement): <head> 92 } else { 93 echo " documentElement に子要素ノードが見つかりませんでした。\n"; 94 } 95 96 // <body> 要素を見つけて、さらにその子要素を調べます。 97 $bodyElement = $dom->getElementsByTagName('body')->item(0); 98 if ($bodyElement instanceof DOMElement) { 99 echo "\nボディ要素 (<{$bodyElement->nodeName}>) の確認:\n"; 100 101 // <body> 要素の最初の直接の子ノードを取得します。 102 // このHTMLの場合、<body>の最初のノードはコメントノード "<!-- ヘッダーセクション -->" です。 103 $firstNodeOfBody = $bodyElement->firstChild; 104 if ($firstNodeOfBody !== null) { 105 echo " bodyElement->firstChild: " . $firstNodeOfBody->nodeName . " (Type: " . $firstNodeOfBody->nodeType . ")\n"; 106 // 出力例: bodyElement->firstChild: #comment (Type: 8) (XML_COMMENT_NODEは8) 107 } else { 108 echo " bodyElement に最初のノードがありません。\n"; 109 } 110 111 // getFirstElementChild 関数を使用して、<body> 要素の最初の子要素ノードを取得します。 112 // コメントノードをスキップして、最初の要素ノードである <header> を見つけます。 113 $firstElementChildOfBody = getFirstElementChild($bodyElement); 114 if ($firstElementChildOfBody instanceof DOMElement) { 115 echo " getFirstElementChild(bodyElement): <{$firstElementChildOfBody->nodeName}>\n"; // 出力例: getFirstElementChild(bodyElement): <header> 116 } else { 117 echo " bodyElement に子要素ノードが見つかりませんでした。\n"; 118 } 119 } else { 120 echo "\n<body> 要素が見つかりませんでした。\n"; 121 } 122 123} else { 124 echo "ドキュメントのルート要素が見つかりませんでした。\n"; 125} 126 127?>
このPHPのサンプルコードは、HTMLやXMLドキュメントを操作するDOMDocumentクラスを使用し、特定のノードの「最初の子要素ノード」を取得するgetFirstElementChild関数を定義しています。DOMDocumentはPHPでドキュメント構造を解析し、各要素やテキスト、コメントなどをノードとして扱います。
通常、PHPのDOM操作ではDOMNode::firstChildプロパティを使うと、テキストノードやコメントノードを含む、最初に見つかった子ノードを返します。しかし、ウェブ開発では多くの場合、HTMLタグのような要素ノードだけを対象としたい場面があります。
getFirstElementChild関数は、JavaScriptの同名のプロパティに相当する機能を提供します。この関数はDOMNode型のノードを引数として受け取り、その子ノードを順に調べ、XML_ELEMENT_NODE(要素ノード)であるものだけをフィルタリングします。最初に見つかった要素ノードをDOMElementオブジェクトとして返し、子要素ノードが一つも見つからない場合はnullを返します。
サンプルコードの実行例では、HTMLドキュメントをロードした後、<html>要素や<body>要素に対して、DOMNode::firstChildとgetFirstElementChild関数の違いを比較しています。特に<body>要素では、最初の直接の子ノードがコメントである場合でも、getFirstElementChild関数がコメントをスキップし、期待される最初の要素ノードである<header>を正確に取得できることを示しており、より直感的な要素操作が可能になることを理解できます。
PHPのDOM拡張には、JavaScriptのような「firstElementChild」プロパティは標準で提供されていません。リファレンス情報で「戻り値なし」とあるのは、このプロパティがPHPには存在しないことを示しています。そのため、サンプルコードでは「DOMNode::firstChild」と「DOMNode::nextSibling」を使って要素ノードのみを検索する「getFirstElementChild」関数を独自に作成しています。通常の「DOMNode::firstChild」は、空白や改行を含むテキストノードやコメントノードも取得するため、要素ノードだけを扱う際には、本サンプルコードのように「nodeType」が「XML_ELEMENT_NODE」か確認してフィルタリング処理を行うことが重要です。