【PHP8.x】DOMProcessingInstruction::C14N()メソッドの使い方
C14Nメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
C14Nメソッドは、DOMProcessingInstructionオブジェクトが表す処理命令ノードを、W3Cが定める正規化(Canonicalization)の仕様に従って変換し、その結果を文字列として取得するメソッドです。正規化とは、XML文書の論理的な意味を保ったまま、属性の順序や空白の扱いなどを統一的なルールに基づいて整え、バイトレベルで一意な表現に変換する処理を指します。この処理は、主にXML署名などで文書の完全性を検証する際に重要となります。このメソッドには複数の引数があり、例えば第一引数のexclusiveをtrueに設定すると、排他的正規化というモードで処理が行われ、名前空間の扱いをより細かく制御できます。また、第二引数のwith_commentsで、出力にコメントノードを含めるかどうかを指定することも可能です。メソッドの実行に成功した場合は正規化された文字列が返され、失敗した場合はfalseが返ります。
構文(syntax)
1<?php 2 3$dom = new DOMDocument(); 4 5$processingInstruction = $dom->createProcessingInstruction( 6 'php-style', 7 'href="my-style.css"' 8); 9 10$canonicalString = $processingInstruction->C14N(); 11 12?>
引数(parameters)
bool $exclusive = false, bool $with_comments = false, ?array $xpath = null, ?array $ns_prefixes = null
- bool $exclusive = false: 排他的な正規化を行うかどうかを指定します。trueの場合、指定されたXPath式に一致するノードのみが正規化されます。
- bool $with_comments = false: コメントノードを含めて正規化するかどうかを指定します。
- ?array $xpath = null: 正規化の対象となるノードを絞り込むためのXPath式を指定します。
- ?array $ns_prefixes = null: 名前空間プレフィックスのリストを指定します。正規化の際にこれらのプレフィックスのみが考慮されます。
戻り値(return)
string|false
このメソッドは、XML文書の正規化された文字列表現を返します。正規化に失敗した場合は false を返します。
サンプルコード
PHP XML C14N処理命令を正規化する
1<?php 2 3declare(strict_types=1); 4 5/** 6 * XML文字列から特定の処理命令ノードを抽出し、正規化(C14N)するサンプル関数。 7 * 8 * XMLの正規化(Canonicalization, C14N)とは、論理的に等価なXML文書を 9 * バイトレベルで同一の表現に変換するプロセスです。 10 * これは主にXMLデジタル署名などで、署名対象の一意性を保証するために使用されます。 11 * 12 * このサンプルでは、XML文書内の <?xml-stylesheet ... ?> という処理命令ノードを対象に、 13 * DOMProcessingInstruction::C14N() メソッドを使って正規化を行います。 14 * 15 * @return void 16 */ 17function demonstrateProcessingInstructionC14N(): void 18{ 19 // サンプルとなるXML文字列を定義します。 20 // <?xml-stylesheet ... ?> の部分が「処理命令 (Processing Instruction)」です。 21 $xmlString = <<<XML 22<?xml version="1.0" encoding="UTF-8"?> 23<?xml-stylesheet type="text/css" href="style.css" ?> 24<document> 25 <title>Sample Document</title> 26</document> 27XML; 28 29 // DOMDocumentオブジェクトを生成し、XMLを読み込みます。 30 $dom = new DOMDocument(); 31 if (!$dom->loadXML($xmlString)) { 32 echo "XMLの読み込みに失敗しました。" . PHP_EOL; 33 return; 34 } 35 36 // 処理命令ノード (DOMProcessingInstruction) を探します。 37 $piNode = null; 38 foreach ($dom->childNodes as $node) { 39 // ノードの型が処理命令 (XML_PI_NODE) であるかチェックします。 40 if ($node instanceof DOMProcessingInstruction) { 41 $piNode = $node; 42 break; // 最初に見つかった処理命令ノードを使用します。 43 } 44 } 45 46 // 処理命令ノードが見つかった場合のみ、正規化を実行します。 47 if ($piNode) { 48 // DOMProcessingInstruction::C14N() を呼び出して、ノードを正規化された文字列に変換します。 49 // 引数はデフォルト値(排他的正規化なし、コメントなし)を使用します。 50 $canonicalizedString = $piNode->C14N(); 51 52 if ($canonicalizedString !== false) { 53 echo "元の処理命令ノードのターゲット: " . $piNode->target . PHP_EOL; 54 echo "元の処理命令ノードのデータ: " . $piNode->data . PHP_EOL; 55 echo "---" . PHP_EOL; 56 echo "正規化(C14N)された文字列:" . PHP_EOL; 57 echo $canonicalizedString . PHP_EOL; 58 } else { 59 echo "処理命令ノードの正規化に失敗しました。" . PHP_EOL; 60 } 61 } else { 62 echo "XML内に処理命令ノードが見つかりませんでした。" . PHP_EOL; 63 } 64} 65 66// 関数を実行して結果を表示します。 67demonstrateProcessingInstructionC14N(); 68
DOMProcessingInstructionクラスのC14N()メソッドは、XMLの処理命令ノードを「正規化(C14N)」し、その結果を文字列として返します。XMLの正規化とは、意味が同じであれば異なる書き方(例: 属性の順序や空白)が許されるXML文書を、バイトレベルで完全に同一の表現に統一する処理です。これにより、デジタル署名などでデータが改ざんされていないことを正確に検証できます。
このサンプルコードでは、まずXML文字列を読み込み、ループ処理で<?xml-stylesheet ... ?>という処理命令ノード(DOMProcessingInstructionオブジェクト)を取得します。次に、そのオブジェクトに対してC14N()メソッドを呼び出すことで、ノードを正規化された文字列に変換しています。引数を省略しているため、デフォルトの設定(排他的正規化なし、コメントを含まない)で処理が実行されます。
戻り値は、処理が成功した場合は正規化された文字列、失敗した場合はfalseです。コード内では、戻り値がfalseでないことを確認してから結果を出力しています。
このC14N()メソッドは、XML文書全体ではなく、特定の「処理命令ノード」一つだけを正規化(文字列化)する点に注意してください。サンプルコードの実行結果でも、<?xml-stylesheet ... ?>の部分のみが出力されていることが確認できます。メソッドが処理に失敗した場合はfalseを返すため、戻り値のチェックは必ず行いましょう。空文字列とfalseを明確に区別するため、サンプルコードのように!== falseを用いた厳密な比較が推奨されます。また、この機能はPHPのXML拡張が内部で利用するlibxml2ライブラリに依存しているため、サーバーの環境によっては利用できない可能性があります。
PHP DOMProcessingInstruction::C14N()でXML正規化
1<?php 2 3declare(strict_types=1); 4 5/** 6 * DOMProcessingInstruction::C14N() の使用例を示します。 7 * 8 * XMLドキュメント内の処理命令ノード(Processing Instruction Node)を取得し、 9 * それを正規化(Canonicalization)して文字列として出力します。 10 */ 11function demonstrateProcessingInstructionC14N(): void 12{ 13 // 処理命令(<?xml-stylesheet ...?>)を含むXML文字列を準備します。 14 $xmlString = <<<XML 15 <?xml version="1.0" encoding="UTF-8"?> 16 <?xml-stylesheet type="text/css" href="style.css"?> 17 <root> 18 <item>This is a sample XML.</item> 19 </root> 20 XML; 21 22 // DOMDocumentオブジェクトを作成し、XMLを読み込みます。 23 $document = new DOMDocument(); 24 $document->loadXML($xmlString); 25 26 // 処理命令ノード(型が XML_PI_NODE)を検索します。 27 $piNode = null; 28 foreach ($document->childNodes as $node) { 29 if ($node->nodeType === XML_PI_NODE) { 30 // DOMProcessingInstructionオブジェクトが見つかったらループを抜けます。 31 /** @var DOMProcessingInstruction $node */ 32 $piNode = $node; 33 break; 34 } 35 } 36 37 // 処理命令ノードが見つからない場合は処理を中断します。 38 if ($piNode === null) { 39 echo 'Processing Instruction node not found.' . PHP_EOL; 40 return; 41 } 42 43 // C14N()メソッドを呼び出して、ノードを正規化された文字列に変換します。 44 // C14N (Canonicalization) は、論理的に等価なXML文書が 45 // バイトレベルで同一表現となるように変換するプロセスです。 46 $canonicalizedString = $piNode->C14N(); 47 48 // 結果を出力します。 49 if ($canonicalizedString !== false) { 50 echo '--- Original Processing Instruction Node ---' . PHP_EOL; 51 echo $document->saveXML($piNode) . PHP_EOL; 52 echo PHP_EOL; 53 echo '--- Canonicalized (C14N) String ---' . PHP_EOL; 54 echo $canonicalizedString . PHP_EOL; 55 } else { 56 echo 'Failed to canonicalize the node.' . PHP_EOL; 57 } 58} 59 60// 関数を実行してサンプルコードの動作を確認します。 61demonstrateProcessingInstructionC14N();
このPHPサンプルコードは、XML文書内の処理命令ノード(DOMProcessingInstruction)を正規化(C14N)する方法を示します。処理命令ノードとは、<?xml-stylesheet ...?>のように、XMLプロセッサに特定の指示を与えるためのノードです。
コードはまず、処理命令を含むXML文字列をDOMDocumentオブジェクトに読み込みます。次に、DOMツリーの中から目的の処理命令ノードを探し出し、DOMProcessingInstructionオブジェクトとして取得します。
そして、このオブジェクトに対してC14N()メソッドを呼び出します。C14N()は「Canonicalization」の略で、XMLの論理的な意味を保ったまま、物理的な表現(バイト列)を標準的な形式に統一する処理です。これにより、見た目が少し違うXMLでも、内容が同じであれば同一の文字列として比較できるようになります。
このメソッドは、引数を指定することで正規化のルールを細かく制御できますが、このサンプルではデフォルト設定で実行しています。戻り値として正規化された文字列を返しますが、処理に失敗した場合はfalseが返されます。最終的に、元のノードのXML表現と、C14N()によって生成された正規化後の文字列を並べて出力し、その結果を確認できます。
このコードは、XML文書全体ではなく、特定の処理命令ノード(<?...?>の部分)を対象に正規化を行う点に注意が必要です。C14N()メソッドは処理に失敗するとfalseを返すため、戻り値のチェックは必ず厳密等価演算子(!== false)で行うようにしてください。正規化とは、空白や属性の順序といった見た目の違いをなくし、論理的に同じ内容であれば必ず同一の文字列に変換する処理のことです。主にXML電子署名などでデータの同一性を検証する際に利用されます。サンプルでは引数を省略していますが、要件に応じて排他的正規化の指定やコメントの有無などを引数で細かく制御できます。