downloads | documentation | faq | getting help | mailing lists | licenses | wiki | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

utf8_decode> <xdiff_string_patch
[edit] Last updated: Mon, 01 Nov 2010

view this page in

CLXXI. XML パーサ関数

導入

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

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

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

要件

このエクステンションは、expat を使用します。 これは、http://www.jclark.com/xml/expat.htmlにあります。 expatに付属のMakefileは、デフォルトでライブラリを構築しません。こ れを行うmakeルールを次のように指定できます。

libexpat.a: $(OBJS)
    ar -rc $@ $(OBJS)
    ranlib $@
expat のソース RPM パッケージが http://sourceforge.net/projects/expat/ にあります。

インストール手順

付属しているexpatライブラリを用いて以下の関数はデフォルトで有効となっ ています。 --disable-xmlを指定してXMLサポート を無効にすることができます。Apache 1.3.9以降でモジュールとしてPHPを コンパイルする場合、PHPは、Apacheから自動的に付属する expatライブラリを使用します。 付属するexpatライブラリを使用したくない場合には、 --with-expat-dir=DIRを指定してPHP のconfigureを実行してください。ただし、DIRは、expatをインストールした ベースディレクトリです。

Windows 版の PHP には この拡張モジュールのサポートが組み込まれています。これらの関数を使用 するために拡張モジュールを追加でロードする必要はありません。

実行時設定

設定ディレクティブは定義されていません。

リソース型

xml

xml_parser_create()および xml_parser_create_ns() により返された xmlリソースは、 このエクステンションにより提供された関数で使用される XMLパーサのインスタンスを参照します。

定義済み定数

以下の定数が定義されています。 この関数の拡張モジュールが PHP 組み込みでコンパイルされているか、 実行時に動的にロードされている場合のみ使用可能です。

XML_ERROR_NONE (integer)

XML_ERROR_NO_MEMORY (integer)

XML_ERROR_SYNTAX (integer)

XML_ERROR_NO_ELEMENTS (integer)

XML_ERROR_INVALID_TOKEN (integer)

XML_ERROR_UNCLOSED_TOKEN (integer)

XML_ERROR_PARTIAL_CHAR (integer)

XML_ERROR_TAG_MISMATCH (integer)

XML_ERROR_DUPLICATE_ATTRIBUTE (integer)

XML_ERROR_JUNK_AFTER_DOC_ELEMENT (integer)

XML_ERROR_PARAM_ENTITY_REF (integer)

XML_ERROR_UNDEFINED_ENTITY (integer)

XML_ERROR_RECURSIVE_ENTITY_REF (integer)

XML_ERROR_ASYNC_ENTITY (integer)

XML_ERROR_BAD_CHAR_REF (integer)

XML_ERROR_BINARY_ENTITY_REF (integer)

XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF (integer)

XML_ERROR_MISPLACED_XML_PI (integer)

XML_ERROR_UNKNOWN_ENCODING (integer)

XML_ERROR_INCORRECT_ENCODING (integer)

XML_ERROR_UNCLOSED_CDATA_SECTION (integer)

XML_ERROR_EXTERNAL_ENTITY_HANDLING (integer)

XML_OPTION_CASE_FOLDING (integer)

XML_OPTION_TARGET_ENCODING (integer)

XML_OPTION_SKIP_TAGSTART (integer)

XML_OPTION_SKIP_WHITE (integer)

イベントハンドラ

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 エレメントの構造をイン デントを付けて表示します。

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

<?php
$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(
"XML 入力をオープンできませんでした");
}
while (
$data = fread($fp, 4096)) {
    if (!
xml_parse($xml_parser, $data, feof($fp))) {
        die(
sprintf("XML エラー: %s が %d 行目で発生しました",
                   
xml_error_string(xml_get_error_code($xml_parser)),
                   
xml_get_current_line_number($xml_parser)));
    }
}
xml_parser_free($xml_parser);
?>

XMLタグのマッピングの例

例 2. XMLをHTMLにマップする

この例は、XMLドキュメントのタグを直接HTMLタグにマップします。 "map array" にないエレメントは無視されます。もちろん、この例は、 特定の XML ドキュメント型を有する場合のみ動作します。

<?php
$file
= "data.xml";
$map_array = array(
   
"BOLD"     => "B",
   
"EMPHASIS" => "I",
   
"LITERAL"  => "TT"
);

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

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

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

$xml_parser = xml_parser_create();
// case-folding を用いることで、$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(
"XML 入力をオープンできませんでした");
}

