はじめに
前回の記事では、XMLパーサーの現在の設定値を取得する xml_parser_get_option() を解説しました。今回はその対となる、設定を実際に変更するための関数、xml_parser_set_option() を正面から取り上げます。
これまでの記事の中でも、要素名の大文字小文字変換を制御したり、出力エンコーディングを指定したりする場面で、この関数がたびたび登場してきました。xml_parser_set_option() は、ExpatベースのXMLパーサーのデフォルトの挙動を、プロジェクトの要件に合わせて細かく調整するための重要な関数です。本記事では、設定可能な4つのオプションそれぞれの意味と、実践的な活用パターンを詳しく解説します。
関数概要
| 項目 | 内容 |
|---|---|
| 関数名 | xml_parser_set_option() |
| 所属拡張 | XML Parser拡張(Expatベース、標準で有効) |
| シグネチャ | xml_parser_set_option(XMLParser $parser, int $option, string|int|bool $value): bool |
| 引数1 | $parser — xml_parser_create()等で生成したパーサーインスタンス |
| 引数2 | $option — 設定したいオプションの定数(XML_OPTION_*) |
| 引数3 | $value — 設定する値 |
| 戻り値 | 成功時に true |
| 対応バージョン | PHP 4以降(PHP 8.0以降は引数の型がXMLParserオブジェクトに変更) |
| 対になる関数 | xml_parser_get_option() |
| 呼び出しタイミング | xml_parse()を呼び出す前に設定するのが基本 |
4つの設定可能なオプション(イメージ図)
xml_parser_set_option($parser, オプション定数, 値)
│
┌──────────┼──────────┬──────────────┐
▼ ▼ ▼ ▼
XML_OPTION_ XML_OPTION_ XML_OPTION_ XML_OPTION_
CASE_FOLDING TARGET_ SKIP_TAGSTART SKIP_WHITE
ENCODING
│ │ │ │
▼ ▼ ▼ ▼
要素名を 出力文字列 タグ名の先頭 要素間の空白
大文字に のエンコー 何文字かを 文字のみの
変換するか ディングを スキップする データを
(true/false) 指定する 文字数 無視するか
(true/false)
ポイントは、xml_parser_set_option() で設定できるオプションがこの4種類に限定されているという点です。それぞれが解析結果の見た目や扱いやすさに直接影響するため、用途に応じて適切に組み合わせることが重要です。
実践サンプル7選
例1:基本的な使い方(case foldingの無効化)
<?php
class CaseFoldingDemo
{
public function disableCaseFolding(XMLParser $parser): void
{
// false を指定すると、要素名の大文字変換が行われなくなる
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
}
}
$parser = xml_parser_create();
$demo = new CaseFoldingDemo();
$demo->disableCaseFolding($parser);
$tags = [];
xml_set_element_handler($parser, function ($p, $name) use (&$tags) { $tags[] = $name; }, fn () => null);
xml_parse($parser, '<MyRoot><MyItem/></MyRoot>', true);
xml_parser_free($parser);
print_r($tags); // ['MyRoot', 'MyItem'] (大文字小文字が保持される)
例2:出力エンコーディングを統一するクラス
<?php
class OutputEncodingNormalizer
{
/**
* 入力データのエンコーディングに関わらず、
* ハンドラに渡される文字列を常にUTF-8に統一する
*/
public function createNormalizingParser(?string $inputEncoding = null): XMLParser
{
$parser = xml_parser_create($inputEncoding);
xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
return $parser;
}
}
$normalizer = new OutputEncodingNormalizer();
$parser = $normalizer->createNormalizingParser('ISO-8859-1');
$values = [];
xml_set_character_data_handler($parser, function ($p, $data) use (&$values) {
if (trim($data) !== '') $values[] = $data;
});
xml_parse($parser, "<root>caf\xe9</root>", true);
xml_parser_free($parser);
print_r($values);
例3:要素間の空白を無視して純粋なデータのみ扱うクラス
<?php
class WhitespaceSkippingParser
{
/**
* インデントや改行のためだけの空白文字を、
* データとして扱わないように設定する
*/
public function createCleanParser(): XMLParser
{
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, true);
return $parser;
}
}
$factory = new WhitespaceSkippingParser();
$parser = $factory->createCleanParser();
$dataEvents = 0;
xml_set_character_data_handler($parser, function ($p, $data) use (&$dataEvents) {
$dataEvents++;
});
// インデントや改行が多いXMLでも、余計な空白イベントが発生しにくくなる
xml_parse($parser, "<root>\n <item>\n value\n </item>\n</root>", true);
xml_parser_free($parser);
echo "文字データイベント発生回数: {$dataEvents}" . PHP_EOL;
例4:複数オプションをまとめて設定するファクトリークラス
<?php
class ConfiguredParserFactory
{
/**
* よく使う組み合わせをまとめて設定する
* ファクトリーメソッドパターンの実装
*/
public static function createForCleanDataExtraction(): XMLParser
{
$parser = xml_parser_create('UTF-8');
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, true);
xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
return $parser;
}
}
$parser = ConfiguredParserFactory::createForCleanDataExtraction();
xml_parse($parser, '<Root><Name>太郎</Name></Root>', true);
xml_parser_free($parser);
例5:設定変更が失敗した場合を検知するエラーハンドリング
<?php
class SafeOptionSetter
{
/**
* xml_parser_set_option()の戻り値をチェックし、
* 設定に失敗した場合は例外をスローする
*/
public function setOptionStrict(XMLParser $parser, int $option, mixed $value): void
{
if (!xml_parser_set_option($parser, $option, $value)) {
throw new RuntimeException("オプション設定に失敗しました: option={$option}");
}
}
}
$parser = xml_parser_create();
$setter = new SafeOptionSetter();
try {
$setter->setOptionStrict($parser, XML_OPTION_CASE_FOLDING, false);
echo '設定に成功しました' . PHP_EOL;
} catch (RuntimeException $e) {
echo 'エラー: ' . $e->getMessage() . PHP_EOL;
} finally {
xml_parser_free($parser);
}
例6:解析開始後にオプションを変更しても反映されないことを確認するデモ
<?php
class LateOptionChangeDemonstrator
{
/**
* xml_parse()の呼び出し後にオプションを変更しても、
* 既に処理済みの部分には影響しないことを確認する
*/
public function demonstrate(): void
{
$parser = xml_parser_create();
$tags = [];
xml_set_element_handler(
$parser,
function ($p, $name) use (&$tags, $parser) {
$tags[] = $name;
// 最初の要素処理後にcase foldingを変更してみる
if (count($tags) === 1) {
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
}
},
fn () => null
);
xml_parse($parser, '<Root><Item/></Root>', true);
xml_parser_free($parser);
print_r($tags);
// 挙動は実装依存のため、必ずしも期待通りにならない可能性がある点に注意
}
}
$demo = new LateOptionChangeDemonstrator();
$demo->demonstrate();
例7:設定オプションをテスト可能な形で注入するクラス設計
<?php
class ConfigurableXmlReader
{
private array $options;
/**
* 設定内容を外部から注入できるようにすることで、
* テスト時に異なる設定パターンを容易に検証できるようにする
*/
public function __construct(array $options = [])
{
$this->options = $options + [
XML_OPTION_CASE_FOLDING => true,
XML_OPTION_SKIP_WHITE => false,
];
}
public function read(string $xml): array
{
$parser = xml_parser_create();
foreach ($this->options as $option => $value) {
xml_parser_set_option($parser, $option, $value);
}
$tags = [];
xml_set_element_handler($parser, function ($p, $name) use (&$tags) { $tags[] = $name; }, fn () => null);
xml_parse($parser, $xml, true);
xml_parser_free($parser);
return $tags;
}
}
$reader = new ConfigurableXmlReader([XML_OPTION_CASE_FOLDING => false]);
print_r($reader->read('<MyRoot><MyItem/></MyRoot>'));
関連関数との比較
| 関数 | 役割 | xml_parser_set_optionとの違い |
|---|---|---|
xml_parser_set_option() | パーサーのオプション設定を変更する | 本記事の対象。設定を書き込む側の関数 |
xml_parser_get_option() | パーサーの現在のオプション設定を取得する | 設定を読み出すだけの、対になる関数 |
xml_parser_create() | パーサーインスタンスを生成する | 生成直後はデフォルト設定になっており、変更にはこの関数を使う |
mb_internal_encoding() | PHP全体の内部文字エンコーディングを設定 | 対象がXMLパーサー個別ではなく、PHP実行環境全体である点が異なる |
ini_set() | php.ini相当の設定を実行時に変更 | より広範なPHP設定を対象とし、XMLパーサー専用のオプションではない |
よくある落とし穴(注意点)
xml_parse()の呼び出し後に設定しても意味がない場合がある 基本的には、解析処理を開始する前にすべてのオプションを設定しておくのが安全です。解析途中でオプションを変更した場合の挙動は保証されないため、避けるべきです(例6を参照)。- 設定可能なオプションは4種類に限定されている
XML_OPTION_CASE_FOLDING,XML_OPTION_TARGET_ENCODING,XML_OPTION_SKIP_TAGSTART,XML_OPTION_SKIP_WHITE以外の定数を指定すると、正しく動作しません。 XML_OPTION_TARGET_ENCODINGに対応していないエンコーディング名を指定する サポートされていないエンコーディング名を指定すると、設定に失敗したり、意図しない文字化けが発生したりすることがあります。事前に対応エンコーディングを確認しましょう。- 戻り値のチェックを省略する
xml_parser_set_option()の戻り値(成功/失敗)を確認せずに処理を進めると、意図した設定が反映されないまま解析が進んでしまう可能性があります(例5を参照)。 XML_OPTION_SKIP_TAGSTARTの意味を誤解する このオプションは「タグ名の先頭何文字をスキップするか」を指定するもので、真偽値ではなく整数を指定します。他の真偽値系オプションと混同しないよう注意しましょう。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする関数か | XMLパーサーインスタンスのオプション設定を変更する |
| 主な用途 | 要素名の大文字小文字変換の制御、出力エンコーディングの統一、空白データの無視 |
| 設定可能なオプション | CASE_FOLDING, TARGET_ENCODING, SKIP_TAGSTART, SKIP_WHITEの4種類 |
| 対になる関数 | xml_parser_get_option()(設定を確認する) |
| 注意点 | 解析開始前に設定すること、対応するオプションの範囲、戻り値チェックの徹底 |
xml_parser_set_option() は、ExpatベースのXMLパーサーのデフォルトの挙動を、実際のプロジェクト要件に合わせて調整するための重要な関数です。これまでの記事で紹介してきたcase foldingやエンコーディング調整の実例を通じて、この関数がXML処理の細部を制御する上でいかに実用的かを理解いただけたのではないでしょうか。
