【PHP8.x】DOMEntityReference::appendChild()メソッドの使い方
appendChildメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
appendChildメソッドは、DOMエンティティ参照ノード(DOMEntityReference)に新しい子ノードを追加しようとするメソッドです。このメソッドはDOMNodeクラスから継承されていますが、DOMEntityReferenceオブジェクトに対して使用した場合は特別な動作をします。DOMの仕様上、エンティティ参照ノードとその子孫はすべて読み取り専用として扱われます。これは、エンティティ参照の内容が、文書の型定義(DTD)などで定義された元のエンティティによって決定されるためです。したがって、この読み取り専用のノードに対してappendChildメソッドを用いて子ノードを追加しようとすると、操作は許可されず、常にDOMExceptionという例外がスローされます。具体的には、ノードの変更が許可されていないことを示す「NO_MODIFICATION_ALLOWED_ERR」というエラーが発生します。このため、DOMEntityReferenceオブジェクトに対してこのメソッドを呼び出すことは、プログラムのエラーを引き起こすため避けるべきです。エンティティの内容を操作したい場合は、参照元であるエンティティ宣言自体を変更する必要があります。
構文(syntax)
1<?php 2$document = new DOMDocument(); 3$document->loadXML('<!DOCTYPE root [<!ENTITY anEntity "text">]><root>&anEntity;</root>'); 4 5$entityReference = $document->documentElement->firstChild; 6$newNode = $document->createElement('new'); 7 8$appendedNode = $entityReference->appendChild($newNode); 9?>
引数(parameters)
DOMNode $node
- DOMNode $node: 追加する子ノードを指定するDOMNodeオブジェクト
戻り値(return)
DOMNode|false
DOMNode|false 新しく追加された子ノード、または追加に失敗した場合はfalseを返します。
サンプルコード
PHP DOM appendChild で例外を発生させる
1<?php 2declare(strict_types=1); 3 4/** 5 * DOMEntityReference::appendChild の動作を実証します。 6 * 7 * DOMEntityReference ノードは読み取り専用であり、 8 * 子ノードを追加しようとすると DOMException がスローされることを示します。 9 */ 10function demonstrateDomEntityReferenceAppendChild(): void 11{ 12 // エンティティ 'writer' を定義したXML文字列 13 $xmlString = <<<XML 14 <?xml version="1.0" encoding="utf-8"?> 15 <!DOCTYPE author [ 16 <!ENTITY writer "John Doe"> 17 ]> 18 <author>&writer;</author> 19 XML; 20 21 // DOMDocument オブジェクトを作成 22 $dom = new DOMDocument(); 23 24 // エンティティ参照を展開せず、ノードとして保持するように設定 25 // これが false でないと、&writer; は "John Doe" というテキストノードに置き換えられてしまう 26 $dom->substituteEntities = false; 27 28 // XML を読み込み 29 $dom->loadXML($xmlString); 30 31 // <author>要素内のエンティティ参照ノード (&writer;) を取得 32 $authorElement = $dom->getElementsByTagName('author')->item(0); 33 $entityRefNode = $authorElement->childNodes->item(0); 34 35 // 取得したノードが DOMEntityReference であることを確認 36 if ($entityRefNode instanceof DOMEntityReference) { 37 echo "取得したノードの型: " . get_class($entityRefNode) . PHP_EOL; 38 39 try { 40 // 新しいテキストノードを作成 41 $newNode = $dom->createTextNode(' (editor)'); 42 43 // DOMEntityReference ノードに子ノードを追加しようと試みる 44 // 仕様上、この操作は DOMException をスローする 45 $entityRefNode->appendChild($newNode); 46 } catch (DOMException $e) { 47 // DOMEntityReferenceは読み取り専用のため、appendChildは失敗する 48 echo "例外をキャッチしました: " . $e->getMessage() . PHP_EOL; 49 echo "DOMEntityReference ノードは変更できません。" . PHP_EOL; 50 } 51 } else { 52 echo "DOMEntityReference ノードが見つかりませんでした。" . PHP_EOL; 53 } 54} 55 56// 関数を実行 57demonstrateDomEntityReferenceAppendChild();
DOMEntityReference::appendChildは、エンティティ参照ノードの子リストの末尾に新しい子ノードを追加するためのメソッドです。引数 $node には、追加したいDOMNodeオブジェクトを渡します。
しかし、DOMの仕様上、DOMEntityReferenceノードは読み取り専用であり、その内容を変更することはできません。そのため、このメソッドを呼び出すと、ノードの追加は行われず、常にDOMExceptionというエラーが発生します。仕様上の戻り値は追加されたノード(DOMNode)または失敗を示すfalseですが、このメソッドは例外をスローするため、それらの値が返されることはありません。
サンプルコードでは、&writer;というエンティティ参照を含むXMLを読み込み、DOMEntityReferenceノードを取得しています。そして、try...catchブロック内でappendChildメソッドを呼び出し、意図的に子ノードを追加しようと試みています。案の定、この操作は失敗し、DOMExceptionが捕捉されます。この結果から、DOMEntityReferenceノードは直接的な変更が許可されていないことが明確にわかります。
DOMEntityReference::appendChildメソッドは、エンティティ参照ノードが読み取り専用であるため、仕様上必ず失敗しDOMExceptionというエラーを発生させます。このメソッドは他のDOMノードとの一貫性のために存在しますが、実際にはノードの変更には使用できません。XML文書の構造を変更したい場合は、エンティティ参照ノード自体を操作するのではなく、その親要素に対して子ノードを追加・削除する必要があります。また、コードのようにエンティティをノードとして扱うには、XMLを読み込む前に$dom->substituteEntities = false;と設定することが不可欠です。この設定がないとエンティティはただのテキストに置き換えられてしまうため注意してください。
PHP DOMEntityReference appendChild で追加失敗
1<?php 2 3/** 4 * DOMEntityReference::appendChild の動作を示すサンプル関数。 5 * 6 * DOMの仕様上、エンティティ参照ノード (DOMEntityReference) は読み取り専用であり、 7 * 子ノードを追加することはできません。 8 * そのため、appendChild() を呼び出すと DOMException がスローされます。 9 * このコードは、その挙動を実際に示します。 10 */ 11function demonstrateDomEntityReferenceAppendChild(): void 12{ 13 // 1. DTDでエンティティが定義されたXML文字列を準備 14 $xmlString = <<<XML 15 <?xml version="1.0" encoding="UTF-8"?> 16 <!DOCTYPE root [ 17 <!ENTITY myEntity "これはエンティティです"> 18 ]> 19 <root> 20 </root> 21 XML; 22 23 // 2. DOMDocumentオブジェクトを作成し、XMLを読み込む 24 $dom = new DOMDocument('1.0', 'UTF-8'); 25 // 整形して出力する設定 26 $dom->formatOutput = true; 27 $dom->loadXML($xmlString); 28 29 // 3. ルート要素を取得 30 $root = $dom->documentElement; 31 32 // 4. エンティティ参照ノード (&myEntity;) を作成 33 $entityRef = $dom->createEntityReference('myEntity'); 34 35 // 5. ルート要素にエンティティ参照ノードを追加 36 $root->appendChild($entityRef); 37 38 echo "--- 例外発生前のXML ---" . PHP_EOL; 39 echo $dom->saveXML(); 40 echo PHP_EOL; 41 42 // 6. エンティティ参照ノードに追加しようとする新しいノードを作成 43 $newTextNode = $dom->createTextNode(' (このテキストは追加されません)'); 44 45 try { 46 // 7. DOMEntityReferenceノードに子ノードを追加しようと試みる 47 // この操作は常に失敗し、DOMExceptionがスローされる 48 echo "--- DOMEntityReference::appendChild() を試みます... ---" . PHP_EOL; 49 $entityRef->appendChild($newTextNode); 50 } catch (DOMException $e) { 51 // 8. 期待通り例外がスローされるため、ここで捕捉する 52 echo "期待された例外を捕捉しました:" . PHP_EOL; 53 echo "エラーメッセージ: " . $e->getMessage() . PHP_EOL; 54 } 55 56 echo PHP_EOL . "--- 例外発生後のXML (変更なし) ---" . PHP_EOL; 57 echo $dom->saveXML(); 58} 59 60// 関数を実行 61demonstrateDomEntityReferenceAppendChild(); 62
DOMEntityReference::appendChild()は、エンティティ参照ノードに新しい子ノードを追加するためのメソッドです。しかし、DOMの仕様上、エンティティ参照ノードは読み取り専用と定められており、その内容を変更することはできません。そのため、このメソッドを呼び出すと常にDOMExceptionというエラーが発生し、処理は失敗します。
このサンプルコードは、その動作を実際に示しています。まず、DTD(文書型定義)でエンティティ myEntity を定義したXML文字列をDOMDocumentオブジェクトとして読み込みます。次に、createEntityReference('myEntity') を使ってエンティティ参照ノードを作成し、ドキュメントのルート要素に追加します。
コードの核心部分は try...catch ブロックです。ここで、作成したエンティティ参照ノードに対して appendChild() を呼び出し、新しいテキストノードを追加しようと試みています。この操作は仕様で許可されていないため、DOMException が発生します。catch ブロックでこの例外を捕捉することで、エラーが意図通りに発生することを確認しています。
引数には追加したいDOMNodeオブジェクトを指定しますが、前述の通り処理が必ず失敗するため、戻り値として追加されたノードが返されることはありません。このメソッドの挙動は、エンティティ参照ノードが不変であることを理解する上で重要です。
DOMEntityReferenceオブジェクトは、DTDで定義されたエンティティへの参照を表す読み取り専用のノードです。そのため、appendChildメソッドで子ノードを追加しようとすると、DOMの仕様により必ずDOMExceptionというエラーが発生します。これはPHPの不具合ではなく、XMLの標準的なルールに基づいています。したがって、実際のプログラムでエンティティ参照ノードを直接変更する目的でこのメソッドを呼び出すことはできません。このメソッドを呼び出す可能性がある場合は、サンプルコードのようにtry-catch構文でエラーを適切に処理する必要があります。ノードの追加は、DOMElementのような変更可能なノードに対して行ってください。