【PHP8.x】XMLReader::ENTITY定数の使い方
ENTITY定数の使い方について、初心者にもわかりやすく解説します。
基本的な使い方
ENTITY定数は、PHPのXMLReaderクラスがXML文書を読み込む際に、現在処理しているノードが「エンティティ宣言」であることを示す定数です。
XMLReaderクラスは、XML文書を高速かつメモリ効率良く読み込むための機能を提供し、XML文書内の様々な構造を「ノード」として扱います。XML文書は、要素、属性、テキストなど多くのノードタイプで構成されていますが、その中には「エンティティ宣言」と呼ばれる特別な定義も存在します。エンティティ宣言は、XML文書内で繰り返し使用される文字列の別名(例えば著作権表示文)や、外部ファイルの内容、あるいは特定の特殊文字(例えば のような改行されないスペース)などを一箇所で定義し、文書の他の部分でそれらを参照するための「実体(エンティティ)」を定める仕組みです。
XMLReaderクラスを使用してXML文書を読み進める際、read()メソッドが次のノードに進み、nodeTypeプロパティが現在のノードのタイプを示します。このENTITY定数は、nodeTypeプロパティの値がXML文書内のエンティティ宣言に合致する場合に利用されます。システムエンジニアがXMLReaderを使ってXML文書をプログラムで解析する際には、この定数を用いて現在のノードがエンティティ宣言であるかを識別し、それに応じて適切な処理を実装することで、XML文書の構造と内容を正確に扱えるようになります。
構文(syntax)
1<?php 2$entityNodeType = XMLReader::ENTITY; 3?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
int
XMLReader::ENTITY は、XMLエンティティノードを表す整数定数です。
サンプルコード
PHP XMLReader エンティティデコード処理
1<?php 2 3/** 4 * XMLReader を使用して XML ドキュメントを解析し、 5 * XML エンティティがどのように処理(デコード)されるかを示す関数です。 6 * 特に、PHPのXMLReaderが自動的にエンティティをデコードする挙動と、 7 * XMLReader::ENTITY 定数がどのようなノードを表すかを解説します。 8 * 9 * @param string $xmlString 解析するXML文字列 10 * @return void 11 */ 12function processXmlWithEntityDecoding(string $xmlString): void 13{ 14 // XMLReader オブジェクトを初期化 15 $reader = new XMLReader(); 16 17 // XML 文字列を読み込み、失敗した場合はエラーメッセージを表示して終了 18 if (!$reader->xml($xmlString)) { 19 echo "Error: Failed to load XML string.\n"; 20 return; 21 } 22 23 // DTD (Document Type Definition) をロードして、エンティティ定義を処理できるように設定します。 24 // これにより、XMLReader::ENTITY タイプのノード(エンティティ定義自体)も検出可能になります。 25 // 注意: 外部エンティティのロードはセキュリティリスクとなる可能性があるため、 26 // 信頼できないソースからのXMLには十分注意して使用してください。 27 $reader->setParserProperty(XMLReader::LOADDTD, true); 28 29 echo "--- XML Parsing Start ---\n"; 30 31 // ドキュメントをノードごとに読み進めます 32 while ($reader->read()) { 33 $nodeTypeString = xmlNodeTypeToString($reader->nodeType); 34 35 echo sprintf("Node Type: %s (%s)\n", $reader->nodeType, $nodeTypeString); 36 echo sprintf(" Name: %s\n", $reader->name); 37 echo sprintf(" Value: %s\n", $reader->value); 38 39 // XMLReaderは、要素内のテキストや属性値について、デフォルトでエンティティ参照を自動的にデコードします。 40 // 例えば、< は < に、& は & に変換されます。 41 if ($reader->nodeType === XMLReader::TEXT) { 42 echo sprintf(" (Text Node Content: '%s' - HTML/XML entities like '<' or '&' are usually decoded here.)\n", $reader->value); 43 } 44 45 // XMLReader::ENTITY_REFERENCE は、XMLドキュメント内で '&entity_name;' の形式で参照されている部分を示します。 46 // XMLReaderはこれを読み込む際に、参照先のエンティティ内容を自動的に展開(デコード)しようとします。 47 // そのため、このノード自身の 'value' は通常空になりますが、その後のテキストノードで展開された内容が取得できます。 48 if ($reader->nodeType === XMLReader::ENTITY_REFERENCE) { 49 echo sprintf(" (Found Entity Reference: '&%s;' - This refers to an entity, and its content will be processed.)\n", $reader->name); 50 } 51 52 // XMLReader::ENTITY は、DTD内で定義されているエンティティそのものを表します。 53 // 例: <!ENTITY mycompany "Acme Corp. & Co."> の 'mycompany' 部分。 54 // これはエンティティの定義であり、その参照ではありません。 55 // エンティティがどのようにデコードされるかを示すというよりは、 56 // エンティティの定義情報を読み取る際にこの定数を使用します。 57 if ($reader->nodeType === XMLReader::ENTITY) { 58 echo sprintf(" (Found Entity Definition: '%s' - This is the definition, not a reference.)\n", $reader->name); 59 // expand() を使うと DOMEntity オブジェクトを取得し、定義内容をさらに詳しく調べることができます。 60 $domEntity = $reader->expand(); 61 if ($domEntity instanceof DOMEntity) { 62 // 内部エンティティの場合、nodeValue に置換テキストが含まれます。 63 // ここでも、& のようなエンティティはデコードされて取得されます。 64 if (!empty($domEntity->nodeValue)) { 65 echo sprintf(" (Entity Definition Content/Replacement Text: '%s')\n", $domEntity->nodeValue); 66 } 67 } 68 } 69 echo "--------------------------\n"; 70 } 71 72 $reader->close(); // XMLReader リソースを閉じる 73 echo "--- XML Parsing End ---\n\n"; 74} 75 76/** 77 * XMLReader ノードタイプを表す整数値を、人間が読める文字列に変換するヘルパー関数。 78 * PHP 8 の match 式を使用しています。 79 * 80 * @param int $nodeType XMLReader::nodeType が返す整数値 81 * @return string ノードタイプを示す文字列 82 */ 83function xmlNodeTypeToString(int $nodeType): string 84{ 85 return match ($nodeType) { 86 XMLReader::NONE => 'NONE', 87 XMLReader::ELEMENT => 'ELEMENT (例: <tag>)', 88 XMLReader::ATTRIBUTE => 'ATTRIBUTE (例: id="1")', 89 XMLReader::TEXT => 'TEXT (要素内のテキスト)', 90 XMLReader::CDATA => 'CDATA (CDATAセクション)', 91 XMLReader::ENTITY_REFERENCE => 'ENTITY_REFERENCE (例: &)', 92 XMLReader::ENTITY => 'ENTITY (DTD内のエンティティ定義)', // ここで XMLReader::ENTITY 定数を使用 93 XMLReader::PROCESSING_INSTRUCTION => 'PROCESSING_INSTRUCTION (例: <?php ...?>)', 94 XMLReader::COMMENT => 'COMMENT (例: <!-- comment -->)', 95 XMLReader::DOCUMENT => 'DOCUMENT (ドキュメントルート)', 96 XMLReader::DOCUMENT_TYPE => 'DOCUMENT_TYPE (例: <!DOCTYPE ...>)', 97 XMLReader::DOCUMENT_FRAGMENT => 'DOCUMENT_FRAGMENT', 98 XMLReader::NOTATION => 'NOTATION', 99 XMLReader::WHITESPACE => 'WHITESPACE', 100 XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE', 101 XMLReader::END_ELEMENT => 'END_ELEMENT (例: </tag>)', 102 XMLReader::END_ENTITY => 'END_ENTITY', 103 XMLReader::XML_DECLARATION => 'XML_DECLARATION (例: <?xml ...?>)', 104 default => 'UNKNOWN', 105 }; 106} 107 108// 実際のXML解析の実行例 109$xmlDocument = <<<'XML' 110<?xml version="1.0" encoding="UTF-8"?> 111<!DOCTYPE root [ 112 <!-- ユーザー定義エンティティ: &mycompany; --> 113 <!ENTITY mycompany "Acme Corp. & Co."> 114 <!-- ユーザー定義エンティティ: &greeting; --> 115 <!ENTITY greeting "Hello from &mycompany;!"> 116]> 117<root> 118 <item id="1">Item 1 with <special> chars.</item> 119 <item id="2">Item 2 with &greeting;</item> 120 <description>This is a description containing <![CDATA[<tag> & "escaped text"]]>. XMLReader processes entities automatically.</description> 121 <!-- これはXMLのコメントです --> 122</root> 123XML; 124 125// 上記のXMLコンテンツを解析する関数を呼び出し 126processXmlWithEntityDecoding($xmlDocument); 127
このサンプルコードは、PHPのXMLReaderクラスを使ってXMLドキュメントを解析し、XMLエンティティがどのように処理(デコード)されるかを初心者にも分かりやすく示しています。processXmlWithEntityDecoding関数は、解析したいXML文字列($xmlString)を引数として受け取り、戻り値は特にありません(void)。
PHPのXMLReaderは、要素内のテキストや属性値に含まれる<や&といった一般的なXMLエンティティを、デフォルトで自動的にデコードし、それぞれ<や&に変換して提供します。
本サンプルでは、XMLReader::ENTITY定数の役割を詳しく解説しています。この定数は、XMLドキュメントのDTD(Document Type Definition)内で定義されているエンティティそのもの、例えば <!ENTITY mycompany "Acme Corp. & Co."> の mycompany の部分を表すノードタイプです。エンティティの定義情報を読み取るには、setParserProperty(XMLReader::LOADDTD, true)を設定してDTDのロードを有効にする必要があります。
XMLドキュメント内でエンティティを参照している部分(例: &mycompany;)は、XMLReader::ENTITY_REFERENCEノードとして検出されます。XMLReaderは、この参照を読み込む際にその内容を自動的に展開(デコード)しようとします。したがって、ENTITY_REFERENCEノード自体のvalueは通常空で、展開された内容は後続のテキストノードとして取得されることが多いです。このコードは、これらの違いを実際の解析結果を通して示しています。
このサンプルコードは、PHPのXMLReaderがXMLエンティティを自動でデコードする挙動と、XMLReader::ENTITY定数の役割を解説しています。XMLReaderは、要素のテキストや属性値に含まれる<や&といった一般的なXMLエンティティ参照を自動的にデコードし、その実際の値をvalueプロパティで提供しますので、明示的なデコード処理は通常不要です。
XMLReader::ENTITY定数は、XMLドキュメントのDTD内で定義されているエンティティそのものを指します。これは、ドキュメント本文中に現れるエンティティ参照(例:&myentity;)を示すXMLReader::ENTITY_REFERENCEとは区別が必要です。エンティティ参照は通常、XMLReaderによってその内容が自動的に展開されます。XMLReader::ENTITYノードを検出するためには、setParserProperty(XMLReader::LOADDTD, true)を設定する必要がありますが、外部DTDのロードはセキュリティリスク(XXE攻撃など)を伴う可能性があるため、信頼できないXMLソースを扱う際は特に注意が必要です。
PHP XMLReader エンティティ変換処理
1<?php 2 3/** 4 * XMLReader を使用してXMLドキュメント内のエンティティ参照を処理し、 5 * その変換(解決)を観察する関数です。 6 * 7 * XMLReader::ENTITY は、XMLパーサーがエンティティ参照(例: &, &customEntity;)を 8 * 検出した際に示すノードタイプです。 9 * 10 * この関数は、XMLドキュメントを逐次読み込み、エンティティ参照ノードを検出した際に 11 * その名前を出力します。また、エンティティが解決されたテキストコンテンツが 12 * どのように得られるかも示します。 13 * これは、XMLデータを読み込み、その内部にある情報を適切に「変換」して 14 * 利用する基本的な考え方(哲学)を示しています。 15 * 16 * @param string $xmlString 処理するXML文字列。 17 */ 18function transformXmlEntities(string $xmlString): void 19{ 20 $reader = new XMLReader(); 21 22 // XML文字列を読み込みソースとして設定 23 // 内部DTDでカスタムエンティティを定義することで、シンプルにエンティティ解決を試みます。 24 // 外部DTDを使用する場合、セキュリティ上の理由からデフォルトで無効になっているため、 25 // $reader->setParserProperty(XMLReader::LOADDTD, true) などで明示的に有効化が必要です。 26 if (!$reader->XML($xmlString)) { 27 echo "エラー: XML文字列の読み込みに失敗しました。\n"; 28 return; 29 } 30 31 echo "--- XML エンティティ変換処理開始 ---\n"; 32 33 // XMLドキュメントをノードごとに読み進める 34 while ($reader->read()) { 35 switch ($reader->nodeType) { 36 case XMLReader::ENTITY: 37 // XMLReader::ENTITY は、XMLドキュメント内でエンティティ参照が 38 // 出現した箇所を示すノードタイプです。 39 // 例えば、<item>テキスト&テキスト</item> の "&" の部分です。 40 // このノード自体からは、解決されたエンティティの値は直接取得できません。 41 // しかし、どのエンティティが参照されているか(名前)を知ることができます。 42 echo sprintf( 43 "検出: エンティティ参照 '%s' (名前: %s)\n", 44 $reader->outerXml, // エンティティ参照の生の文字列(例: "&") 45 $reader->name // エンティティの名前(例: "amp", "customEntity") 46 ); 47 break; 48 49 case XMLReader::ELEMENT: 50 // 要素ノードの処理。 51 echo sprintf("要素: <%s>\n", $reader->name); 52 53 // 特定の要素 (例: 'message') の内部コンテンツを読み取ることで、 54 // 含まれるエンティティ参照が解決(変換)される様子を示します。 55 // readString() を呼び出すと、リーダーは現在の要素の終了タグまで進みます。 56 if ($reader->name === 'message' && !$reader->isEmptyElement) { 57 $content = $reader->readString(); // エンティティが解決された文字列 58 echo sprintf(" 要素 <%s> の解決されたコンテンツ: \"%s\"\n", $reader->name, $content); 59 } 60 break; 61 62 case XMLReader::TEXT: 63 // テキストノードの処理。 64 // ここに含まれるテキストは、既にエンティティが解決された(変換された)状態です。 65 // 例えば、"&" はこの段階で "&" に変換されています。 66 if (trim($reader->value) !== '') { 67 echo sprintf("テキスト: \"%s\"\n", $reader->value); 68 } 69 break; 70 71 // 他のノードタイプも存在しますが、今回はエンティティの処理に焦点を当てています。 72 } 73 } 74 75 $reader->close(); 76 echo "--- XML エンティティ変換処理終了 ---\n"; 77} 78 79// サンプルXMLデータ 80// 内部DTDでカスタムエンティティを定義し、組み込みエンティティ & も使用します。 81$xmlData = <<<XML 82<?xml version="1.0" encoding="UTF-8"?> 83<!DOCTYPE root [ 84 <!ENTITY customEntity "スペシャルなカスタム値"> 85]> 86<root> 87 <item>このテキストには&が含まれます。</item> 88 <message>これは&customEntity;を含むメッセージです。</message> 89 <data>別のデータ</data> 90</root> 91XML; 92 93// 関数を実行して、XMLエンティティの処理と変換の挙動を観察します。 94transformXmlEntities($xmlData);
このサンプルコードは、PHPのXMLReader拡張機能を用いて、XMLドキュメント内のエンティティ参照を処理し、その変換(解決)の様子を示すものです。XMLReader::ENTITYは、XMLパーサーがXMLドキュメント内でエンティティ参照(例: &, &customEntity;)を検出した際に、そのノードタイプとして識別するために使われる定数です。この定数自体は引数を取らず、内部的に整数値を保持します。
transformXmlEntities関数は、引数として与えられたXML文字列を一行ずつ読み込み、各ノードのタイプを調べて処理を行います。特にXMLReader::ENTITYタイプが検出された際には、それがどのようなエンティティ参照であるか(参照名や元の文字列)を出力します。また、要素のコンテンツやテキストノードを読み取ることで、&が&に、&customEntity;が定義された「スペシャルなカスタム値」に変わるなど、エンティティ参照がどのように実際のデータに「解決」(変換)されるかを示しています。
この処理を通じて、XMLデータを効率的に解析し、エンティティのような内部的な参照を適切な値に変換して利用するという、データ処理の基本的な考え方(哲学)を理解することができます。transformXmlEntities関数はvoidを戻り値とし、直接値を返さずに処理の経過と結果を標準出力に表示することで、その挙動を観察できるようになっています。
このサンプルコードは、XMLReader::ENTITYが示すエンティティ参照ノードの扱いに重点を置いています。XMLReader::ENTITYノードではエンティティ参照の元の表記や名前がわかりますが、その時点では解決された実際のテキスト値は取得できません。解決されたテキストは、親要素のコンテンツをreadString()で読み込んだり、その後のTEXTノードとして現れる部分で取得できる点にご注意ください。外部DTDを利用する際は、セキュリティ上の理由からデフォルトで無効化されています。信頼できない外部DTDを読み込むことはセキュリティリスクを高めるため、setParserPropertyで明示的に有効化する場合は安全性を十分に確認してください。また、readString()は現在の要素の終了タグまでリーダーを進めるため、ループ内の読み込み位置に注意が必要です。XMLデータを正しく「変換」して利用するための処理の考え方を理解しましょう。