【PHP8.x】DOMEntityReference::C14N()メソッドの使い方
C14Nメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
C14Nメソッドは、DOMEntityReferenceオブジェクトが表すエンティティ参照ノードと、そのすべての子孫ノードを、W3Cが定める正規化(Canonicalization)のルールに従って文字列に変換する処理を実行するメソッドです。XMLの正規化とは、属性の順序、空白文字の扱い、文字参照の形式といった表現上の差異を吸収し、論理的に等価なXML文書が必ず同一のバイト表現になるように変換するプロセスを指します。この処理は、XML文書に対するデジタル署名の生成や検証など、データの完全性や同一性を厳密に確認する必要がある場面で非常に重要です。このメソッドは、引数を通じて正規化の挙動を詳細に制御することが可能です。例えば、XML文書の一部をそのコンテキストから切り離して正規化する排他的正規化を行うか、コメントノードを変換後の文字列に含めるかなどを指定できます。処理が成功した場合は正規化されたノードを表す文字列を返し、失敗した場合にはfalseを返します。このメソッドを利用することで、XML文書内の特定のエンティティ参照部分を、標準化された一貫性のあるテキスト形式として安全に取り出すことが可能になります。
構文(syntax)
1$result = $dom_entity_reference->C14N( 2 exclusive: false, 3 with_comments: false, 4 xpath: null, 5 ns_prefixes: null 6);
引数(parameters)
?string $uri, bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null
- ?string $uri: 正規化するエンティティ参照のURIを指定します。NULLを指定すると、現在のDOMツリー全体が対象となります。
- bool $exclusive = false: trueを指定すると、
xmlns属性は対象となる名前空間にのみ限定されます。 - bool $withComments = false: trueを指定すると、コメントノードも正規化の対象に含めます。
- ?array $xpath = null: 正規化の対象とするノードをXPathクエリの配列で指定します。
- ?array $nsPrefixes = null: 正規化に含める名前空間のプレフィックスの配列を指定します。
戻り値(return)
string|false
このメソッドは、エンティティ参照を正規化された文字列として返します。正規化に失敗した場合は false を返します。
サンプルコード
PHP XML C14Nで実体参照を正規化する
1<?php 2 3/** 4 * XML内の実体参照ノードを正規化するサンプルコード。 5 * 6 * この関数は、DTDで定義された実体参照を含むXML文字列を解析し、 7 * DOMEntityReference オブジェクトを取得します。 8 * その後、C14N() メソッドを使用してノードを正規化(Canonicalization)し、 9 * 標準的なテキスト表現に変換した結果を出力します。 10 */ 11function canonicalizeEntityReference(): void 12{ 13 // 内部実体を定義したXML文字列 14 // '&writer;' は "John Doe" というテキストを参照します。 15 $xmlString = <<<XML 16 <?xml version="1.0" encoding="UTF-8"?> 17 <!DOCTYPE book [ 18 <!ENTITY writer "John Doe"> 19 ]> 20 <book> 21 <author>&writer;</author> 22 </book> 23 XML; 24 25 // DOMDocument オブジェクトを生成 26 $document = new DOMDocument(); 27 28 // XML文字列を読み込みます。 29 // この時、実体参照は DOMEntityReference ノードとして保持されます。 30 $document->loadXML($xmlString); 31 32 // <author> 要素内の実体参照ノード (&writer;) を取得します。 33 // getElementsByTagName('author')->item(0) で <author> ノードを取得し、 34 // ->firstChild でその最初の子ノード (実体参照ノード) を取得します。 35 $entityRefNode = $document->getElementsByTagName('author')->item(0)->firstChild; 36 37 // ノードが DOMEntityReference であることを確認 38 if ($entityRefNode instanceof DOMEntityReference) { 39 // C14N() はノードを正規化された文字列に変換します。 40 // C14N は Canonicalization の略で、XMLを標準的な形式に変換するプロセスです。 41 // 実体参照の場合、参照先のテキスト ('John Doe') が返されます。 42 $canonicalizedText = $entityRefNode->C14N(); 43 44 if ($canonicalizedText !== false) { 45 // 実行結果: 正規化されたテキスト: John Doe 46 echo "正規化されたテキスト: " . $canonicalizedText . PHP_EOL; 47 } else { 48 echo "ノードの正規化に失敗しました。" . PHP_EOL; 49 } 50 } else { 51 echo "実体参照ノードが見つかりませんでした。" . PHP_EOL; 52 } 53} 54 55// 関数を実行 56canonicalizeEntityReference(); 57
DOMEntityReference::C14Nメソッドは、XML文書内の実体参照ノードを、正規化(C14N)された標準的な文字列形式に変換するために使用します。正規化とは、意味的に同じXML文書が必ず同じバイト表現になるように変換する処理のことです。
このサンプルコードでは、まずDTD(文書型定義)で&writer;という実体参照に"John Doe"という文字列を定義したXML文字列を用意します。次にDOMDocumentオブジェクトでこのXMLを読み込み、<author>タグの子要素として存在する実体参照ノード(DOMEntityReferenceオブジェクト)を取得しています。
取得したノードオブジェクトに対してC14N()メソッドを実行すると、実体参照がその参照先の内容、つまり"John Doe"というテキストに置き換えられた文字列が返されます。このメソッドは、正規化のアルゴリズムを指定するURIや、コメントを含めるかどうかなどをオプションの引数で指定できますが、このサンプルでは引数を省略し、デフォルトのルールで変換しています。
戻り値は、処理が成功した場合は正規化された文字列、失敗した場合はfalseとなります。このため、サンプルコードでは戻り値がfalseでないことを確認してから、変換後の文字列を出力しています。
DOMDocumentでXMLを読み込む際、DTDで定義された実体参照は DOMEntityReference ノードとして保持されます。サンプルコードでは firstChild を使ってこのノードを取得していますが、他のノードが先にある可能性も考慮し、実際の開発ではノードの種類をループなどで確認するとより安全です。C14N()メソッドはノードを標準的なテキスト形式(正規化)に変換するもので、実体参照ノードの場合は参照先のテキストが返されます。このメソッドは処理に失敗すると false を返すため、サンプルコードのように !== false を使って厳密に返り値をチェックすることが重要です。この機能はPHPの dom 拡張機能に依存します。
PHPでXMLをC14N1.1正規化する
1<?php 2 3declare(strict_types=1); 4 5/** 6 * XML文字列を C14N 1.1 (Canonical XML Version 1.1) を使用して正規化するサンプルコード 7 * 8 * この関数は、属性の順序や空白が異なるXML文字列を読み込み、 9 * DOMDocument::C14N() メソッドを使用して正規化し、結果を出力します。 10 * キーワード「c14n11」に対応するため、C14NのURIとして 11 * 'http://www.w3.org/2006/12/xml-c14n11' を指定します。 12 * 13 * @return void 14 */ 15function canonicalizeXmlWithC14N11(): void 16{ 17 // 正規化の対象となるXML文字列 18 // コメント、余分な空白、順不同の属性が含まれています。 19 $xmlString = <<<XML 20<?xml version="1.0" encoding="UTF-8"?> 21<doc xmlns:ns="http://www.example.com/ns"> 22 <!-- This is a comment --> 23 <element attr2="valueB" attr1="valueA" > 24 Some text. 25 </element> 26</doc> 27XML; 28 29 try { 30 // DOMDocumentオブジェクトを生成し、XMLを読み込む 31 $doc = new DOMDocument(); 32 $doc->loadXML($xmlString); 33 34 // DOMDocument::C14N() メソッドを呼び出してXMLを正規化する 35 // 第1引数に C14N 1.1 を示すURIを指定します。 36 // 第3引数を false に設定し、コメントは含めないようにします。 37 $canonicalXml = $doc->C14N( 38 'http://www.w3.org/2006/12/xml-c14n11', // $uri: C14N 1.1 を指定 39 false, // $exclusive 40 false // $withComments 41 ); 42 43 if ($canonicalXml === false) { 44 echo "XMLの正規化に失敗しました。\n"; 45 return; 46 } 47 48 // 結果を出力する 49 echo "--- Original XML ---\n"; 50 echo $xmlString . "\n\n"; 51 echo "--- Canonicalized XML (C14N 1.1) ---\n"; 52 echo $canonicalXml . "\n"; 53 54 } catch (DOMException $e) { 55 echo "エラー: " . $e->getMessage() . "\n"; 56 } 57} 58 59// 関数を実行 60canonicalizeXmlWithC14N11();
このサンプルコードは、PHPのDOM拡張機能を使い、XML文字列を「正規化」する方法を示しています。XMLの正規化とは、属性の記述順序や空白の有無といった表現上の差異を吸収し、意味的に同じ内容であれば、必ず同一のバイト列表現に変換する処理のことです。これにより、XMLデータの一貫性を保ち、電子署名などで文書が改ざんされていないかを確認する際に役立ちます。
コードの中心はDOMDocumentクラスのC14N()メソッドです。このメソッドは、読み込まれたXMLノードを正規化し、その結果を文字列として返します。引数には、正規化の方式などを指定します。第1引数$uriには、正規化のルールを示すURIを渡します。このサンプルではキーワード「c14n11」に対応するため、'http://www.w3.org/2006/12/xml-c14n11'を指定し、「Canonical XML Version 1.1」という規格で処理することを明示しています。また、第3引数$withCommentsをfalseに設定することで、正規化後のXMLにコメントを含めないようにしています。
このメソッドの戻り値は、正規化に成功した場合はその結果の文字列(string)、失敗した場合はfalseとなります。サンプルコードの実行結果を見ると、元のXMLにあったコメントが削除され、属性がアルファベット順に並び替えられていることが確認できます。
このコードで利用されているC14Nメソッドは、処理に失敗するとfalseを返すため、サンプルコードのように必ず厳密等価演算子(===)で戻り値を確認し、エラー処理を行うことが重要です。第1引数のURIは正規化のバージョンを決定します。今回はc14n11に対応するURIを指定していますが、これを省略したり間違えたりすると意図しないバージョンで処理されるため注意が必要です。また、第3引数でコメントを出力に含めるかを選択できます。このメソッドはXML文書全体だけでなく、特定の要素ノードに対しても適用でき、文書の一部だけを正規化することも可能です。