while (
$data = fread($fp, 4096)) {
    if (!
xml_parse($xml_parser, $data, feof($fp))) {
        die(
sprintf("XML エラー: %s が %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. 外部エンティティの例

<?php
$file
= "xmltest.xml";

function
trustedFile($file)
{
   
// 信頼できるのは、自分自身が所有しているローカルファイルのみです
   
if (!eregi("^([a-z]+)://", $file)
        &&
fileowner($file) == getmyuid()) {
            return
true;
    }
    return
false;
}

function
startElement($parser, $name, $attribs)
{
    echo
"&lt;<font color=\"#0000cc\">$name</font>";
    if (
count($attribs)) {
        foreach (
$attribs as $k => $v) {
            echo
" <font color=\"#009900\">$k</font>=\"<font
                   color=\"#990000\">$v</font>\""
;
        }
    }
    echo
"&gt;";
}

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

function
characterData($parser, $data)
{
    echo
"<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("信頼できない PHP コード: <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("エンティティ %s (%s にある) をオープンできませんでした\n", $openEntityNames,
                  
$systemId);
            return
false;
        }
        while (
$data = fread($fp, 4096)) {
            if (!
xml_parse($parser, $data, feof($fp))) {
               
printf("XML エラー: %s が、%d 行目でエンティティ %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(
"XML 入力をオープンできませんでした");
}

echo
"<pre>";
while (
$data = fread($fp, 4096)) {
    if (!
xml_parse($xml_parser, $data, feof($fp))) {
        die(
sprintf("XML エラー: %s が %d 行目で発生しました\n",
                   
xml_error_string(xml_get_error_code($xml_parser)),
                   
xml_get_current_line_number($xml_parser)));
    }
}
echo
"</pre>";
echo
"パースが完了しました\n";
xml_parser_free($xml_parser);

?>

例 4. xmltest.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>

このファイルは、xmltest.xml からインクルードされます。

例 5. xmltest2.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_into_struct -- 配列構造体に XML データを処理する
xml_parse -- XML ドキュメントの処理を開始する
xml_parser_create_ns -- 名前空間をサポートした XML パーサを生成する
xml_parser_create -- 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 -- 開始要素および終了要素のハンドラを設定する
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 --  処理されないエンティティ宣言用ハンドラを設定する


utf8_decode> <xdiff_string_patch
[edit] Last updated: Mon, 01 Nov 2010
 
add a note add a note User Contributed Notes XML パーサ関数
Glen at ITIStudios dot ca 02-Dec-2011 03:37
<?php
/**
 *  XML to Associative Array Class
 *
 *  Usage:
 *     $domObj = new xmlToArrayParser($xml);
 *     $domArr = $domObj->array;
 *    
 *     if($domObj->parse_error) echo $domObj->get_xml_error();
 *     else print_r($domArr);
 *
 *     On Success:
 *     eg. $domArr['top']['element2']['attrib']['var2'] => val2
 *
 *     On Error:
 *     eg. Error Code [76] "Mismatched tag", at char 58 on line 3
 */

/**
 * Convert an xml file or string to an associative array (including the tag attributes):
 * $domObj = new xmlToArrayParser($xml);
 * $elemVal = $domObj->array['element']
 * Or:  $domArr=$domObj->array;  $elemVal = $domArr['element'].
 *
 * @version  2.0
 * @param Str $xml file/string.
 */
class xmlToArrayParser {
 
/** The array created by the parser can be assigned to any variable: $anyVarArr = $domObj->array.*/
 
public  $array = array();
  public 
$parse_error = false;
  private
$parser;
  private
$pointer;
 
 
/** Constructor: $domObj = new xmlToArrayParser($xml); */
 
public function __construct($xml) {
   
$this->pointer =& $this->array;
   
$this->parser = xml_parser_create("UTF-8");
   
xml_set_object($this->parser, $this);
   
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
   
xml_set_element_handler($this->parser, "tag_open", "tag_close");
   
xml_set_character_data_handler($this->parser, "cdata");
   
$this->parse_error = xml_parse($this->parser, ltrim($xml))? false : true;
  }
 
 
/** Free the parser. */
 
public function __destruct() { xml_parser_free($this->parser);}

 
/** Get the xml error if an an error in the xml file occured during parsing. */
 
public function get_xml_error() {
    if(
$this->parse_error) {
     
$errCode = xml_get_error_code ($this->parser);
     
$thisError "Error Code [". $errCode ."] \"<strong style='color:red;'>" . xml_error_string($errCode)."</strong>\",
                            at char "
.xml_get_current_column_number($this->parser) . "
                            on line "
.xml_get_current_line_number($this->parser)."";
    }else
$thisError = $this->parse_error;
    return
$thisError;
  }
 
  private function
tag_open($parser, $tag, $attributes) {
   
$this->convert_to_array($tag, 'attrib');
   
$idx=$this->convert_to_array($tag, 'cdata');
    if(isset(
$idx)) {
     
$this->pointer[$tag][$idx] = Array('@idx' => $idx,'@parent' => &$this->pointer);
     
$this->pointer =& $this->pointer[$tag][$idx];
    }else {
     
$this->pointer[$tag] = Array('@parent' => &$this->pointer);
     
$this->pointer =& $this->pointer[$tag];
    }
    if (!empty(
$attributes)) { $this->pointer['attrib'] = $attributes; }
  }

 
/** Adds the current elements content to the current pointer[cdata] array. */
 
private function cdata($parser, $cdata) { $this->pointer['cdata'] = trim($cdata); }

  private function
tag_close($parser, $tag) {
   
$current = & $this->pointer;
    if(isset(
$this->pointer['@idx'])) {unset($current['@idx']);}
   
   
$this->pointer = & $this->pointer['@parent'];
    unset(
$current['@parent']);
   
    if(isset(
$current['cdata']) && count($current) == 1) { $current = $current['cdata'];}
    else if(empty(
$current['cdata'])) {unset($current['cdata']);}
  }
 
 
/** Converts a single element item into array(element[0]) if a second element of the same name is encountered. */
 
private function convert_to_array($tag, $item) {
    if(isset(
$this->pointer[$tag][$item])) {
     
$content = $this->pointer[$tag];
     
$this->pointer[$tag] = array((0) => $content);
     
$idx = 1;
    }else if (isset(
$this->pointer[$tag])) {
     
$idx = count($this->pointer[$tag]);
      if(!isset(
$this->pointer[$tag][0])) {
        foreach (
$this->pointer[$tag] as $key => $value) {
            unset(
$this->pointer[$tag][$key]);
           
$this->pointer[$tag][0][$key] = $value;
    }}}else
$idx = null;
    return
$idx;
  }
}
?>

This is supplimental information for the "class xmlToArrayParser".
This is a fully functional error free, extensively tested php class unlike the posts that follow it.

Key phrase: Fully functional, fully tested, error free XML To Array parser.

<?php
/**
 * class xmlToArrayParser
 *
  Notes:
  1. 'attrib' and 'cdata' are keys added to the array when the element contains both attributes and content.
  2. Ignores content that is not in between it's own set of tags.
  3. Don't know if it recognizes processing instructions nor do I know about processing instructions.
     <\?some_pi some_attr="some_value"?>  This is the same as a document declaration.
  4. Empty elements are not included unless they have attributes.
  5. Version 2.0, Dec. 2, 2011, added xml error reporting.
 
  Usage:
    $domObj = new xmlToArrayParser($xml);
    $elemVal = $domObj->array['element']
    Or assign the entire array to its own variable:
    $domArr = $domObj->array;
    $elemVal = $domArr['element']
 
  Example:
    $xml = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <top>
      <element1>element content 1</element1>
      <element2 var2="val2" />
      <element3 var3="val3" var4="val4">element content 3</element3>
      <element3 var5="val5">element content 4</element3>
      <element3 var6="val6" />
      <element3>element content 7</element3>
    </top>';
   
    $domObj = new xmlToArrayParser($xml);
    $domArr = $domObj->array;
   
    if($domObj->parse_error) echo $domObj->get_xml_error();
    else print_r($domArr);

    On Success:
    $domArr['top']['element1'] => element content 1
    $domArr['top']['element2']['attrib']['var2'] => val2
    $domArr['top']['element3']['0']['attrib']['var3'] => val3
    $domArr['top']['element3']['0']['attrib']['var4'] => val4
    $domArr['top']['element3']['0']['cdata'] => element content 3
    $domArr['top']['element3']['1']['attrib']['var5'] => val5
    $domArr['top']['element3']['1']['cdata'] => element content 4
    $domArr['top']['element3']['2']['attrib']['var6'] => val6
    $domArr['top']['element3']['3'] => element content 7
   
    On Error:
    Error Code [76] "Mismatched tag", at char 58 on line 3
 *
 */
?>
wolfon.AT-DoG.inbox.ru 28-Jul-2008 03:09
Finally a simple xml => array class.
Functioning like SimpleXML library.

<?php
class xml  {
    private
$parser;
    private
$pointer;
    public
$dom;
   
    public function
__construct($data) {
       
$this->pointer =& $this->dom;
       
$this->parser = xml_parser_create();
       
xml_set_object($this->parser, $this);
       
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
       
xml_set_element_handler($this->parser, "tag_open", "tag_close");
       
xml_set_character_data_handler($this->parser, "cdata");
       
xml_parse($this->parser, $data);
    }
  
    private function
tag_open($parser, $tag, $attributes) {
        if (isset(
$this->pointer[$tag]['@attributes'])) {
           
$content = $this->pointer[$tag];
           
$this->pointer[$tag] = array(0 => $content);
           
$idx = 1;
        } else if (isset(
$this->pointer[$tag]))
           
$idx = count($this->pointer[$tag]);

        if (isset(
$idx)) {
           
$this->pointer[$tag][$idx] = Array(
               
'@idx' => $idx,
               
'@parent' => &$this->pointer);
              
$this->pointer =& $this->pointer[$tag][$idx];
        } else {
           
$this->pointer[$tag] = Array(
               
'@parent' => &$this->pointer);
           
$this->pointer =& $this->pointer[$tag];
        }
        if (!empty(
$attributes))
           
$this->pointer['@attributes'] = $attributes;
    }

    private function
cdata($parser, $cdata) {
          
$this->pointer['@data'] = $cdata;
    }

    private function
tag_close($parser, $tag) {
       
$current = & $this->pointer;
        if (isset(
$this->pointer['@idx']))
            unset(
$current['@idx']);
       
       
$this->pointer = & $this->pointer['@parent'];
       
          unset(
$current['@parent']);
           if (isset(
$current['@data']) && count($current) == 1)
              
$current = $current['@data'];
           else if (empty(
$current['@data'])||$current['@data']==0)
               unset(
$current['@data']);
    }
}
?>

maybe I'll do some explanations on habr
geoff at spacevs dot com 07-Nov-2007 09:13
Reading xml into a class:

<?PHP
       
class XmlData {}
       
$elements = array();
       
$elements[] =& new XmlData();
        function
startElement($parser, $name, $attrs) {
                global
$elements;
               
$element =& new XMLData();
               
$elements[count($elements)-1]->$name =& $element;
               
$elements[] =& $element;
        }
        function
endElement($parser, $name) {
                global
$elements;
               
array_pop($elements);
        }
        function
characterData($parser, $data) {
                global
$elements;
               
$elements[count($elements)-1]->data = $data;
        }
       
$xml_parser     = xml_parser_create();
       
xml_set_element_handler($xml_parser, "startElement", "endElement");
       
xml_set_character_data_handler($xml_parser, "characterData");
       
xml_parse($xml_parser, $xml, true);
       
xml_parser_free($xml_parser);
       
$request =& array_pop($elements);

        echo
$request->LOGIN->USER->data;
?>
demonpants at gmail dot com 22-Oct-2007 08:59
I wanted to access the ISBN database, and was previously parsing the HTML string generated from their main page, that is until I discovered they have an API that returns XML.

So, if anyone wants to get some information from the ISBN database, all you need to do is the following.

<?php
//Search the ISBN database for the book.
           
$url = "http://www.isbndb.com/api/books.xml? access_key=KEY&index1=isbn&value1=$_GET[ISBN]";
           
$p = xml_parser_create();
           
xml_parse_into_struct($p,file_get_contents($url),$results,$index);
           
xml_parser_free($p);

           
$title = $results[$index[TITLELONG][0]][value];
           
$author = $results[$index[AUTHORSTEXT][0]][value];
           
$publisher = $results[$index[PUBLISHERTEXT][0]][value];
?>

You will need to get an access key from isbndb.com, but it takes two seconds and is free. When you get it, replace KEY in the URL with your own key. Also, my code above will search for the book that fits the ISBN number stored in the GET variable ISBN - you can search by other parameters and return more than one result, but my example is  for a simple ISBN search.
Zvjezdan Patz 09-Sep-2007 08:22
The problem I had was I needed to generate xml on the screen for users to actually see and copy to a file. 

I'm generating the xml manually from a php file and the browser kept interpreting the xml...not very helpful. 

This is how you get around it:

<?php

$file 
= file_get_contents("http://example.com/xml.php?whatever=$whatever");
print
nl2br(htmlentities($file));

?>

Prints all my xml quite nicely.
v9 at fakehalo dot us 13-Jul-2007 08:04
I needed this for work/personal use.  Sometimes you'll have a XML string generated as one long string and no line breaks...nusoap in the case of today/work, but there are any other number of possible things that will generate these.  Anyways, this simply takes a long XML string and returns an indented/line-breaked version of the string for display/readability.

<?php
function xmlIndent($str){
   
$ret = "";
   
$indent = 0;
   
$indentInc = 3;
   
$noIndent = false;
    while((
$l = strpos($str,"<",$i))!==false){
        if(
$l!=$r && $indent>0){ $ret .= "\n" . str_repeat(" ",$indent) . substr($str,$r,($l-$r)); }
       
$i = $l+1;
       
$r = strpos($str,">",$i)+1;
       
$t = substr($str,$l,($r-$l));
        if(
strpos($t,"/")==1){
           
$indent -= $indentInc;
           
$noIndent = true;
        }
        else if((
$r-$l-strpos($t,"/"))==2 || substr($t,0,2)=="<?"){ $noIndent = true; }
        if(
$indent<0){ $indent = 0; }
        if(
$ret){ $ret .= "\n"; }
       
$ret .= str_repeat(" ",$indent);
       
$ret .= $t;
        if(!
$noIndent){ $indent += $indentInc; }
       
$noIndent = false;
    }
   
$ret .= "\n";
    return(
$ret);
}
?>

(...this was only tested for what i needed at work, could POSSIBLY need additions)
geoffers [at] gmail [dot] com 30-Dec-2006 03:27
Time to add my attempt at a very simple script that parses XML into a structure:

<?php

class Simple_Parser
{
    var
$parser;
    var
$error_code;
    var
$error_string;
    var
$current_line;
    var
$current_column;
    var
$data = array();
    var
$datas = array();
   
    function
parse($data)
    {
       
$this->parser = xml_parser_create('UTF-8');
       
xml_set_object($this->parser, $this);
       
xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1);
       
xml_set_element_handler($this->parser, 'tag_open', 'tag_close');
       
xml_set_character_data_handler($this->parser, 'cdata');
        if (!
xml_parse($this->parser, $data))
        {
           
$this->data = array();
           
$this->error_code = xml_get_error_code($this->parser);
           
$this->error_string = xml_error_string($this->error_code);
           
$this->current_line = xml_get_current_line_number($this->parser);
           
$this->current_column = xml_get_current_column_number($this->parser);
        }
        else
        {
           
$this->data = $this->data['child'];
        }
       
xml_parser_free($this->parser);
    }

    function
tag_open($parser, $tag, $attribs)
    {
       
$this->data['child'][$tag][] = array('data' => '', 'attribs' => $attribs, 'child' => array());
       
$this->datas[] =& $this->data;
       
$this->data =& $this->data['child'][$tag][count($this->data['child'][$tag])-1];
    }

    function
cdata($parser, $cdata)
    {
       
$this->data['data'] .= $cdata;
    }

    function
tag_close($parser, $tag)
    {
       
$this->data =& $this->datas[count($this->datas)-1];
       
array_pop($this->datas);
    }
}

$xml_parser = new Simple_Parser;
$xml_parser->parse('<foo><bar>test</bar></foo>');

?>
Didier: dlvb ** free * fr 24-Dec-2006 09:53
Hi !

After parsing the XML and modifying it, I just add a method to rebuild the XML form the internal structure (xmlp->document).
The method xmlp->toXML writes into xmlp->XML attributes. Then, you just have to output it.
I hope it helps.

<?php
class XMLParser {

var
$parser;
var
$filePath;
var
$document;
var
$currTag;
var
$tagStack;
var
$XML;
var
$_tag_to_close = false;
var
$TAG_ATTRIBUT = 'attr';
var
$TAG_DATA = 'data';

function
XMLParser($path) {
   
$this->parser = xml_parser_create();
   
$this->filePath = $path;
   
$this->document = array();
   
$this->currTag =& $this->document;
   
$this->tagStack = array();
   
$this->XML = "";
}

function
parse() {
   
xml_set_object($this->parser, $this);
   
xml_set_character_data_handler($this->parser, 'dataHandler');
   
xml_set_element_handler($this->parser, 'startHandler', 'endHandler');

   if(!(
$fp = fopen($this->filePath, "r"))) {
       die(
"Cannot open XML data file: $this->filePath");
       return
false;
     }

    while(
$data = fread($fp, 4096)) {
        if(!
xml_parse($this->parser, $data, feof($fp))) {
            die(
sprintf("XML error: %s at line %d",
               
xml_error_string(xml_get_error_code($this->parser)),
            
xml_get_current_line_number($this->parser)));
      }
    }

   
fclose($fp);
   
xml_parser_free($this->parser);

    return
true;
}

function
startHandler($parser, $name, $attribs) {
     if(!isset(
$this->currTag[$name]))
         
$this->currTag[$name] = array();

    
$newTag = array();
     if(!empty(
$attribs))
         
$newTag[$this->TAG_ATTRIBUT] = $attribs;
    
array_push($this->currTag[$name], $newTag);

    
$t =& $this->currTag[$name];
    
$this->currTag =& $t[count($t)-1];
    
array_push($this->tagStack, $name);
}

function
dataHandler($parser, $data) {
   
$data = trim($data);

    if(!empty(
$data)) {
      if(isset(
$this->currTag[$this->TAG_DATA]))
           
$this->currTag[$this->TAG_DATA] .= $data;
      else
           
$this->currTag[$this->TAG_DATA] = $data;
    }
}

function
endHandler($parser, $name) {
    
$this->currTag =& $this->document;
    
array_pop($this->tagStack);

     for(
$i = 0; $i < count($this->tagStack); $i++) {
         
$t =& $this->currTag[$this->tagStack[$i]];
         
$this->currTag =& $t[count($t)-1];
     }
}

function
clearOutput () {
   
$this->XML = "";
}

function
openTag ($tag) {
   
$this->XML.="<".strtolower ($tag);
   
$this->_tag_to_close = true;
}

function
closeTag () {
    if (
$this->_tag_to_close) {
       
$this->XML.=">";
       
$this->_tag_to_close = false;
    }
}

function
closingTag ($tag) {
   
$this->XML.="</".strtolower ($tag).">";
}

function
output_attributes ($contenu_fils) {
    foreach (
$contenu_fils[$this->TAG_ATTRIBUT] as $nomAttribut => $valeur) {
       
$this->XML.= " ".strtolower($nomAttribut)."=\"".$valeur."\"";
    }
}

function
addData ($texte) {
// to be completed
   
$ca  = array ("é", "è", "ê", "à");
   
$par = array ("&eacute;", "&egrave;", "&ecirc;", "agrave;");
    return
htmlspecialchars(str_replace ($ca, $par, $texte), ENT_NOQUOTES);
}

function
toXML ($tags="") {
    if (
$tags=="") {
       
$tags = $this->document;
     
$this->clearOutput ();
    }

    foreach (
$tags as $tag => $contenu) {
       
$this->process ($tag, $contenu);
    }
}

function
process ($tag, $contenu) {
    
// Pour tous les TAGs
   
foreach ($contenu as $indice => $contenu_fils) {
        
$this->openTag ($tag);

        
// Pour tous les fils (non attribut et non data)
        
foreach ($contenu_fils as $tagFils => $fils) {
             switch (
$tagFils) {
                 case
$this->TAG_ATTRIBUT:
                        
$this->output_attributes ($contenu_fils);
                       
$this->closeTag ();
                         break;
                 case
$this->TAG_DATA:
                       
$this->closeTag ();
                        
$this->XML.= $this->addData ($contenu_fils [$this->TAG_DATA]);
                         break;
                 default:
                        
$this->closeTag ();
                        
$this->process ($tagFils, $fils);
                         break;
             }
         }

       
$this->closingTag ($tag);
    }
}

}
?>
forquan 28-Jan-2006 03:45
Here's code that will create an associative array from an xml file.  Keys are the tag data and subarrays are formed from attributes and child tags

<?php
$p
=& new xmlParser();
$p->parse('/*xml file*/');
print_r($p->output);
?>

<?php
class xmlParser{
   var
$xml_obj = null;
   var
$output = array();
   var
$attrs;

   function
xmlParser(){
      
$this->xml_obj = xml_parser_create();
      
xml_set_object($this->xml_obj,$this);
      
xml_set_character_data_handler($this->xml_obj, 'dataHandler');
      
xml_set_element_handler($this->xml_obj, "startHandler", "endHandler");
   }

   function
parse($path){
       if (!(
$fp = fopen($path, "r"))) {
           die(
"Cannot open XML data file: $path");
           return
false;
       }

       while (
$data = fread($fp, 4096)) {
           if (!
xml_parse($this->xml_obj, $data, feof($fp))) {
               die(
sprintf("XML error: %s at line %d",
              
xml_error_string(xml_get_error_code($this->xml_obj)),
              
xml_get_current_line_number($this->xml_obj)));
              
xml_parser_free($this->xml_obj);
           }
       }

       return
true;
   }

   function
startHandler($parser, $name, $attribs){
      
$_content = array();
       if(!empty(
$attribs))
        
$_content['attrs'] = $attribs;
      
array_push($this->output, $_content);
   }

   function
dataHandler($parser, $data){
       if(!empty(
$data) && $data!="\n") {
          
$_output_idx = count($this->output) - 1;
          
$this->output[$_output_idx]['content'] .= $data;
       }
   }

   function
endHandler($parser, $name){
       if(
count($this->output) > 1) {
          
$_data = array_pop($this->output);
          
$_output_idx = count($this->output) - 1;
          
$add = array();
           if (
$_data['attrs'])
               
$add['attrs'] = $_data['attrs'];
           if (
$_data['child'])
               
$add['child'] = $_data['child'];
          
$this->output[$_output_idx]['child'][$_data['content']] = $add;
       }    
   }
}
?>
Greg S 17-Nov-2005 08:56
If you need utf8_encode support and configure PHP with --disable-all you will have some trouble. Unfortunately the configure options aren't completely documented. If you need utf8 functions and have everything disabled just recompile PHP with --enable-xml and you should be good to go.
compu_global_hyper_mega_net_2 at yahoo dot com 19-Sep-2004 01:35
The documentation regarding white space was never complete I think.

The XML_OPTION_SKIP_WHITE doesn't appear to do anything.  I want to preserve the newlines in a cdata section.  Setting XML_OPTION_SKIP_WHITE to 0 or false doesn't appear to help.  My character_data_handler is getting called once for each line.  This obviously should be reflected in the documentation as well.  When/how often does the handler get called exactly?  Having to build separate test cases is very time consuming.

Inserting newlines myself in my cdata handler is no good either.  For non actual CDATA sections that cause my handler to get called, long lines are split up in multiple calls.  My handler would not be able to tell the difference whether or not the subsequent calls would be due to the fact that the data is coming from the next line or the fact that some internal buffer is long enough for it to 'flush' out and call the handler.
This behaviour also needs to be properly documented.
odders 18-Mar-2004 10:36
I wrote a simple xml parser mainly to deal with rss version 2. I found lots of examples on the net, but they were all masive and bloated and hard to manipulate.

Output is sent to an array, which holds arrays containg data for each item.

Obviously, you will have to make modifications to the code to suit your needs, but there isnt a lot of code there, so that shouldnt be a problem.

<?php

   $currentElements
= array();
  
$newsArray = array();

  
readXml("./news.xml");

   echo(
"<pre>");
  
print_r($newsArray);
   echo(
"</pre>");

  
// Reads XML file into formatted html
  
function readXML($xmlFile)
   {

     
$xmlParser = xml_parser_create();

     
xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, false);
     
xml_set_element_handler($xmlParser, startElement, endElement);
     
xml_set_character_data_handler($xmlParser, characterData);

     
$fp = fopen($xmlFile, "r");

      while(
$data = fread($fp, filesize($xmlFile))){
        
xml_parse($xmlParser, $data, feof($fp));}

     
xml_parser_free($xmlParser);

   }

  
// Sets the current XML element, and pushes itself onto the element hierarchy
  
function startElement($parser, $name, $attrs)
   {

      global
$currentElements, $itemCount;

     
array_push($currentElements, $name);

      if(
$name == "item"){$itemCount += 1;}

   }

  
// Prints XML data; finds highlights and links
  
function characterData($parser, $data)
   {

      global
$currentElements, $newsArray, $itemCount;

     
$currentCount = count($currentElements);
     
$parentElement = $currentElements[$currentCount-2];
     
$thisElement = $currentElements[$currentCount-1];

      if(
$parentElement == "item"){
        
$newsArray[$itemCount-1][$thisElement] = $data;}
      else{
         switch(
$name){
            case
"title":
               break;
            case
"link":
               break;
            case
"description":
               break;
            case
"language":
               break;
            case
"item":
               break;}}

   }

  
// If the XML element has ended, it is poped off the hierarchy
  
function endElement($parser, $name)
   {

      global
$currentElements;

     
$currentCount = count($currentElements);
      if(
$currentElements[$currentCount-1] == $name){
        
array_pop($currentElements);}

   }

?>
software at serv-a-com dot com 22-Jan-2003 02:08
use:

<?php
while ($data = str_replace("\n","",fread($fp, 4096))){
 
// ...
}
?>

instead of:

<?php
while ($data = fread($fp, 4096)) {
 
// ...
}
?>

[UPDATE 17-FEB-2003: This post] resulted in some of the visitors e-mailg me on the carriage return stripping issue with questions. I'll try to make the following mumble as brief and easy to understand as possible.

1. Overview of the 4096 fragmentation issue
As you know the following freads the file 4096 bytes at a time (that is 4KB) this is perhaps ok for testing expat and figuring out how things work, but it it rather dangerous in the production environment. Data may not be fully understandable due to fread fragmentation and improperly formatted due to numerous sources(formats) of data contained within (i.e. end of line delimited CDATA).

<?php
while ($data = fread($fp, 4096)) {
  if (!
xml_parse($xml_parser, $data, feof($fp))) {
   
// ...
 
}
}
?>

Sometimes to save time one may want to load it all up into a one big variable and leave all the worries to expat. I think anything under 500 KB is ok (as long as nobody knows about it). Some may argue that larger variables are acceptable or even necessary because of the magic that take place while parsing using xml_parse. Our XML parser(expat) works and can be successfully implemented only when we know what type of XML data we are dealing with, it's average size and structure of general layout and data contained within tags. For example if the tags are followed by a line delimiter like a new line we can read it with fgets in and with minimal effort make sure that no data will be sent to the function that does not end with a end tag. But this require a fair knowledge of the file's preference for storing XML data and tags (and a bit of code between reading data and xml_parse'ing it).
It will save you a headache.

2. Pre Parser Strings and New Line Delimited Data
One important thing to note at this point is that the xml_parse function requires a string variable. You can manipulate the content of any string variable easily as we all know.

A better approach to removing newlines than:

<?php
while ($data = fread($fp, 4096)) {
$data = preg_replace("/\n|\r/","",$data); //flarp
if (!xml_parse($xml_parser, $data, feof($fp))) {...
?>

Above works across all 3 line-delimited text files  (\n, \r, \r\n). But this could potentially (or will most likely) damage or scramble data contained in for example CDATA areas. As far as I am concerned end of line characters should not be used _within_ XML tags. What seems to be the ultimate solution is to pre-parse the loaded data this would require checking the position within the XML document and adding or subtracting (using a in-between fread temporary variable) data based on conditions like: "Is within tag", "Is within CDATA" etc. before fedding it to the parser. This of course opens up a new can of worms (as in parse data for the parser...). (above procedure would take place between fread and xml_parser calls this method would be compatible with the general usage examples on top of the page)

3. The Answer to parsing arbitrary XML and Preprocessor Revisited
You can't just feed any XML document to the parser you constructed and assuming that it will work! You have to know what kind of methods for storing data are used, for example is there a end of line delimited data in the  file ?, Are there any carriage returns in the tags etc... XML files come formatted in different ways some are just a one long string of characters with out any end of line markers others have newlines, carriage returns or both (Microsloth Windows). May or may not contain space and other whitespace between tags. For this reason it is important to what I call Normalize the data before feeding it to the parser. You can perform this with regular expressions or plain old str_replace and concatenation. In many cases this can be done to the file it self sometimes to string data on the fly( as shown in the example above). But I feel it is important to normalize the data before even calling the function to call xml_parse. If you have the ability to access all data before that call you can convert it to what you fell the data should have been in the first place and omit many surprises and expensive regular expression substitution (in a tight spot) while fread'ing the data.
guy at bhaktiandvedanta dot com 27-Sep-2002 12:01
For a simple XML parser you can use this function. It doesn't require any extensions to run.

<?php
// Extracts content from XML tag

function GetElementByName ($xml, $start, $end) {

    global
$pos;
   
$startpos = strpos($xml, $start);
    if (
$startpos === false) {
        return
false;
    }
   
$endpos = strpos($xml, $end);
   
$endpos = $endpos+strlen($end);   
   
$pos = $endpos;
   
$endpos = $endpos-$startpos;
   
$endpos = $endpos - strlen($end);
   
$tag = substr ($xml, $startpos, $endpos);
   
$tag = substr ($tag, strlen($start));

    return
$tag;

}

// Open and read xml file. You can replace this with your xml data.

$file = "data.xml";
$pos = 0;
$Nodes = array();

if (!(
$fp = fopen($file, "r"))) {
    die(
"could not open XML input");
}
while (
$getline = fread($fp, 4096)) {
   
$data = $data . $getline;
}

$count = 0;
$pos = 0;

// Goes throw XML file and creates an array of all <XML_TAG> tags.
while ($node = GetElementByName($data, "<XML_TAG>", "</XML_TAG>")) {
   
$Nodes[$count] = $node;
   
$count++;
   
$data = substr($data, $pos);
}

// Gets infomation from tag siblings.
for ($i=0; $i<$count; $i++) {
$code = GetElementByName($Nodes[$i], "<Code>", "</Code>");
$desc = GetElementByName($Nodes[$i], "<Description>", "</Description>");
$price = GetElementByName($Nodes[$i], "<BasePrice>", "</BasePrice>");
}
?>

Hope this helps! :)
Guy Laor
dmarsh dot NO dot SPAM dot PLEASE at spscc dot ctc dot edu 18-Sep-2002 12:27
Some reference code I am working on as "XML Library" of which I am folding it info an object. Notice the use of the DEFINE:

Mainly Example 1 and parts of 2 & 3 re-written as an object:
--- MyXMLWalk.lib.php ---
<?php

if (!defined("PHPXMLWalk")) {
define("PHPXMLWalk",TRUE);

class
XMLWalk {
 var
$p; //short for xml parser;
 
var $e; //short for element stack/array

 
function prl($x,$i=0) {
   
ob_start();
   
print_r($x);
   
$buf=ob_get_contents();
   
ob_end_clean();
    return
join("\n".str_repeat(" ",$i),split("\n",$buf));
  }

 function
XMLWalk() {
 
$this->p = xml_parser_create();
 
$this->e = array();
 
xml_parser_set_option($this->p, XML_OPTION_CASE_FOLDING, true);
 
xml_set_element_handler($this->p, array(&$this, "startElement"), array(&$this, "endElement"));
 
xml_set_character_data_handler($this->p, array(&$this, "dataElement"));
 
register_shutdown_function(array(&$this, "free")); // make a destructor
 
}

  function
startElement($parser, $name, $attrs) {
    if (
count($attrs)>=1) {
     
$x = $this->prl($attrs, $this->e[$parser]+6);
    } else {
     
$x = "";
    }

    print
str_repeat(" ",$this->e[$parser]+0). "$name $x\n";
   
$this->e[$parser]++;
   
$this->e[$parser]++;
  }

  function
dataElement($parser, $data) {
    print
str_repeat(" ",$this->e[$parser]+0). htmlspecialchars($data, ENT_QUOTES) ."\n";
  }

  function
endElement($parser, $name) {
   
$this->e[$parser]--;
   
$this->e[$parser]--;
  }
  function
parse($data, $fp) {
    if (!
xml_parse($this->p, $data, feof($fp))) {
        die(
sprintf("XML error: %s at line %d",
                   
xml_error_string(xml_get_error_code($this->p)),
                   
xml_get_current_line_number($this->p)));
    }
  }

  function
free() {
   
xml_parser_free($this->p);
  }

}
// end of class

} // end of define

?>

--- end of file ---

Calling code:
<?php

...

require(
"MyXMLWalk.lib.php");

$file = "x.xml";

$xme = new XMLWalk;

if (!(
$fp = fopen($file, "r"))) {
    die(
"could not open XML input");
}

while (
$data = fread($fp, 4096)) {
 
$xme->parse($data, $fp);
}

...
?>
jon at gettys dot org 14-Aug-2002 01:59
[Editor's note: see also xml_parse_into_struct().]

Very simple routine to convert an XML file into a PHP structure. $obj->xml contains the resulting PHP structure. I would be interested if someone could suggest a cleaner method than the evals I am using.

<?php
$filename
= 'sample.xml';
$obj->tree = '$obj->xml';
$obj->xml = '';

function
startElement($parser, $name, $attrs) {
    global
$obj;
   
   
// If var already defined, make array
   
eval('$test=isset('.$obj->tree.'->'.$name.');');
    if (
$test) {
      eval(
'$tmp='.$obj->tree.'->'.$name.';');
      eval(
'$arr=is_array('.$obj->tree.'->'.$name.');');
      if (!
$arr) {
        eval(
'unset('.$obj->tree.'->'.$name.');');
        eval(
$obj->tree.'->'.$name.'[0]=$tmp;');
       
$cnt = 1;
      }
      else {
        eval(
'$cnt=count('.$obj->tree.'->'.$name.');');
      }
     
     
$obj->tree .= '->'.$name."[$cnt]";
    }
    else {
     
$obj->tree .= '->'.$name;
    }
    if (
count($attrs)) {
        eval(
$obj->tree.'->attr=$attrs;');
    }
}

function
endElement($parser, $name) {
    global
$obj;
   
// Strip off last ->
   
for($a=strlen($obj->tree);$a>0;$a--) {
        if (
substr($obj->tree, $a, 2) == '->') {
           
$obj->tree = substr($obj->tree, 0, $a);
            break;
        }
    }
}

function
characterData($parser, $data) {
    global
$obj;

    eval(
$obj->tree.'->data=\''.$data.'\';');
}

$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
if (!(
$fp = fopen($filename, "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);
print_r($obj->xml);
return
0;

?>
hans dot schneider at bbdo-interone dot de 24-Jan-2002 08:43
I had to TRIM the data when I passed one large String containig a wellformed XML-File to xml_parse. The String was read by CURL, which aparently put a BLANK at the end of the String. This BLANK produced a "XML not wellformed"-Error in xml_parse!
sam at cwa dot co dot nz 28-Sep-2000 07:39
I've discovered some unusual behaviour in this API when ampersand entities are parsed in cdata; for some reason the parser breaks up the section around the entities, and calls the handler repeated times for each of the sections. If you don't allow for this oddity and you are trying to put the cdata into a variable, only the last part will be stored.

You can get around this with a line like:

$foo .= $cdata;

If the handler is called several times from the same tag, it will append them, rather than rewriting the variable each time. If the entire cdata section is returned, it doesn't matter.

May happen for other entities, but I haven't investigated.

Took me a while to figure out what was happening; hope this saves someone else the trouble.
Daniel dot Rendall at btinternet dot com 07-Jul-1999 10:21
When using the XML parser, make sure you're not using the magic quotes option (e.g. use set_magic_quotes_runtime(0) if it's not the compiled default), otherwise you'll get 'not well-formed' errors when dealing with tags with attributes set in them.

 
show source | credits | sitemap | contact | advertising | mirror sites