CIIIXML パーサ関数

導入

XML について

XML (eXtensible Markup Language) は、Web における構造化された ドキュメント交換用のデータフォーマットです。XML は、World Wide Web consortium (W3C) で規定された規格です。XML に関する情報およ び関連する技術は、http://www.w3.org/XML/ で参照することができます。

インストール

この拡張機能は、expat を使用します。これは、 http://www.jclark.com/xml/にあります。expatに 付属のMakefileは、デフォルトでライブラリを構築しません。これを行 うmakeルールを次のように指定できます。
libexpat.a: $(OBJS)
    ar -rc $@ $(OBJS)
    ranlib $@
expat のソース RPM パッケージが http://sourceforge.net/projects/expat/ にあります。

Apache-1.3.7 以降を使用している場合、すでに必要なexpatライブラリ はインストールされていることに注意して下さい。(パスを追加設定せず に) --with-xmlを付けてPHPの configureを実行して下さい。これにより Apache に組み込まれたexpat ライブラリが自動的に使用されます。

UNIXでは、--with-xmlオプション を付けてconfigure を実行して下さい。 expatライブラリをコンパイラのパスが通っ た場所にインストールする必要があります。Apache 1.3.9 以降のモジュー ルとして PHP をコンパイルする場合、PHP は自動的に Apache にバン ドルされたexpat ライブラリを使用しま す。expat を通常と異なる場所にインストールした場合は、configure を実行する前に環境変数に CPPFLAGS および LDFLAGSを設定する必要があるかもしれません。

PHP を構築して下さい。そう! 再構築は、当然 必要です。

この拡張機能について

このPHP拡張機能は、James Clark氏の expatのサポートをPHPに付加します。 このツールキットは、XML ドキュメントの構文解析をしますが、 検証は行いません。3種類のソース 文字エンコーディングUS-ASCII, ISO-8859-1 ,UTF-8 がPHPでサポートされます。UTF-16 はサポートさ れません。

この拡張機能は、XML パーサの作成 を行い、異なった XML イベントに関してハンドラ を定義します。各XMLパーサーには、設定可能な小数の パラメータ もあります。

XML イベントハンドラは次のように定義されます。

表 1サポートされる XML ハンドラ

ハンドラ設定用の PHP 関数イベントの説明
xml_set_element_handler() 要素イベントは、XML パーサーが開始または終了タグに出会うたび に発行されます。開始タグと終了タグについて別のハンドラがあり ます。
xml_set_character_data_handler() 文字データは、タグの間の空白を含めて XML ドキュメントにおけ るほぼ全ての非マークアップ部分の内容です。XML パーサーは、 空白を加えたり削除したりしないことに注意して下さい。空白が 意味を有するかどうかを決めるのは、アプリケーション側の責任 です。
xml_set_processing_instruction_handler() PHP プログラマは、既に処理用命令 (PI) に既に慣れているに違 いありません。<?php ?> は処理用命令であり、この場合、 php は "PI ターゲット"と呼ばれます。 これらの処理はアプリケーション依存ですが、全ての PI ターゲッ トが "XML" から始まることだけは、規定されています。
xml_set_default_handler() 別のハンドラでしないことをデフォルトのハンドラで行います。 XML およびドキュメント型の宣言のようなことをデフォルトハンドラで 行います。
xml_set_unparsed_entity_decl_handler() このハンドラは、処理されない (NDATA) エンティティの宣言用に コールされます。
xml_set_notation_decl_handler() このハンドラは、表記の宣言用にコールされます。
xml_set_external_entity_ref_handler() このハンドラは、XML パーサーが外部処理された通常のエンティティ への参照を見つけた際にコールされます。これは、例えば、ファ イルまたは URL への参照とすることが可能です。例としては、 外部エンティティ の例 を参照下さい。

大文字変換(Case Folding)

要素ハンドラ関数は、その要素に大文字小文字を変換する (case-folded)の名前をつけることができます。 大文字変換(case-folding) は、XML標準により "大文字でないものは等 価な大文字に置換される一連の文字に適用されるプロセス" として定義 されています。言い替えると、XML に関しては単に大文字変換は大文字 にすることを意味します。

