【PHP8.x】Dom\HTMLDocument::adoptNode()メソッドの使い方
adoptNodeメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
adoptNodeメソッドは、他のHTMLドキュメントに属するノードを、現在のドキュメントへ移動させる処理を実行するメソッドです。このメソッドは、引数で指定されたノードを、元のドキュメントツリーから完全に削除し、現在のドキュメントの子として追加できる状態にします。ノードだけでなく、そのノードが持つすべての子要素やテキストといった子孫ノードも一緒に移動します。移動が完了すると、ノードの所有者を示すプロパティ(ownerDocument)は現在のドキュメントに更新されます。類似の機能を持つimportNodeメソッドが元のノードをコピーするのに対し、adoptNodeメソッドは元のノードそのものを移動させる、つまり「切り取りと貼り付け」のような動作をする点が大きな違いです。処理が成功した場合は移動したノードオブジェクトを返し、失敗した場合はfalseを返します。この機能により、あるドキュメントの一部を別のドキュメントへ効率的に移し替えるといった操作が可能になります。
構文(syntax)
1<?php 2 3// 移動元のドキュメント 4$sourceDoc = new Dom\HTMLDocument(); 5$sourceDoc->loadHTML('<!DOCTYPE html><html><body><p>移動させる段落</p></body></html>'); 6 7// 移動先のドキュメント 8$destDoc = new Dom\HTMLDocument(); 9$destDoc->loadHTML('<!DOCTYPE html><html><body><h1>移動先</h1></body></html>'); 10 11// 移動させたいノードを取得 12$nodeToAdopt = $sourceDoc->getElementsByTagName('p')->item(0); 13 14// $sourceDoc から $nodeToAdopt を $destDoc に採用(移動)する 15// 成功した場合、採用されたノードが返される 16$adoptedNode = $destDoc->adoptNode($nodeToAdopt); 17 18// 採用したノードを移動先のドキュメントに追加する 19if ($adoptedNode) { 20 $destDoc->body->appendChild($adoptedNode); 21} 22 23// 結果を出力する 24echo $destDoc->saveHTML(); 25 26?>
引数(parameters)
Dom\Node $node
- Dom\Node $node: 新しいドキュメントに採用するノードを指定します。
戻り値(return)
Dom\Node
このメソッドは、指定されたノードを現在のドキュメントに適合させた新しいノードを返します。
サンプルコード
PHP HTMLDocument::adoptNodeでノードを移動する
1<?php 2 3/** 4 * Dom\HTMLDocument::adoptNode メソッドのサンプルコード。 5 * 6 * 別のHTMLドキュメントからノードを「養子」として現在のドキュメントに取り込み、 7 * その後、現在のドキュメントのツリーに追加する方法を示します。 8 * 9 * adoptNode はノードの所有ドキュメントを変更しますが、ノードを自動的にツリーに追加するわけではありません。 10 * ツリーに追加するには、appendChild などのメソッドを別途使用する必要があります。 11 */ 12function demonstrateAdoptNode(): void 13{ 14 // 1. ソースとなるHTMLドキュメントを作成します。 15 // このドキュメントからノードを取得し、別のドキュメントへ移動させます。 16 $sourceDoc = new Dom\HTMLDocument(); 17 $sourceDoc->loadHTML('<html><body><h1>元のドキュメントの見出し</h1><p>これは元のドキュメントの段落です。</p></body></html>'); 18 19 // 2. ターゲットとなるHTMLドキュメントを作成します。 20 // このドキュメントが、ソースドキュメントのノードを「養子」として受け入れます。 21 $targetDoc = new Dom\HTMLDocument(); 22 $targetDoc->loadHTML('<html><head><title>ターゲットドキュメント</title></head><body><h2>新しいドキュメント</h2><div id="content"></div></body></html>'); 23 24 // 3. ソースドキュメントから移動させたいノード(ここでは最初のh1要素)を取得します。 25 $nodeToAdopt = $sourceDoc->getElementsByTagName('h1')->item(0); 26 27 if (!$nodeToAdopt instanceof Dom\Node) { 28 echo "エラー: ソースドキュメントからh1ノードが見つかりませんでした。\n"; 29 return; 30 } 31 32 echo "--- adoptNode 実行前の状態 ---\n"; 33 echo "移動対象ノード (h1) の現在の所有ドキュメント: " . spl_object_hash($nodeToAdopt->ownerDocument) . "\n"; 34 echo "ターゲットドキュメント: " . spl_object_hash($targetDoc) . "\n\n"; 35 36 // 4. adoptNode メソッドを使用して、ノードの所有ドキュメントをターゲットドキュメントに変更します。 37 // このメソッドは、ノードの ownerDocument プロパティを変更しますが、 38 // まだノードをターゲットドキュメントのDOMツリーに追加するわけではありません。 39 $adoptedNode = $targetDoc->adoptNode($nodeToAdopt); 40 41 echo "--- adoptNode 実行後の状態 ---\n"; 42 // 養子にされたノードの所有ドキュメントがターゲットドキュメントに変わっていることを確認 43 echo "養子にされたノード (h1) の新しい所有ドキュメント: " . spl_object_hash($adoptedNode->ownerDocument) . "\n\n"; 44 45 // 5. 養子にされたノードをターゲットドキュメント内の特定の位置(id="content" のdiv要素)に追加します。 46 // adoptNode だけではノードはツリーに追加されないため、明示的に追加する必要があります。 47 $contentDiv = $targetDoc->getElementById('content'); 48 49 if ($contentDiv instanceof Dom\Element) { 50 $contentDiv->appendChild($adoptedNode); 51 echo "h1ノードがターゲットドキュメントの'content' divに追加されました。\n\n"; 52 } else { 53 echo "エラー: ターゲットドキュメントに 'content' というIDを持つ要素が見つかりませんでした。\n\n"; 54 return; 55 } 56 57 // 6. ターゲットドキュメントの最終的なHTMLコンテンツを出力し、変更を確認します。 58 echo "--- ターゲットドキュメントの最終的なHTML ---\n"; 59 echo $targetDoc->saveHTML(); 60} 61 62// 関数を実行して、Dom\HTMLDocument::adoptNode の動作を確認します。 63demonstrateAdoptNode(); 64 65?>
Dom\HTMLDocument::adoptNodeメソッドは、PHPのDOM操作において、異なるHTMLドキュメントに属するノードを、現在のHTMLドキュメントに「養子」として取り込むために使用されます。このメソッドの主な役割は、指定されたノードの所有ドキュメントを変更することです。
引数$nodeには、別のドキュメントに存在する任意のDom\Nodeオブジェクトを指定します。このノードは、元のドキュメントのDOMツリーから切り離され、その所有ドキュメントが現在のDom\HTMLDocumentオブジェクトに変更されます。戻り値は、所有ドキュメントが変更された後の、引数で渡されたノード自身です。
サンプルコードでは、まず$sourceDocと$targetDocという二つのHTMLドキュメントを作成しています。$sourceDocから<h1>要素を一つ取得し、このノードを$targetDocに移動させます。$targetDoc->adoptNode($nodeToAdopt)を実行することで、<h1>ノードの所有者が$targetDocに変わります。ただし、この時点ではノードはまだ$targetDocのDOMツリーには追加されていません。そのため、$targetDoc->getElementById('content')->appendChild($adoptedNode)を用いて、明示的に<h1>ノードを$targetDoc内の特定の場所に追加しています。このようにadoptNodeはノードの所有権を移すだけであり、実際にDOMツリーに配置するには別途appendChildなどのメソッドが必要です。
Dom\HTMLDocument::adoptNodeは、ノードの所有ドキュメントを別のものに変更するメソッドです。ノードを「養子」として新しいドキュメントに取り込みますが、これだけではノードは新しいドキュメントのDOMツリーには追加されません。実際にツリー内に配置するには、appendChildなどのメソッドを別途呼び出す必要があります。また、ノード取得時にinstanceofで型を確認したり、ノードが見つからない場合の処理を実装したりすることで、予期せぬエラーを防ぎ、より安全にコードを利用できます。この操作により、元のドキュメントからはノードが移動することになります。
PHP DomDocument: adoptNodeでノードを取り込む
1<?php 2 3/** 4 * Dom\HTMLDocument::adoptNode メソッドのサンプルコード。 5 * 6 * このサンプルは、データベース (ADODBなど) から取得したと仮定されるHTMLコンテンツを 7 * 別のDOMドキュメントに取り込むシナリオをシミュレートします。 8 * adoptNode メソッドは、あるドキュメントに属するノードを別のドキュメントの所有に切り替えるために使用されます。 9 */ 10function demonstrateAdoptNodeWithHtmlContent(): void 11{ 12 // 1. メインとなるHTMLドキュメントを作成します。 13 // これはウェブページ全体を表すドキュメントと想定できます。 14 $mainDocument = new Dom\HTMLDocument(); 15 $mainDocument->loadHTML( 16 '<!DOCTYPE html>' . 17 '<html>' . 18 '<head><title>メインページ</title></head>' . 19 '<body>' . 20 '<h1>メインコンテンツのエリア</h1>' . 21 '<div id="content-container">' . 22 '<h2>ここに外部コンテンツが挿入されます</h2>' . 23 '</div>' . 24 '</body>' . 25 '</html>' 26 ); 27 28 // 2. データベース (例: ADODB) から取得したと仮定されるHTMLコンテンツの文字列を用意します。 29 // 実際にはデータベースから取得しますが、ここでは例として文字列を直接定義します。 30 $htmlContentFromDatabase = 31 '<div class="adodb-item">' . 32 '<h3>データベースからのお知らせ</h3>' . 33 '<p>この段落はデータベースから取得され、DOMツリーに取り込まれました。</p>' . 34 '<span>更新日: ' . date('Y-m-d H:i:s') . '</span>' . 35 '</div>'; 36 37 // 3. 取得したHTMLコンテンツを一時的にパースするための新しいDOMドキュメントを作成します。 38 // このドキュメントは、取り込みたいノードの「元の所有者」となります。 39 $sourceDocument = new Dom\HTMLDocument(); 40 $sourceDocument->loadHTML($htmlContentFromDatabase); 41 42 // 4. ソースドキュメントから取り込みたいノードを取得します。 43 // ここでは、データベースコンテンツのルートである最初の `div` 要素を取得します。 44 $nodeToAdopt = $sourceDocument->getElementsByTagName('div')->item(0); 45 46 // ノードが正常に取得できたことを確認します。 47 if ($nodeToAdopt instanceof Dom\Node) { 48 echo "--- メインドキュメントの初期状態 ---" . PHP_EOL; 49 echo $mainDocument->saveHTML() . PHP_EOL . PHP_EOL; 50 51 echo "--- ソースドキュメントの内容 (取り込み前) ---" . PHP_EOL; 52 echo $sourceDocument->saveHTML() . PHP_EOL . PHP_EOL; 53 54 // 5. mainDocument に adoptNode メソッドを使用して、nodeToAdopt を取り込みます。 55 // これにより、nodeToAdopt は sourceDocument から削除され、 56 // mainDocument の所有するノードとして扱われるようになります。 57 // adoptNode の戻り値は、取り込まれたノード自身です。 58 $adoptedNode = $mainDocument->adoptNode($nodeToAdopt); 59 60 // 6. 取り込んだノードをメインドキュメント内の適切な場所に追加します。 61 // ここでは、id="content-container" の div 要素の子として追加します。 62 $targetContainer = $mainDocument->getElementById('content-container'); 63 if ($targetContainer instanceof Dom\Element) { 64 $targetContainer->appendChild($adoptedNode); 65 echo "--- ノードがメインドキュメントに取り込まれ、追加されました ---" . PHP_EOL . PHP_EOL; 66 } else { 67 echo "エラー: ターゲットコンテナが見つかりませんでした。" . PHP_EOL; 68 } 69 70 echo "--- メインドキュメントの最終状態 ---" . PHP_EOL; 71 echo $mainDocument->saveHTML() . PHP_EOL . PHP_EOL; 72 73 echo "--- ソースドキュメントの内容 (取り込み後) ---" . PHP_EOL; 74 // adoptNode によりノードが削除されているため、ソースドキュメントは空になるか、ノードが減ります。 75 echo $sourceDocument->saveHTML() . PHP_EOL . PHP_EOL; 76 } else { 77 echo "エラー: データベースから取得したHTMLからノードを抽出できませんでした。" . PHP_EOL; 78 } 79} 80 81// サンプル関数を実行します。 82demonstrateAdoptNodeWithHtmlContent(); 83 84?>
PHP 8のDom\HTMLDocument::adoptNodeメソッドは、異なるDOMドキュメントに属するノードの所有権を、呼び出し元のドキュメントに移動させるために使用されます。このサンプルコードは、データベース(ADODBなど)から取得したと仮定されるHTMLコンテンツを、ウェブページのメインとなるDOMドキュメントに取り込むシナリオをシミュレートしています。
まず、ウェブページ全体を表すメインのHTMLドキュメントと、データベースから取得したHTMLを一時的にパースするためのソースドキュメントを作成します。adoptNodeメソッドは、ソースドキュメントから取り出したいノードを引数Dom\Node $nodeとして受け取ります。このメソッドが実行されると、指定されたノードは元のソースドキュメントから削除され、adoptNodeを呼び出したメインドキュメントがそのノードの新しい所有者となります。メソッドの戻り値Dom\Nodeは、所有権が移動したノードそのものです。その後、この戻り値のノードを、メインドキュメント内の適切な場所にappendChildなどで追加することで、異なるソースから得られたHTML要素を一つのDOMツリーに効率的に統合できます。
このサンプルコードで特に注意すべき点は、adoptNodeメソッドがノードを元のドキュメントから移動させ、そのドキュメントからは削除されるということです。ノードは新しいドキュメントの所有物となりますが、自動的にどこかに挿入されるわけではありません。そのため、adoptNodeの後に必ずappendChildなどのメソッドを使って、目的の場所へノードを追加する操作が必要になります。また、getElementsByTagNameやgetElementByIdなどでノードを取得する際は、ノードが正しく取得できたか、instanceofで型を確認することで、実行時エラーを防ぎ安全なコードになります。