xml|関数
PHP の XML 解析関数 まず第一に、私はコンピューター標準が好きであることを認めなければなりません。もし誰もが業界の標準に従えば、インターネットはより良いメディアになるでしょう。標準化されたデータ交換フォーマットの使用により、オープンでプラットフォームに依存しないコンピューティング モデルが実現可能になります。それが私が XML 愛好家である理由です。 幸いなことに、私のお気に入りのスクリプト言語は XML をサポートしているだけでなく、XML のサポートも強化されています。 PHP を使用すると、XML ドキュメントをインターネットにすばやく公開したり、XML ドキュメントに関する統計情報を収集したり、XML ドキュメントを他の形式に変換したりすることができます。たとえば、私は XML で書いた記事や書籍を管理するために、PHP の XML 処理機能をよく使用します。 この記事では、PHP の組み込み Expat パーサーを使用して XML ドキュメントを処理する方法について説明します。例を通して、Expat の処理方法を説明します。同時に、サンプルは次の方法を示します: 独自の処理関数を構築する XML ドキュメントを独自の PHP データ構造に変換する Expat を導入する XML パーサー (XML プロセッサーとも呼ばれる) は、プログラムにアクセスできる構造とXML ドキュメントのコンテンツ。 Expat は、PHP スクリプト言語用の XML パーサーです。 Mozilla、Apache、Perl などの他のプロジェクトでも使用されます。 イベントベースのパーサーとは何ですか? XML パーサーの 2 つの基本的なタイプ: ツリーベースのパーサー: XML ドキュメントをツリー構造に変換します。このタイプのパーサーは、結果のツリーの各要素にアクセスするための API を提供しながら、記事全体を解析します。その共通規格は DOM (Document Object Mode) です。 イベントベースのパーサー: XML ドキュメントを一連のイベントとして扱います。特別なイベントが発生すると、パーサーは開発者が提供する関数を呼び出してそれを処理します。 イベントベースのパーサーは、XML ドキュメントのデータ中心のビューを備えています。これは、XML ドキュメントの構造ではなくデータ部分に焦点を当てていることを意味します。これらのパーサーはドキュメントを最初から最後まで処理し、コールバック関数を通じて要素の開始、要素の終了、特徴データの開始などのイベントをアプリケーションに報告します。以下は、「Hello-World」の XML ドキュメントの例です。 Hello World イベントベースのパーサーは、次の 3 つのイベントとしてレポートします。 CDATA 値を持つ項目の開始: Hello World 終了要素: 挨拶 ツリーベースのパーサーとは異なり、イベントベースのパーサーはドキュメントを記述する構造を生成しません。 CDATA アイテムでは、イベントベースのパーサーでは親要素の挨拶情報を取得できません。 ただし、より低いレベルのアクセスが提供されるため、リソースの有効活用とより高速なアクセスが可能になります。この方法では、ドキュメント全体をメモリに収める必要がなく、実際にはドキュメント全体が実際のメモリ値よりも大きくなる場合もあります。 Expat は、そのようなイベントベースのパーサーです。もちろん、Expat を使用する場合は、必要に応じて PHP で完全なネイティブ ツリー構造を生成することもできます。 上記の Hello-World の例には、完全な XML 形式が含まれています。ただし、関連付けられた DTD (Document Type Definition) も埋め込まれた DTD も存在しないため、これは無効です。 Expat の場合、これは何の違いもありません。Expat は有効性をチェックしないパーサーであるため、ドキュメントに関連付けられた DTD を無視します。ただし、ドキュメントを完全にフォーマットする必要があることに注意してください。そうでないと、Expat (他の XML 準拠パーサーと同様) がエラー メッセージを表示して停止します。 有効性をチェックしないパーサーとして、Exapt は速度と軽量であるため、インターネット プログラムに非常に適しています。 Expat のコンパイル Expat は PHP3.0.6 バージョン (またはそれ以降) にコンパイルできます。 Apache 1.3.9 以降、Expat は Apache の一部として含まれています。 Unix システムでは、-with-xml オプションを使用して PHP を構成することで、PHP にコンパイルできます。 PHP を Apache モジュールとしてコンパイルする場合、Expat はデフォルトで Apache の一部として含まれます。 Windows では、XML ダイナミック リンク ライブラリをロードする必要があります。 XML 例: XMLstats Expat の機能を理解する 1 つの方法は、例を通して見ることです。これから説明する例では、Expat を使用して XML ドキュメントの統計を収集します。 ドキュメント内の各要素について、以下の情報が出力されます: ドキュメント内で要素が使用された回数 要素内の文字データの数 要素の親要素 子要素要素の親要素と子要素を保存するための構造体を生成するために PHP を使用します 準備 XML パーサー インスタンスの生成に使用される関数は、xml_parser_create() です。このインスタンスは、今後のすべての関数に使用されます。この考え方は、PHP の MySQL 関数の接続タグに非常に似ています。通常、イベントベースのパーサーでは、ドキュメントを解析する前に、特定のイベントが発生したときに呼び出されるコールバック関数を登録する必要があります。 Expat には例外イベントはなく、次の 7 つの可能なイベントが定義されています: オブジェクト XML 解析関数 説明 要素 xml_set_element_handler() 要素の開始と終了 文字データ 処理命令 xml_set_processing_instruction_handler() 処理命令の発生 表記法宣言の発生 xml_set_notation_decl_handler() 表記法宣言の発生 デフォルト xml_set_default_handler() 指定されていないその他のイベントハンドラー すべてのコールバック関数は、最初のパラメーターとしてパーサーのインスタンスを取る必要があります (他のパラメーターもあります)。 サンプル スクリプトについては、この記事の最後にあります。注意が必要なのは、要素処理関数と文字データ処理関数の両方を使用していることです。要素のコールバック ハンドラー関数は、xml_set_element_handler() を通じて登録されます。 この関数には 3 つのパラメータが必要です: パーサーのインスタンス 開始要素を処理するコールバック関数の名前 終了要素を処理するコールバック関数の名前 コールバック関数は、解析を開始するときに存在する必要がありますXML ドキュメント。これらは、PHP マニュアルに記載されているプロトタイプと一致して定義する必要があります。 たとえば、Expat は開始要素のハンドラー関数に 3 つのパラメーターを渡します。スクリプト例では、次のように定義されています: function start_element($parser, $name, $attrs) 最初のパラメータはパーサー識別子、2 番目のパラメータは開始要素の名前、 3 番目のパラメータは、要素のすべての属性と値を含む配列です。 XML ドキュメントの解析を開始すると、Expat は開始要素に遭遇するたびに start_element() 関数を呼び出してパラメーターを渡します。 XML 大文字と小文字の折り畳みオプション xml_parser_set_option() 関数を使用して、大文字と小文字の折り畳みオプションをオフにします。このオプションはデフォルトでオンになっており、ハンドラー関数に渡される要素名が自動的に大文字に変換されます。ただし、XML では大文字と小文字が区別されます (したがって、統計 XML ドキュメントでは大文字と小文字が非常に重要です)。この例では、ケース折りたたみオプションをオフにする必要があります。 ドキュメントを解析する すべての準備作業が完了したら、スクリプトは最終的に XML ドキュメントを解析できるようになります: カスタム関数 Xml_parse_from_file() は、パラメータで指定されたファイルを開き、サイズ 4kb で解析します xml_parse() は、xml_parse_from_file() と同様、エラーが発生した場合、つまり XML ドキュメントが完全にフォーマットされていない場合に false を返します。 xml_get_error_code() 関数を使用して、最後のエラーの数値コードを取得できます。この数値コードを xml_error_string() 関数に渡して、エラー テキスト情報を取得します。 XML の現在の行番号を出力し、デバッグを容易にします。 解析プロセス中に、コールバック関数が呼び出されます。 文書構造を説明する 文書を解析するとき、Expat が強調する必要がある問題は、「文書構造の基本的な説明をどのように維持するか?」ということです。 前に述べたように、イベントベースのパーサー自体は構造情報を生成しません。 ただし、タグ構造は XML の重要な機能です。たとえば、要素シーケンス は、 とは異なる意味を持ちます。そうは言っても、たとえどちらも「タイトル」という用語を使用していても、本のタイトルと絵のタイトルは互いに何の関係もないと、著者なら誰でも言うでしょう。したがって、イベントベースのパーサーで XML を効率的に処理するには、独自のスタックまたはリストを使用してドキュメントに関する構造情報を維持する必要があります。 ドキュメント構造のミラーを作成するには、スクリプトは少なくとも現在の要素の親要素を知っている必要があります。これは、Exapt の API では不可能であり、コンテキスト情報なしで現在の要素のイベントのみを報告します。したがって、独自のスタック構造を構築する必要があります。 このスクリプトの例では、先入れ後出し (FILO) スタック構造を使用します。配列を通じて、スタックはすべての開始要素を保存します。開始要素処理関数の場合、現在の要素は array_push() 関数によってスタックの先頭にプッシュされます。同様に、終了要素処理関数は、array_pop() を通じて先頭要素を削除します。 シーケンス の場合、スタックは次のように埋められます: 開始要素 book: スタックの最初の要素に "book" を割り当てます ( $スタック[0])。 開始要素 title: スタックの先頭 ($stack[1]) に「title」を割り当てます。 要素タイトルの終了: スタック ($stack[1]) から最上位の要素を削除します。 要素タイトルの終了: スタック ($stack[0]) から最上位の要素を削除します。 PHP3.0 は、$ Depth 変数を通じて要素のネストを手動で制御することにより、この例を実装します。これにより、スクリプトがより複雑に見えます。 PHP4.0 では、array_pop() 関数と array_push() 関数を使用して、スクリプトをより簡潔に見せます。 データを収集する 各要素に関する情報を収集するには、スクリプトは各要素のイベントを記憶する必要があります。グローバル配列変数 $elements を使用して、ドキュメント内のさまざまな要素をすべて保存します。配列の項目は要素クラスのインスタンスであり、4 つの属性 (クラスの変数) を持ちます。 $count - 要素がドキュメント内で見つかった回数 $chars - ドキュメント内の文字イベントのバイト数要素 $parents - 親要素 $childs - 子要素 ご覧のとおり、クラス インスタンスを配列に保存するのは簡単です。 注: PHP の機能の 1 つは、対応する配列全体を走査するのと同じように、while(list() = each()) ループを通じてクラス構造全体を走査できることです。すべてのクラス変数 (および PHP3.0 を使用する場合はメソッド名) は文字列として出力されます。 要素が見つかったら、ドキュメント内でその要素が何回出現したかを追跡するために、対応するカウンターをインクリメントする必要があります。対応する $elements 項目の count 要素も 1 つ増加します。 また、現在の要素がその子要素であることを親要素に知らせる必要があります。したがって、現在の要素の名前は、親要素の $childs 配列内の項目に追加されます。最後に、現在の要素はその親が誰であるかを記憶する必要があります。したがって、親要素は、現在の要素の $parents 配列内の項目に追加されます。 統計の表示 残りのコードは、$elements 配列とそのサブ配列をループして、統計結果を表示します。これは最も単純なネストされたループですが、正しい結果が出力されますが、コードは簡潔でも特別なスキルもありません。これは、作業を完了するために毎日使用できる単なるループです。 このスクリプトの例は、PHP の CGI モードのコマンド ラインを通じて呼び出されるように設計されています。そのため、統計結果の出力形式はテキスト形式となります。スクリプトをインターネット上で使用する場合は、出力関数を変更して HTML 形式を生成する必要があります。 概要 Exapt は、PHP 用の XML パーサーです。イベントベースのパーサーとして、文書の構造的な記述は生成されません。ただし、低レベルのアクセスを提供することにより、リソースの利用効率が向上し、アクセスが高速化されます。 有効性をチェックしないパーサーとして、Expat は XML 文書に関連付けられた DTD を無視しますが、文書の形式が不完全な場合は、エラー メッセージを表示して停止します。 ドキュメントを処理するためのイベント処理関数を提供します スタックやツリーなどの独自のイベント構造を構築して、XML 構造情報マークアップの利点を活用します。 新しい XML プログラムが毎日登場し、PHP の XML サポートは常に強化されています (たとえば、DOM ベースの XML パーサー LibXML のサポートが追加されました)。 PHP と Expat を使用すると、有効かつオープンでプラットフォームに依存しない今後の標準に備えることができます。 例 /*************************************************** * ************************* * 名前: XML 解析の例: XML 文書情報統計 * 説明 * この例では、PHP の Expat パーサー Collect を使用しますXML ドキュメント情報 (例: 各要素、親要素、子要素の出現数) * パラメータとしての XML ファイル。/xmlstats_PHP4.php3 test.xml * $Requires: Expat Requirements: Expat PHP4.0 コンパイル済みCGI モードへ ********************************************** ******** *******************************/ // 最初のパラメータは XML ファイルです $file = $argv[1]; $elements = $ stack = array(); $total_elements = $total_chars = 0; //要素の基本クラス var $count = 0; var $parents = array() ; var $childs = array(); } // XML ファイルを解析する関数 function xml_parse_from_file($parser, $file) { if(!file_exists($file)) { die("ファイル "$file" が見つかりません。"); } if(!($fp = @fopen($file, "r"))) { die("ファイル "$file" を開けません."); } while($data = fread($fp, 4096)) { if(!xml_parse($parser, $data, feof($fp))) { return(false); } } fclose($fp); return(true); } // 出力結果関数(ボックス形式) function print_box($title, $value) { printf("n+ % '-60s+n", ""); printf("|%20s", "$title:"); printf("%14s", $value); printf("%26s|n", " "); printf("+%'-60s+n", ""); } // 出力結果関数(行形式) function print_line($title, $value) { printf("%20s", "$title:"); printf("%15sn", $value) } // 並べ替え関数 function my_sort($a, $b) { return(is_object) ($a) && is_object($b) ? $b->count - $a->count: 0); function start_element($parser, $name, $attrs) { global $elements , $stack; // 要素は既にグローバル $elements 配列にありますか? if(!isset($elements[$name])) { // いいえ - 要素のクラス インスタンスを追加します $element = 新しい要素 } // この要素のカウンターは 1 つ増加します $elements[$name]->count++; // 親要素はありますか? if(isset($stack[count($stack)-1])) { // はい - 親要素を $last_element に割り当てます $last_element = $stack[count($stack)-1]; // 現在の要素の親要素配列が空の場合、0 に初期化します if(!isset($elements[$name]->parents[$last_element])) { $elements[$name]- >parents [$last_element] = 0; } // この要素の親要素カウンターを 1 つ増やします $elements[$name]->parents[$last_element]++;現在の要素の親要素の子要素の配列は空で、0 に初期化されます if(!isset($elements[$last_element]->childs[$name])) { $elements[$last_element]-> ;childs[$name ] = 0; } // 要素の親要素の子要素カウンターに 1 を追加します $elements[$last_element]->childs[$name]++; / / 現在の要素をスタックに追加します array_push($stack, $name); } function stop_element($parser, $name) { global $stack; stack array_pop($stack); } function char_data($parser, $data) { global $elements, $stack, $ Depth // 現在の要素の文字数を増やします $elements; [$stack][ count($stack)-1]]->chars += strlen(trim($data)) } // パーサーのインスタンスを生成します $parser = xml_parser_create(); // 処理関数を設定します xml_set_element_handler($parser, "start_element", "stop_element") xml_set_character_data_handler($parser, "char_data"); xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);ファイルを解析する $ret = xml_parse_from_file($ parser, $file); if(!$ret) { die(sprintf("XML エラー: %s 行 %d", xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser))) ; } // パーサーを解放します xml_parser_free($parser); // ヘルパー要素を解放します unset($elements["current_element"]); "last_element"]); // 要素の数に応じて並べ替えます uasort($elements, "my_sort"); // $elements の要素情報を収集するループ while(list($name, $element) ) = each($elements)) { print_box("要素名", $name); print_line("要素数", $element->count); print_line("文字数", $element-) >chars); printf(" n%20sn", "* 親要素"); // 要素の親をループし、結果を出力します while(list($key, $value) = each ($element->parents)) { print_line($key, $value); } if(count($element->parents) == 0) { printf("%35sn", " [ルート要素]"); } // 要素の子をループして結果を出力 printf("n%20sn", "* Child elements"); while(list($key, $value) ) = each($element->childs)) { print_line($key, $value) } if(count($element->childs) == 0) { printf("%35sn ", "[子供なし]"); } $total_elements += $element->count; $total_chars += $element->chars; } // 最終結果 print_box("Total要素", $total_elements); print_box("合計文字数", $total_chars); ?>