デフォルトで、ハンドラ関数に渡される全ての要素名は、大文字変換さ れます。この動作は、xml_parser_get_option() およびxml_parser_set_option() 関数でXMLパーサー 毎にそれぞれ問い合わせ、制御することが可能です。

エラーコード

(xml_parse() により返されるものとして) XMLエラーコードとして次のような定数が定義されています。:

XML_ERROR_NONE
XML_ERROR_NO_MEMORY
XML_ERROR_SYNTAX
XML_ERROR_NO_ELEMENTS
XML_ERROR_INVALID_TOKEN
XML_ERROR_UNCLOSED_TOKEN
XML_ERROR_PARTIAL_CHAR
XML_ERROR_TAG_MISMATCH
XML_ERROR_DUPLICATE_ATTRIBUTE
XML_ERROR_JUNK_AFTER_DOC_ELEMENT
XML_ERROR_PARAM_ENTITY_REF
XML_ERROR_UNDEFINED_ENTITY
XML_ERROR_RECURSIVE_ENTITY_REF
XML_ERROR_ASYNC_ENTITY
XML_ERROR_BAD_CHAR_REF
XML_ERROR_BINARY_ENTITY_REF
XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF
XML_ERROR_MISPLACED_XML_PI
XML_ERROR_UNKNOWN_ENCODING
XML_ERROR_INCORRECT_ENCODING
XML_ERROR_UNCLOSED_CDATA_SECTION
XML_ERROR_EXTERNAL_ENTITY_HANDLING

文字エンコーディング

PHPのXML拡張機能は、異なった文字エンコーディング を通じてUnicode 文字セットをサポートします。ソースエンコーディング およびターゲットエンコーディング という2種類の文字エンコーディングがあります。 PHP におけるドキュメントの内部表現は、常に UTF-8でエンコードされます。

ソースエンコーディングは、XMLドキュメントが 構文解析された際に行わ れます。XML パーサの 作成を行う際に、ソースエンコードを指定することができます。 (このエンコーディングは、その XML パーサーが存在する間、後で変更す ることはできません)サポートされるソースエンコーディングは、 ISO-8859-1, US-ASCII , UTF-8 です。前の二つは、シングルバイトエンコー ディングです。これは、各文字がシングルバイトで表現されることを意 味します。UTF-8 は、1から4バイトの可変ビット 数(最大21ビット)で構成された文字をエンコードすることが可能です。 PHP で用いられるデフォルトのソースエンコーディングは、 ISO-8859-1です。

ターゲットエンコーディングは、PHPがデータをXMLハンドラ関数に 渡す時に行われます。あるXMLパーサが作成された際、ターゲットエン コーディングは、ソースエンコーディングと同様に設定されます。 しかし、これは、いつでも変更可能です。ターゲットエンコーディング は、タグ名と同様に文字データに作用し、命令を処理します。

XML パーサがソースエンコーディングが表現できる範囲の外側の文字に 出会った場合、エラーが返されます。

解釈するXMLドキュメントにおいてPHPが文字に出会った際に、選択した ターゲットエンコーディングで表現できない文字に出会った場合、問題 の文字は "降格" されます。現在、このことはこのような文字が疑問符 で置換されることを意味します。

いくつかの例

以下にXMLドキュメントを処理するPHPスクリプトの例をいくつか示しま す。

XML エレメント構造の例

この最初の例は、あるドキュメント中のstart エレメントの構造をイン デントを付けて表示します。

例 1XML エレメント構造を表示

$file = "data.xml";
$depth = array();

function startElement($parser, $name, $attrs){
    global $depth;
    for ($i = 0; $i < $depth[$parser]; $i++) {
        print "  ";
    }
    print "$name\n";
    $depth[$parser]++;
}

function endElement($parser, $name){
    global $depth;
    $depth[$parser]--;
}

$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
if (!($fp = fopen($file, "r"))) {
    die("could not open XML input");
}
while ($data = fread($fp, 4096)) {
    if (!xml_parse($xml_parser, $data, feof($fp))) {
        die(sprintf("XML error: %s at line %d",
                    xml_error_string(xml_get_error_code($xml_parser)),
                    xml_get_current_line_number($xml_parser)));
    }
}
xml_parser_free($xml_parser);

