【PHP8.x】Dom\DocumentType::C14N()メソッドの使い方
C14Nメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『C14Nメソッドは、DocumentTypeノードをW3C勧告の仕様に基づいて正規化(Canonicalize)し、その結果を文字列として取得するために実行するメソッドです。正規化とは、XML文書の論理的な意味を変えることなく、属性の順序や空白の扱いなどを定められたルールに従って統一的な形式に変換する処理を指します。この処理により、例えば電子署名などで文書の同一性を検証する際に、異なる環境で生成されたXML文書でもバイト単位で厳密に比較することが可能になります。このメソッドは、XML文書全体ではなく、<!DOCTYPE ...>で表される文書型定義(DTD)の部分のみを対象とします。引数を通じて、正規化のモードを細かく制御することが可能です。例えば、第一引数の exclusive で排他的正規化を行うか、第二引数の withComments でコメントノードを結果に含めるかを真偽値で指定できます。処理が成功すると正規化されたDTDの文字列が返され、失敗した場合は false が返ります。』
構文(syntax)
1<?php 2 3$dom = new DOMDocument(); 4$dom->loadHTML('<!DOCTYPE html><html><body><p>Hello</p></body></html>'); 5 6$doctype = $dom->doctype; 7 8if ($doctype instanceof \Dom\DocumentType) { 9 // DocumentTypeノードを正規化された文字列形式に変換します 10 $canonicalString = $doctype->C14N(); 11 12 echo $canonicalString; 13} 14 15?>
引数(parameters)
bool $exclusive = false, bool $withComments = false, ?array $xpath = null, ?array $nsPrefixes = null
- bool $exclusive = false: 排他セレクタを使用して正規化するかどうかを決定するブール値。
trueに設定すると、指定されたXPath式に一致する要素のみが正規化されます。 - bool $withComments = false: コメントを含めて正規化するかどうかを決定するブール値。
- ?array $xpath = null: 正規化の対象となる要素を指定するXPath式の配列。
nullの場合は、ドキュメント全体が対象となります。 - ?array $nsPrefixes = null: 正規化の際に含める名前空間プレフィックスの配列。
nullの場合は、使用されているすべての名前空間が考慮されます。
戻り値(return)
string|false
C14Nメソッドは、XML文書の正規化された文字列表現を返します。正規化に失敗した場合はfalseを返します。
サンプルコード
PHP XML C14NによるDTD正規化
1<?php 2 3/** 4 * Dom\DocumentType::C14N() を使用して文書型定義(DTD)を正規化するサンプル関数 5 * 6 * XML文書から文書型定義(DTD)ノードを取得し、 7 * それを正規化(Canonicalization)した文字列を生成して出力します。 8 * 9 * @return void 10 */ 11function demonstrateDocumentTypeC14N(): void 12{ 13 // DTD (文書型定義) を含むXML文字列を準備します 14 // この例では、外部DTDサブセットと内部DTDサブセットの両方を含んでいます 15 $xmlString = <<<XML 16<?xml version="1.0" encoding="UTF-8"?> 17<!DOCTYPE note SYSTEM "Note.dtd" [ 18 <!ELEMENT note (to, from, heading, body)> 19 <!ELEMENT to (#PCDATA)> 20 <!ELEMENT from (#PCDATA)> 21 <!ELEMENT heading (#PCDATA)> 22 <!ELEMENT body (#PCDATA)> 23]> 24<note> 25 <to>Tove</to> 26 <from>Jani</from> 27 <heading>Reminder</heading> 28 <body>Don't forget me this weekend!</body> 29</note> 30XML; 31 32 // DOMDocument オブジェクトをインスタンス化します 33 $doc = new DOMDocument(); 34 35 // XML文字列を読み込みます 36 $doc->loadXML($xmlString); 37 38 // 文書から DocumentType ノード (<!DOCTYPE ... > の部分) を取得します 39 $docType = $doc->doctype; 40 41 // DocumentType ノードが存在するか確認します 42 if ($docType instanceof Dom\DocumentType) { 43 // DocumentType ノードを C14N (Canonical XML) 形式で正規化します 44 // これにより、論理的に等価なDTDは常に同じテキスト表現になります 45 $canonicalizedString = $docType->C14N(); 46 47 if ($canonicalizedString !== false) { 48 echo "正規化された文書型定義(DTD):\n"; 49 echo $canonicalizedString; 50 } else { 51 echo "DTDの正規化に失敗しました。"; 52 } 53 } else { 54 echo "XMLに文書型定義(DTD)が見つかりませんでした。"; 55 } 56} 57 58// 関数を実行します 59demonstrateDocumentTypeC14N();
このサンプルコードは、PHPのDom\DocumentType::C14N()メソッドを使い、XML文書に含まれる文書型定義(DTD)を正規化する方法を示しています。正規化(C14N)とは、XMLの論理的な意味を変えずに、テキスト表現をW3Cが定める標準的な形式に統一する処理のことです。これにより、見た目が異なるXMLでも内容が同じであれば、正規化後には完全に同一の文字列となり、データの一致性を厳密に比較できるようになります。
コードの処理の流れとして、まずDTDが記述されたXML文字列をDOMDocumentオブジェクトに読み込みます。次に、doctypeプロパティから文書型定義を表すDom\DocumentTypeオブジェクトを取得します。そして、このオブジェクトに対してC14N()メソッドを呼び出すことで、DTD部分を正規化された文字列に変換しています。
C14N()メソッドは、正規化のモードを指定する$exclusive(真偽値)や、コメントを含めるかを指定する$withComments(真偽値)などの引数を取りますが、このサンプルでは省略しているためデフォルトの設定で動作します。メソッドの戻り値は、処理が成功すれば正規化された文字列、失敗した場合はfalseが返されます。そのため、コードでは戻り値がfalseでないことを確認してから、結果を画面に出力しています。
このコードはXMLの文書型定義(DTD)を正規化するものですが、注意点があります。まず、DTDを含まないXML文書を読み込んだ場合、$doc->doctypeプロパティはnullになります。そのため、メソッド呼び出し前に必ずinstanceofなどを用いてオブジェクトの存在を確認してください。次に、C14N()メソッドは処理に失敗するとfalseを返すため、戻り値を厳密な比較演算子!== falseで判定することが重要です。このメソッドはDTD部分のみを対象とします。XML文書全体を正規化する場合はDOMDocumentオブジェクト自身のC14N()メソッドを使用してください。
PHP XML C14N11 DTD正規化
1<?php 2 3declare(strict_types=1); 4 5/** 6 * Dom\DocumentType::C14N() を使用してDTDノードを正規化するサンプル 7 * 8 * XMLドキュメント内の文書型定義(DTD)ノードを 9 * 正規形(Canonical Form)の文字列に変換します。 10 * 正規化により、論理的に同じでもテキスト表現が異なるXMLの比較が可能になります。 11 */ 12function demonstrateDocumentTypeC14N(): void 13{ 14 // DTD (文書型定義) を含むXML文字列を準備します。 15 $xmlString = <<<XML 16<?xml version="1.0" encoding="UTF-8"?> 17<!DOCTYPE note [ 18 <!ELEMENT note (to,from,heading,body)> 19 <!ELEMENT to (#PCDATA)> 20 <!ELEMENT from (#PCDATA)> 21 <!ELEMENT heading (#PCDATA)> 22 <!ELEMENT body (#PCDATA)> 23]> 24<note> 25 <to>Tove</to> 26 <from>Jani</from> 27 <heading>Reminder</heading> 28 <body>Don't forget me this weekend!</body> 29</note> 30XML; 31 32 try { 33 // 1. Dom\Document オブジェクトを作成し、XMLを読み込みます。 34 // PHP 8以降では DOM* クラスは Dom\* 名前空間にあります。 35 $doc = new \Dom\Document(); 36 $doc->loadXML($xmlString); 37 38 // 2. DocumentType ノードを取得します。 39 // $doc->doctype プロパティで DTD を表す Dom\DocumentType オブジェクトを取得できます。 40 $docType = $doc->doctype; 41 42 // 3. DocumentType ノードが存在するか確認します。 43 if ($docType instanceof \Dom\DocumentType) { 44 // 4. C14N() メソッドを呼び出して正規化された文字列を取得します。 45 // このメソッドは、DTD部分を標準的なテキスト表現に変換します。 46 $canonicalizedString = $docType->C14N(); 47 48 if ($canonicalizedString !== false) { 49 echo "--- 正規化されたDTDノード ---" . PHP_EOL; 50 echo $canonicalizedString . PHP_EOL; 51 } else { 52 echo "DTDノードの正規化に失敗しました。" . PHP_EOL; 53 } 54 } else { 55 echo "DTD (DocumentType) ノードが見つかりませんでした。" . PHP_EOL; 56 } 57 } catch (\Dom\Exception $e) { 58 // XMLのパースエラーなど、DOM関連の例外を捕捉します。 59 echo "エラーが発生しました: " . $e->getMessage() . PHP_EOL; 60 } 61} 62 63// 関数を実行します。 64demonstrateDocumentTypeC14N(); 65
このPHPコードは、XML文書に含まれるDTD(文書型定義)を、Dom\DocumentType::C14N()メソッドを使って正規化するサンプルです。正規化(C14N)とは、論理的に同じ内容を持つXMLを、空白や属性の順序といった表記上の差異を吸収し、一意のテキスト表現に変換する処理を指します。これにより、異なるXMLデータが実質的に同じかどうかを、文字列として正確に比較できるようになります。
コードでは、まずDTDが定義されたXML文字列を読み込み、Dom\Documentオブジェクトを生成します。次に、doctypeプロパティを通じてDTDノードを表すDom\DocumentTypeオブジェクトを取得します。このオブジェクトのC14N()メソッドを呼び出すと、対象のDTDノードが標準的なルールに従って整形された文字列に変換されます。
C14N()メソッドは、排他的正規化の有効化やコメントを含めるかなどを指定する引数を持ちますが、このサンプルでは引数を省略しているため、標準的な設定で正規化が実行されます。メソッドの戻り値は、処理が成功した場合は正規化された文字列、失敗した場合はfalseです。そのため、コードのように戻り値がfalseでないことを確認してから結果を利用するのが安全な実装です。この処理により、XML文書の構造定義部分だけを、比較や検証のために標準化された形式で取り出すことができます。
このコードはPHP 8で導入されたDom名前空間を使用しているため、古いPHP環境では動作しません。XML文書に文書型定義(DTD)が含まれていない場合、$doc->doctypeはnullを返します。そのため、C14N()メソッドを呼び出す前には、必ずnullでないことを確認してください。また、C14N()メソッドは処理に失敗するとfalseを返す可能性があるため、その戻り値が文字列であることを検証してから利用することが重要です。この正規化という処理は、空白や属性の順序が違うだけの論理的に同じXMLを、テキストとして比較可能にするために使われます。XMLの形式が不正な場合は例外が発生するため、try-catch構文で処理全体を囲むと安全性が高まります。