【PHP8.x】Dom\Element::substitutedNodeValueプロパティの使い方
substitutedNodeValueプロパティの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
substitutedNodeValueプロパティは、DOM Elementノードが置換されたノードの値を取得または設定するためのプロパティです。このプロパティは、特にXML外部エンティティ(XXE)攻撃を緩和するために導入されました。
XXE攻撃は、外部エンティティの定義を通じて、サーバー上の機密情報にアクセスしたり、サービス拒否攻撃を引き起こしたりする可能性があります。substitutedNodeValueプロパティを使用することで、外部エンティティが展開される前の元のノードの値を取得できます。
具体的には、XMLドキュメントに外部エンティティ参照が含まれている場合、DOMDocument::resolveExternalsプロパティがTRUEに設定されていると、パーサーは外部エンティティを展開します。nodeValueプロパティは、展開された後の値を返しますが、substitutedNodeValueプロパティは、展開される前のエンティティ参照の文字列(例:&entityName;)を返します。
システムエンジニアがこのプロパティを理解し、適切に使用することで、XXE攻撃に対する脆弱性を低減し、より安全なXML処理を実装することができます。XMLデータを扱う際には、substitutedNodeValueプロパティの存在を意識し、必要に応じて活用することを推奨します。これにより、予期せぬ情報漏洩やシステムへの不正アクセスを防ぐことが可能になります。特に、外部からのXMLデータを扱うシステムにおいては、このプロパティの理解と適切な利用が重要となります。
構文(syntax)
1readonly public ?string $substitutedNodeValue
引数(parameters)
引数なし
引数はありません
戻り値(return)
string|null
このプロパティは、要素が置換ノードである場合にそのノードの値を文字列として返します。要素が置換ノードでない場合はnullを返します。
サンプルコード
PHP Dom\Element substitutedNodeValue を理解する
1<?php 2 3/** 4 * Dom\Element::substitutedNodeValue プロパティの動作を示すサンプルコードです。 5 * 6 * このプロパティは、XML ドキュメント内でエンティティ参照やデフォルト値が適用された後、 7 * 要素のノードが置き換えられた場合の値を返します。 8 * 主にXMLエンティティが解決された後の内容を確認する際に役立ちます。 9 */ 10function demonstrateSubstitutedNodeValue(): void 11{ 12 // エンティティ参照を含むXML文字列を定義します。 13 // <!DOCTYPE> 宣言内で '&greeting;' と '&object;' というエンティティを定義しています。 14 $xmlString = <<<XML 15<?xml version="1.0" encoding="UTF-8"?> 16<!DOCTYPE root [ 17 <!ENTITY greeting "Hello"> 18 <!ENTITY object "PHP World"> 19]> 20<root> 21 <message>&greeting;, &object;!</message> 22 <plainText>This is just plain text.</plainText> 23</root> 24XML; 25 26 // Dom\Document オブジェクトを作成します。 27 // PHP 8 からは Dom 名前空間を使用します。 28 $dom = new Dom\Document(); 29 30 // XML文字列をロードします。 31 // loadXML() は、DTD (Document Type Definition) を処理し、エンティティを解決します。 32 if (!$dom->loadXML($xmlString)) { 33 echo "エラー: XMLのロードに失敗しました。\n"; 34 return; 35 } 36 37 echo "--- Dom\\Element::substitutedNodeValue のデモンストレーション ---\n\n"; 38 39 // <message> 要素を取得します。 40 // この要素にはエンティティ参照 (&greeting;, &object;) が含まれています。 41 $messageElements = $dom->getElementsByTagName('message'); 42 43 if ($messageElements->count() > 0) { 44 /** @var Dom\Element $messageElement 取得した要素は Dom\Element 型として扱います。 */ 45 $messageElement = $messageElements->item(0); 46 47 echo "■ <message> 要素について:\n"; 48 echo " - nodeValue (生の値、エンティティは未解決): " . $messageElement->nodeValue . "\n"; 49 echo " - substitutedNodeValue (置換後の値、エンティティは解決済み): " . $messageElement->substitutedNodeValue . "\n\n"; 50 51 echo "解説: nodeValue は XML ソースコード上のそのままの値('&greeting;, &object;!')ですが、\n"; 52 echo " substitutedNodeValue は定義されたエンティティが 'Hello, PHP World!' に置換された値を示します。\n\n"; 53 } else { 54 echo "エラー: <message> 要素が見つかりませんでした。\n\n"; 55 } 56 57 // <plainText> 要素を取得します。 58 // この要素にはエンティティ参照が含まれていません。 59 $plainTextElements = $dom->getElementsByTagName('plainText'); 60 61 if ($plainTextElements->count() > 0) { 62 /** @var Dom\Element $plainTextElement */ 63 $plainTextElement = $plainTextElements->item(0); 64 65 echo "■ <plainText> 要素について (エンティティ参照なし):\n"; 66 echo " - nodeValue: " . $plainTextElement->nodeValue . "\n"; 67 echo " - substitutedNodeValue: " . $plainTextElement->substitutedNodeValue . "\n\n"; 68 69 echo "解説: エンティティ参照がない場合、nodeValue と substitutedNodeValue は同じ値になります。\n"; 70 echo " これは、置換するべきエンティティが存在しないため、元の値がそのまま返されるためです。\n"; 71 } else { 72 echo "エラー: <plainText> 要素が見つかりませんでした。\n"; 73 } 74} 75 76// 定義した関数を実行して、動作を確認します。 77demonstrateSubstitutedNodeValue();
PHP 8のDom\Element::substitutedNodeValueプロパティは、XMLドキュメント内の要素の値を、エンティティ参照やデフォルト値が適用されて置換された後の、最終的な形で取得するために使用されます。このプロパティは引数を取らず、要素の置換後の値として文字列を返します。もし置換後の値が存在しない場合はnullが返されます。
サンプルコードでは、エンティティ参照を含む<message>要素と、エンティティ参照を含まない<plainText>要素の二つを扱っています。<message>要素の場合、nodeValueプロパティはXMLソース上の「&greeting;, &object;!」という生の文字列を返しますが、substitutedNodeValueプロパティはXMLパーサがエンティティを解決し、「Hello, PHP World!」という最終的な文字列を返します。これにより、エンティティが定義された内容に置き換えられた後の値を確認できます。一方、<plainText>要素のようにエンティティ参照がない場合は、置換の必要がないためnodeValueとsubstitutedNodeValueはどちらも「This is just plain text.」と同じ値を返します。このプロパティは、XMLドキュメントのDTD(文書型定義)に基づいてエンティティなどが処理された後の、要素の実際のテキスト内容を知りたい場合に役立ちます。
このサンプルコードはPHP 8以降で導入されたDom名前空間を利用しています。Dom\Element::substitutedNodeValueプロパティは、XMLドキュメント内のエンティティ参照(例: &greeting;)が解決され、実際の値に置換された後の要素のテキスト内容を返します。対照的にnodeValueプロパティは、エンティティが解決される前の生の値を取得します。したがって、XMLエンティティが含まれる場合にのみ両者の値が異なります。このプロパティの戻り値はstringまたはnullとなるため、値がnullである可能性を考慮した処理が必要です。また、Dom\Document::loadXML()メソッドはXMLの形式が不正な場合に失敗することがあるため、必ずエラーハンドリングを行うようにしてください。
PHP Dom\Element substitutedNodeValue を取得する
1<?php 2 3/** 4 * Dom\Element::substitutedNodeValue プロパティの使用例を示します。 5 * このプロパティは、要素の置換されたノード値(例: HTMLエンティティが解決された値)を返します。 6 * Dom\Element::textContent と比較することで、その違いを理解しやすくなります。 7 */ 8function demonstrateSubstitutedNodeValue(): void 9{ 10 // 新しい Dom\Document を作成します。 11 // loadHTML を使用して、HTML文字列からDOMツリーを構築します。 12 // これにより、要素やエンティティが適切にパースされます。 13 $document = new Dom\Document(); 14 $document->loadHTML('<!DOCTYPE html><html><body><p id="myParagraph">Hello & World!</p></body></html>'); 15 16 // id が "myParagraph" の要素を取得します。 17 // Dom\Document::getElementById は、指定されたIDを持つ要素を返します。 18 $element = $document->getElementById('myParagraph'); 19 20 // 要素が正常に取得できたかを確認します。 21 if ($element instanceof Dom\Element) { 22 echo "要素のテキストコンテンツ (textContent): " . $element->textContent . "\n"; 23 // textContent は、通常、エンティティをそのままの形式で保持します。 24 25 echo "要素の置換されたノード値 (substitutedNodeValue): " . $element->substitutedNodeValue . "\n"; 26 // substitutedNodeValue は、& のようなHTMLエンティティが & に解決された値を提供します。 27 // これは、ユーザーに表示される最終的なテキストコンテンツに近い値です。 28 } else { 29 echo "指定されたIDの要素が見つかりませんでした。\n"; 30 } 31 32 // null が返されるケース(例: 要素にテキストコンテンツがない場合や、存在しない要素の場合)も確認します。 33 // まず、新しい空の要素を作成します。 34 $emptyElement = $document->createElement('emptyNode'); 35 $document->appendChild($emptyElement); // ドキュメントに要素を追加 36 37 echo "空の要素の substitutedNodeValue: " . ($emptyElement->substitutedNodeValue ?? 'NULL') . "\n"; 38 // テキストコンテンツがない場合、substitutedNodeValue は通常 null を返します。 39} 40 41// 関数を実行して、サンプルコードの動作を確認します。 42demonstrateSubstitutedNodeValue();
PHPのDom\Element::substitutedNodeValueプロパティは、HTMLドキュメント内の要素から「置換されたノード値」を取得するために利用されます。このプロパティは引数を一切取らず、要素のテキストコンテンツをstring型で返すか、またはnullを返します。
具体的には、要素が&のようなHTMLエンティティを含んでいる場合、substitutedNodeValueプロパティはこれらのエンティティを解決し、実際の文字(この場合は&)に変換したテキスト値を返します。これにより、ウェブブラウザでユーザーに表示される最終的なテキストコンテンツに近い値を得ることができます。一方で、textContentプロパティはエンティティをそのままの形式で保持する傾向があります。
サンプルコードでは、まずHTML文字列から<p>要素を取得しています。この要素は"Hello & World!"というテキストを含んでいます。$element->textContentでは"Hello & World!"と出力されるのに対し、$element->substitutedNodeValueでは&が&に解決され、"Hello & World!"と出力される様子が確認できます。
また、要素にテキストコンテンツが存在しない場合や、テキストノードが含まれていない場合には、このプロパティはnullを返します。サンプルコードの後半では、空の要素に対してsubstitutedNodeValueを呼び出し、結果がnullとなるケースも示しています。substitutedNodeValueは、DOM要素のテキスト内容を、よりユーザー表示に近い形で取得したい場合に有用なプロパティです。
Dom\Element::substitutedNodeValueプロパティは、HTMLエンティティ(例: &)が解決されたテキストを返します。これはtextContentがエンティティをそのまま保持する点と異なりますので、意図するテキスト内容に応じて使い分けることが重要です。この違いを理解せずに使用すると、期待する結果が得られない場合があります。また、要素にテキストコンテンツが存在しない場合や、対象要素が見つからない場合など、戻り値がnullになることがあります。コードを安全に利用するため、nullが返される可能性を考慮し、適切にハンドリングする処理を必ず記述してください。サンプルコードのように?? 'NULL'でデフォルト値を設定するなどの対応が推奨されます。