XMLタグのマッピングの例

例 2XMLをHTMLにマップする

この例は、XMLドキュメントのタグを直接HTMLタグにマップします。 "map array" にないエレメントは無視されます。もちろん、この例は、 特定の XML ドキュメント型を有する場合のみ動作します。
$file = "data.xml";
$map_array = array(
    "BOLD"     => "B",
    "EMPHASIS" => "I",
    "LITERAL"  => "TT"
);

function startElement($parser, $name, $attrs){
    global $map_array;
    if ($htmltag = $map_array[$name]) {
        print "<$htmltag>";
    }
}

 function endElement($parser, $name){
    global $map_array;
    if ($htmltag = $map_array[$name]) {
        print "</$htmltag>";
    }
}

function characterData($parser, $data){
    print $data;
}

$xml_parser = xml_parser_create();
// $map_array の中のタグをみつけられるように大文字変換を行う
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
if (!($fp = fopen($file, "r"))) {
    die("could not open XML input");
}
while ($data = fread($fp, 4096)) {
    if (!xml_parse($xml_parser, $data, feof($fp))) {
        die(sprintf("XML error: %s at line %d",
                    xml_error_string(xml_get_error_code($xml_parser)),
                    xml_get_current_line_number($xml_parser)));
    }
}
xml_parser_free($xml_parser);

XML 外部エンティティの例

この例は、XML コードに焦点を当てます。この例は、他のドキュメント をインクルードし処理するための外部エンティティリファレンスのハン ドラの使用法およびPIの処理方法、PIが含むコードに関する"信頼度" を定義する手段を説明します。

この例で使用される XML ドキュメントは、例題ファイル (xmltest.xml および xmltest2.xml) にあります。

例 3外部エンティティの例

$file = "xmltest.xml";

function trustedFile($file){
    // 自己所有のローカルファイルのみを信頼する
    if (!eregi("^([a-z]+)://", $file) 
        && fileowner($file) == getmyuid()) {
        return true;
    }
    return false;
}

function startElement($parser, $name, $attribs){
    print "&lt;<font color=\"#0000cc\">$name</font>";
    if (sizeof($attribs)) {
        while (list($k, $v) = each($attribs)) {
            print " <font color=\"#009900\">$k</font>=\"<font
	           color=\"#990000\">$v</font>\"";
        }
    }
    print "&gt;";
}

function endElement($parser, $name){
    print "&lt;/<font color=\"#0000cc\">$name</font>&gt;";
}

function characterData($parser, $data){
    print "<b>$data</b>";
}

function PIHandler($parser, $target, $data){
    switch (strtolower($target)) {
        case "php":
            global $parser_file;
            // 処理されるドキュメントが "信頼されている" 場合、
	    // PHP コードをその内部で実行します。
	    // そうでない場合、そのコードが代わりに表示されます。
            if (trustedFile($parser_file[$parser])) {
                eval($data);
            } else {
                printf("Untrusted PHP code: <i>%s</i>", 
                        htmlspecialchars($data));
            }
            break;
    }
}

function defaultHandler($parser, $data){
    if (substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") {
        printf('<font color="#aa00aa">%s</font>', 
                htmlspecialchars($data));
    } else {
        printf('<font size="-1">%s</font>', 
                htmlspecialchars($data));
    }
}

function externalEntityRefHandler($parser, $openEntityNames, $base, $systemId,
                                  $publicId){
    if ($systemId) {
        if (!list($parser, $fp) = new_xml_parser($systemId)) {
            printf("Could not open entity %s at %s\n", $openEntityNames,
                   $systemId);
            return false;
        }
        while ($data = fread($fp, 4096)) {
            if (!xml_parse($parser, $data, feof($fp))) {
                printf("XML error: %s at line %d while parsing entity %s\n",
                       xml_error_string(xml_get_error_code($parser)),
                       xml_get_current_line_number($parser), $openEntityNames);
                xml_parser_free($parser);
                return false;
            }
        }
        xml_parser_free($parser);
        return true;
    }
    return false;
}

