【PHP8.x】Dom\EntityReference::C14N()メソッドの使い方
C14Nメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『C14Nメソッドは、エンティティ参照ノードおよびその配下にあるすべての子孫ノードを、正規化された形式の文字列に変換して出力するメソッドです。XML文書は、記述方法が異なっていても論理的に同じ内容を表すことがあります。例えば、属性の順序や空白の有無が違っても意味は同じです。正規化(Canonicalization)とは、このような表記の揺れを統一されたルールに従って整形し、意味的に等価な文書が常に一意の文字列表現になるように変換する処理を指します。これにより、デジタル署名での検証や、二つの文書が内容的に同一であるかを正確に比較することが可能になります。このメソッドは、コメントを含めるか否かを指定する with_comments や、排他的正規化を行うかを指定する exclusive といった引数を通じて、W3Cの仕様に基づいた正規化の挙動を詳細に制御できます。処理が成功すると正規化された文字列を返し、失敗した場合は false を返します。
構文(syntax)
1<?php 2 3$doc = new Dom\Document(); 4$doc->loadXML('<!DOCTYPE r [<!ENTITY e "entity content">]><r>&e;</r>'); 5 6$entityRef = $doc->documentElement->firstChild; 7 8$canonicalString = $entityRef->C14N( 9 xpath_query: null, 10 exclusive: false, 11 with_comments: false, 12 ns_prefixes: null 13); 14
引数(parameters)
bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null
- bool $exclusive: true の場合、排他的な正規化を有効にします。
- bool $withComments: true の場合、コメントノードも正規化に含めます。
- ?array $xpath: 正規化の対象となるノードを XPath 式の配列で指定します。
- ?array $nsPrefixes: 名前空間プレフィックスと URI のマッピング配列を指定します。
戻り値(return)
string|false
このメソッドは、XMLエンティティ参照を正規化された文字列で返します。正規化に失敗した場合はfalseを返します。
サンプルコード
PHP Dom\EntityReference::C14N でXMLエンティティ正規化する
1<?php 2 3/** 4 * Dom\EntityReference::C14N の使用例を示します。 5 * 6 * この関数は、DTD(文書型定義)で定義されたエンティティ参照を含むXMLを作成し、 7 * そのエンティティ参照ノードを正規化(C14N)して結果を出力します。 8 * 正規化とは、XMLの論理的な意味を変えずに、物理的な表現(バイト列)を 9 * 一意に定めるプロセスで、電子署名などで利用されます。 10 */ 11function demonstrateEntityReferenceC14N(): void 12{ 13 // 1. DTDとエンティティ参照を含むXML文字列を定義します。 14 // DTD内でエンティティ 'writer' を 'Sample Author' として定義し、 15 // <author> タグ内で '&writer;' として参照しています。 16 $xmlString = <<<XML 17<?xml version="1.0" encoding="UTF-8"?> 18<!DOCTYPE root [ 19 <!ENTITY writer "Sample Author"> 20]> 21<root> 22 <author>&writer;</author> 23</root> 24XML; 25 26 // 2. DOMDocumentオブジェクトを作成し、XMLを読み込みます。 27 $doc = new DOMDocument(); 28 // 外部エンティティの読み込みを無効化しセキュリティを確保します(ベストプラクティス)。 29 if (!$doc->loadXML($xmlString, LIBXML_NONET)) { 30 echo "XMLの読み込みに失敗しました。\n"; 31 return; 32 } 33 34 // 3. XPathを使用してエンティティ参照ノードを取得します。 35 // <author> タグの最初の子ノードがエンティティ参照ノードに該当します。 36 $xpath = new DOMXPath($doc); 37 $nodeList = $xpath->query('//author/node()'); 38 39 if ($nodeList === false || $nodeList->length === 0) { 40 echo "ノードが見つかりませんでした。\n"; 41 return; 42 } 43 44 $entityRefNode = $nodeList->item(0); 45 46 // 4. ノードが Dom\EntityReference のインスタンスであることを確認し、C14N() を実行します。 47 if ($entityRefNode instanceof Dom\EntityReference) { 48 // C14N() メソッドを呼び出し、ノードを正規化します。 49 // エンティティ参照ノードの場合、その中身(展開されたテキスト)が返されます。 50 $canonicalString = $entityRefNode->C14N(); 51 52 // 5. 結果を出力します。 53 echo "エンティティ参照ノード '&" . $entityRefNode->nodeName . ";' の正規化結果:\n"; 54 echo "--------------------------------------------------\n"; 55 echo $canonicalString . "\n"; 56 } else { 57 echo "エンティティ参照ノードが見つかりませんでした。\n"; 58 } 59} 60 61// 関数を実行します。 62demonstrateEntityReferenceC14N(); 63
PHPの Dom\EntityReference::C14N() メソッドは、XML文書内のエンティティ参照ノードを正規化し、その結果を文字列として取得します。正規化(C14N)とは、XMLの論理的な意味を保ったまま、その物理的なバイト表現を標準的な形式に統一する処理です。これにより、XMLデータがバイト単位で比較可能となり、電子署名の検証などで利用されます。
サンプルコードでは、まずDTD(文書型定義)で &writer; というエンティティ参照を定義したXML文字列を準備します。次に、このXMLを読み込んで &writer; にあたるエンティティ参照ノードを取得し、C14N() メソッドを呼び出しています。このメソッドが実行されると、エンティティ参照はその定義内容であるテキスト「Sample Author」に置き換えられ、正規化された文字列として返されます。
C14N() メソッドには、排他的正規化の指定やコメントを含めるかどうかの設定など、正規化のルールを制御するための引数がありますが、これらは省略可能です。メソッドの戻り値は、処理が成功した場合は正規化された文字列 (string)、失敗した場合は false となります。
Dom\EntityReference::C14N()メソッドは、XMLのエンティティ参照ノードを正規化し、その中身を展開した文字列を返します。サンプルでは&writer;からSample Authorというテキストを取得しています。XMLを扱う際、特に外部のデータを読み込む場合は、loadXML関数の第二引数にLIBXML_NONETを指定することが非常に重要です。これにより、意図しない外部エンティティの読み込みを防ぎ、XXE脆弱性というセキュリティリスクを回避できます。また、XPathの//author/node()は<author>タグ直下の子ノードを取得する指定です。このコードのようにエンティティ参照が子ノードになっていることを確認してからC14N()を実行してください。正規化は電子署名などでXML文書の一意性を保証するための処理ですが、エンティティ参照ノードに使うと、定義されたテキストを取得できると理解すると分かりやすいです。