function new_xml_parser($file) {
    global $parser_file;

    $xml_parser = xml_parser_create();
    xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 1);
    xml_set_element_handler($xml_parser, "startElement", "endElement");
    xml_set_character_data_handler($xml_parser, "characterData");
    xml_set_processing_instruction_handler($xml_parser, "PIHandler");
    xml_set_default_handler($xml_parser, "defaultHandler");
    xml_set_external_entity_ref_handler($xml_parser, "externalEntityRefHandler");
    
    if (!($fp = @fopen($file, "r"))) {
        return false;
    }
    if (!is_array($parser_file)) {
        settype($parser_file, "array");
    }
    $parser_file[$xml_parser] = $file;
    return array($xml_parser, $fp);
}

if (!(list($xml_parser, $fp) = new_xml_parser($file))) {
    die("could not open XML input");
}

print "<pre>";
while ($data = fread($fp, 4096)) {
    if (!xml_parse($xml_parser, $data, feof($fp))) {
        die(sprintf("XML error: %s at line %d\n",
                    xml_error_string(xml_get_error_code($xml_parser)),
                    xml_get_current_line_number($xml_parser)));
    }
}
print "</pre>";
print "parse complete\n";
xml_parser_free($xml_parser);

?>

例 4xmltest.xml

<?xml version='1.0'?>
<!DOCTYPE chapter SYSTEM "/just/a/test.dtd" [
<!ENTITY plainEntity "FOO entity">
<!ENTITY systemEntity SYSTEM "xmltest2.xml">
]>
<chapter>
 <TITLE>Title &plainEntity;</TITLE>
 <para>
  <informaltable>
   <tgroup cols="3">
    <tbody>
     <row><entry>a1</entry><entry morerows="1">b1</entry><entry>c1</entry></row>
     <row><entry>a2</entry><entry>c2</entry></row>
     <row><entry>a3</entry><entry>b3</entry><entry>c3</entry></row>
    </tbody>
   </tgroup>
  </informaltable>
 </para>
 &systemEntity;
 <section id="about">
  <title>About this Document</title>
  <para>
   <!-- this is a comment -->
   <?php print 'Hi!  This is PHP version '.phpversion(); ?>
  </para>
 </section>
</chapter>

This file is included from xmltest.xml:

例 5xmltest2.xml

<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY testEnt "test entity">
]>
<foo>
   <element attrib="value"/>
   &testEnt;
   <?php print "This is some more PHP code being executed."; ?>
</foo>

目次
utf8_decode --  UTF-8 エンコードされたISO-8859-1文字列をシングルバイトの ISO-8859-1 に変換する
utf8_encode -- ISO-8859-1 文字列を UTF-8 にエンコードする
xml_error_string -- XML パーサのエラー文字列を得る
xml_get_current_byte_index -- カレントの XML パーサのバイトインデックスを得る
xml_get_current_column_number -- XML パーサのカレントのカラム番号を得る
xml_get_current_line_number -- XML パーサのカレントの行番号を得る
xml_get_error_code -- XML パーサのエラーコードを得る
xml_parse -- XML ドキュメントの処理を開始する
xml_parse_into_struct -- 配列構造体にXMLデータを処理する
xml_parser_create -- XML パーサを作成する
xml_parser_create_ns -- XMLパーサを生成する
xml_parser_free -- XML パーサを解放する
xml_parser_get_option -- XML パーサからオプションを得る
xml_parser_set_option -- XML パーサのオプションを設定する
xml_set_character_data_handler -- 文字データハンドラを設定する
xml_set_default_handler -- デフォルトのハンドラを設定する
xml_set_element_handler -- start および end 要素のハンドラを設定する
xml_set_end_namespace_decl_handler -- 名前空間終了ハンドラを設定する
xml_set_external_entity_ref_handler -- 外部リファレンスハンドラを設定する
xml_set_notation_decl_handler -- 表記法宣言ハンドラを設定する
xml_set_object -- オブジェクト内部でXMLパーサを使用する
xml_set_processing_instruction_handler --  処理命令 (PI) 用ハンドラを設定する
xml_set_start_namespace_decl_handler -- 名前空間開始ハンドラを設定する
xml_set_unparsed_entity_decl_handler --  処理されないエンティティ宣言用ハンドラを設定する