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

search for in the

xml_parse> <xml_get_error_code
Last updated: Fri, 28 Nov 2008

view this page in

xml_parse_into_struct

(PHP 4, PHP 5)

xml_parse_into_structParse XML data into an array structure

Description

int xml_parse_into_struct ( resource $parser , string $data , array &$values [, array &$index ] )

This function parses an XML file into 2 parallel array structures, one (index ) containing pointers to the location of the appropriate values in the values array. These last two parameters must be passed by reference.

Parameters

parser

data

values

index

Return Values

xml_parse_into_struct() returns 0 for failure and 1 for success. This is not the same as FALSE and TRUE, be careful with operators such as ===.

Examples

Below is an example that illustrates the internal structure of the arrays being generated by the function. We use a simple note tag embedded inside a para tag, and then we parse this and print out the structures generated:

Example #1 xml_parse_into_struct() example

<?php
$simple 
"<para><note>simple note</note></para>";
$p xml_parser_create();
xml_parse_into_struct($p$simple$vals$index);
xml_parser_free($p);
echo 
"Index array\n";
print_r($index);
echo 
"\nVals array\n";
print_r($vals);
?>

When we run that code, the output will be:

Index array
Array
(
    [PARA] => Array
        (
            [0] => 0
            [1] => 2
        )

    [NOTE] => Array
        (
            [0] => 1
        )

)

Vals array
Array
(
    [0] => Array
        (
            [tag] => PARA
            [type] => open
            [level] => 1
        )

    [1] => Array
        (
            [tag] => NOTE
            [type] => complete
            [level] => 2
            [value] => simple note
        )

    [2] => Array
        (
            [tag] => PARA
            [type] => close
            [level] => 1
        )

)

Event-driven parsing (based on the expat library) can get complicated when you have an XML document that is complex. This function does not produce a DOM style object, but it generates structures amenable of being transversed in a tree fashion. Thus, we can create objects representing the data in the XML file easily. Let's consider the following XML file representing a small database of aminoacids information:

Example #2 moldb.xml - small database of molecular information

<?xml version="1.0"?>
<moldb>

  <molecule>
      <name>Alanine</name>
      <symbol>ala</symbol>
      <code>A</code>
      <type>hydrophobic</type>
  </molecule>

  <molecule>
      <name>Lysine</name>
      <symbol>lys</symbol>
      <code>K</code>
      <type>charged</type>
  </molecule>

</moldb>
And some code to parse the document and generate the appropriate objects:

Example #3 parsemoldb.php - parses moldb.xml into an array of molecular objects

<?php

class AminoAcid {
    var 
$name;  // aa name
    
var $symbol;    // three letter symbol
    
var $code;  // one letter code
    
var $type;  // hydrophobic, charged or neutral
    
    
function AminoAcid ($aa
    {
        foreach (
$aa as $k=>$v)
            
$this->$k $aa[$k];
    }
}

function 
readDatabase($filename
{
    
// read the XML database of aminoacids
    
$data implode(""file($filename));
    
$parser xml_parser_create();
    
xml_parser_set_option($parserXML_OPTION_CASE_FOLDING0);
    
xml_parser_set_option($parserXML_OPTION_SKIP_WHITE1);
    
xml_parse_into_struct($parser$data$values$tags);
    
xml_parser_free($parser);

    
// loop through the structures
    
foreach ($tags as $key=>$val) {
        if (
$key == "molecule") {
            
$molranges $val;
            
// each contiguous pair of array entries are the 
            // lower and upper range for each molecule definition
            
for ($i=0$i count($molranges); $i+=2) {
                
$offset $molranges[$i] + 1;
                
$len $molranges[$i 1] - $offset;
                
$tdb[] = parseMol(array_slice($values$offset$len));
            }
        } else {
            continue;
        }
    }
    return 
$tdb;
}

function 
parseMol($mvalues
{
    for (
$i=0$i count($mvalues); $i++) {
        
$mol[$mvalues[$i]["tag"]] = $mvalues[$i]["value"];
    }
    return new 
AminoAcid($mol);
}

$db readDatabase("moldb.xml");
echo 
"** Database of AminoAcid objects:\n";
print_r($db);

?>
After executing parsemoldb.php, the variable $db contains an array of AminoAcid objects, and the output of the script confirms that:
** Database of AminoAcid objects:
Array
(
    [0] => aminoacid Object
        (
            [name] => Alanine
            [symbol] => ala
            [code] => A
            [type] => hydrophobic
        )

    [1] => aminoacid Object
        (
            [name] => Lysine
            [symbol] => lys
            [code] => K
            [type] => charged
        )

)



xml_parse> <xml_get_error_code
Last updated: Fri, 28 Nov 2008
 
add a note add a note User Contributed Notes
xml_parse_into_struct
aerik at thesylvans dot com
29-Nov-2008 12:55
Here's another attempt at dealing with rebuilding the xml from the array.  I'm basically preventing certain tags from being self closing by just checking for them explicitly (sure wish expat didn't interpret <textarea /> and <textarea></textarea> the same way (as "complete" tags).

<?
function array2xml($xmlary){
 
$o='';
  foreach(
$xmlary as $tag ){
    if(
$tag['tag'] == 'textarea' && !isset($tag['value'])){
     
//fake a value so it won't self close
     
$tag['value']='';
    }
   
//tab space:
   
$t = '';
    for(
$i=1; $i < $tag['level'];$i++){
     
$t.="\t";
    }
    switch(
$tag['type']){
      case
'complete':
      case
'open':
       
$o.=$t.'<'.$tag['tag'];
        if(isset(
$tag['attributes'])){
          foreach(
$tag['attributes'] as $attr=>$aval){
           
$o.=' '.$attr.'="'.$aval.'"';
          }
//foreach
       
}//attributes
       
if($tag['type'] == 'complete'){
          if(!isset(
$tag['value'])){
           
$o .= ' />'."\n";
          } else {
           
$o .= '>'."\n".$t.$tag['value']."\n".$t.'</'.$tag['tag'].'>'."\n";
          }
        }else{
         
$o .= '>'."\n";
        }
        break;
      case
'close':
       
$o .= $t.'</'.$tag['tag'].'>'."\n";
        break;
      case
'cdata':
       
$o .= $t.$tag['value']."\n";
        break;
    }
//switch
 
}//foreach
}
?>
lpatrick
03-Nov-2008 01:36
@ dale at ucsc dot edu
I modified your code for entries like <caption>Value</caption>
<?php
$o
='';
foreach(
$vals as $tag ){
   
//tab space:
   
for($i=1; $i < $tag['level'];$i++)
       
$o.="\t";
    if(
$tag['type']!='close'){
        if(
$tag['type']!='cdata')
           
$o.='<'.$tag['tag'];
        if(isset(
$tag['attributes'])){
            foreach(
$tag['attributes'] as $attr=>$aval){
               
$o.=' '.$attr.'="'.$aval.'"';
            }
        }
        if(
$tag['type']!='cdata'){
           
$o.=($tag['type']=='complete' && (!isset($tag['value']) || $tag['value']==''))?'/>':'>';
        }
       
$o.=(isset($tag['value']))?$tag['value']:'';
        if(
$tag['type']!='cdata'){
           
$o.=($tag['type']=='complete' && (isset($tag['value']) && $tag['value']<>''))?'</'.$tag['tag'].'>':'';
        }
       
$o.="\n";
    }else{
       
$o.='</'.$tag['tag'].'>'."\n";
    }
}
?>
dale at ucsc dot edu
26-Sep-2008 01:16
here you can [re]assemble xml from a xml_parse_into_struct generated array: ie array2xml

<?php
$xml_parser
= xml_parser_create( 'UTF-8' ); // UTF-8 or ISO-8859-1
xml_parser_set_option( $xml_parser, XML_OPTION_CASE_FOLDING, 0 );
xml_parser_set_option( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
xml_parse_into_struct( $xml_parser, $my_text_xml, $aryXML );
xml_parser_free($xml_parser);

//now use aryXML array to xml string:
       
$o='';
        foreach(
$aryXML as $tag ){
           
//tab space:
           
for($i=1; $i < $tag['level'];$i++)
               
$o.="\t";
            if(
$tag['type']!='close'){
                if(
$tag['type']!='cdata')
                   
$o.='<'.$tag['tag'];
                if(isset(
$tag['attributes'])){               
                    foreach(
$tag['attributes'] as $attr=>$aval){
                       
$o.=' '.$attr.'="'.$aval.'"';
                    }
                }
                if(
$tag['type']!='cdata'){
                   
$o.=($tag['type']=='complete')?'/>'."\n":'>'."\n";
                }
               
$o.=(isset($tag['value']))?$tag['value']:'';
            }else{
               
$o.='</'.$tag['tag'].'>';
            }               
        }
?>
Llorenc
10-Sep-2008 12:20
Sorry, forgot my last post below, I confused CDATA sections (<![CDATA[]]>) with the value type "cdata", which despite its name it's very different! The first allows adding markup (&,<,>) as regular text and the second is to indicate a not complete node between the open and close nodes. What is true is that the CDATA delimiters are lost after xml_parse_into_struct so it's not possible to revert the process.

Please, feel free to remove my comments or leave them if you think they can help anyone.
Llorenc
09-Sep-2008 03:08
Many examples below using this function check if a given tag returned is of type "cdata". It seems to me nowadays CDATA sections are not detected by all implementations, if any. In the servers I could test (FreeBSD 6.2 with PHP 5.2.6 and libxml 2.6.26 or PHP 4.4.8 and expat 2.0.0 - MacOSX 10.5.4 with PHP 5.2.6 and libxml 2.6.16) my CDATA sections were reported as being of type "complete".

This problem was reported earlier as a bug (#26188), but was discarded because "the behaviour is quite dependent on the backend XML parsing implementation" (sic).

So if you want to detect CDATA sections and not depend on a given implementation, you'll have to forget this function and use xml_parse and xml_set_character_data_handler instead.
POST at PSPAETH dot DE
14-Aug-2008 05:45
I wrote a simple function which totally flattens any xml structure. You can then directly get the data or use regular expression functions to fetch data from the output. E.g., if the input is
   <gallery xmlns='http://www.pspaeth.de/joomlaPlugins/psg'>
        <g imageDir='einsatzGallerie'></g>
          <caption for="f1.jpg"}This is picture number 1</caption>
          <caption for="f2.jpg"}This is picture number 2</caption>
        </g>
   </gallery>
the output will be an array:
   ['.gallery0@xmlns']=> 
      ".gallery0@xmlns=http://www.pspaeth.de/joomlaPlugins/psg"
   ['.gallery0.g0@imageDir']=>
      ".gallery0.g0@imageDir=einsatzGallerie"
   ['.gallery0.g0.caption0@for']=>
      ".gallery0.g0.caption0@for=f1.jpg"
   ['.gallery0.g0.caption0']=>
      ".gallery0.g0.caption0=This is picture number 1"
   ['.gallery0.g0.caption1@for']=> 
      ".gallery0.g0.caption1@for=f2.jpg"
   ['.gallery0.g0.caption1']=> 
      ".gallery0.g0.caption1=This is picture number 2"

Here the code:
<?php
function _xmlToArray($xml) {
  
$parser = xml_parser_create('UTF-8'); // UTF-8 or ISO-8859-1
  
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
  
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
  
xml_parse_into_struct($parser, $xml, $values);
  
xml_parser_free($parser);
    
  
$return = array();
  
$stack = '';
  
$attrs = array();
  
$levelCounters = array(0);
   foreach(
$values as $val) {
     if(
$val['type'] == "open") {
      
$ind = array_pop($levelCounters);
      
$stack = $stack . "." . $val['tag'] . $ind;
       if (
$val['attributes']) {
           foreach(
$val['attributes'] as $attrKey => $attrVal)
            
$return[$stack . '@' . $attrKey] = $stack . '@' . $attrKey . "=" .
               
$attrVal;
       }
      
array_push($levelCounters,$ind+1);
      
array_push($levelCounters,0);          
     } elseif(
$val['type'] == "close") {
      
$stack = substr($stack,0,strrpos($stack,'.'));
     } elseif(
$val['type'] == "complete") {
      
$ind = array_pop($levelCounters);
      
$stack = $stack . "." . $val['tag'] . $ind;
       if (
$val['attributes']) {
           foreach(
$val['attributes'] as $attrKey => $attrVal)
            
$return[$stack . '@' . $attrKey] = $stack . '@' . $attrKey . "=" .
               
$attrVal;
       }
      
$return[$stack] = $stack . "=" . $val['value'];
      
$stack = substr($stack,0,strrpos($stack,'.'));
      
array_push($levelCounters,$ind+1);
     }
   }
   return
$return;
}  
?>
wickedfather at hotmail dot com
06-Jul-2008 09:33
Had a comment up that was removed saying I'd found drawbacks, but here's a class what I wrote which works fine for me.  Comments removed due to post restrictions.  Pass the xml into the parse method or in the constructor.  Php4 and 5 safe.

class XMLThing
{
    var $rawXML;
    var $valueArray = array();
    var $keyArray = array();
    var $parsed = array();
    var $index = 0;
    var $attribKey = 'attributes';
    var $valueKey = 'value';
    var $cdataKey = 'cdata';
    var $isError = false;
    var $error = '';

    function XMLThing($xml = NULL)
    {
        $this->rawXML = $xml;
    }

    function parse($xml = NULL)
    {
        if (!is_null($xml))
        {
            $this->rawXML = $xml;
        }

        $this->isError = false;
           
        if (!$this->parse_init())
        {
            return false;
        }

        $this->index = 0;
        $this->parsed = $this->parse_recurse();
        $this->status = 'parsing complete';

        return $this->parsed;
    }

    function parse_recurse()
    {       
        $found = array();
        $tagCount = array();

        while (isset($this->valueArray[$this->index]))
        {
            $tag = $this->valueArray[$this->index];
            $this->index++;

            if ($tag['type'] == 'close')
            {
                return $found;
            }

            if ($tag['type'] == 'cdata')
            {
                $tag['tag'] = $this->cdataKey;
                $tag['type'] = 'complete';
            }

            $tagName = $tag['tag'];

            if (isset($tagCount[$tagName]))
            {       
                if ($tagCount[$tagName] == 1)
                {
                    $found[$tagName] = array($found[$tagName]);
                }
                   
                $tagRef =& $found[$tagName][$tagCount[$tagName]];
                $tagCount[$tagName]++;
            }
            else   
            {
                $tagCount[$tagName] = 1;
                $tagRef =& $found[$tagName];
            }

            switch ($tag['type'])
            {
                case 'open':
                    $tagRef = $this->parse_recurse();

                    if (isset($tag['attributes']))
                    {
                        $tagRef[$this->attribKey] = $tag['attributes'];
                    }
                       
                    if (isset($tag['value']))
                    {
                        if (isset($tagRef[$this->cdataKey]))   
                        {
                            $tagRef[$this->cdataKey] = (array)$tagRef[$this->cdataKey];   
                            array_unshift($tagRef[$this->cdataKey], $tag['value']);
                        }
                        else
                        {
                            $tagRef[$this->cdataKey] = $tag['value'];
                        }
                    }
                    break;

                case 'complete':
                    if (isset($tag['attributes']))
                    {
                        $tagRef[$this->attribKey] = $tag['attributes'];
                        $tagRef =& $tagRef[$this->valueKey];
                    }

                    if (isset($tag['value']))
                    {
                        $tagRef = $tag['value'];
                    }
                    break;
            }           
        }

        return $found;
    }

    function parse_init()
    {
        $this->parser = xml_parser_create();

        $parser = $this->parser;
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);    
        xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);       
        if (!$res = (bool)xml_parse_into_struct($parser, $this->rawXML, $this->valueArray, $this->keyArray))
        {
            $this->isError = true;
            $this->error = 'error: '.xml_error_string(xml_get_error_code($parser)).' at line '.xml_get_current_line_number($parser);
        }
        xml_parser_free($parser);

        return $res;
    }
}
Anonymous
02-Jul-2008 05:35
@jemptymethod:

I found only one downfall to your function. If you have duplicates, the first one cannot be accessed iteratively.

I added the following to fix the problem:

<?php
...
        else if (
is_array($parent['children'][$tag])) {
            if(!isset(
$parent['children'][$tag][0])) {
               
$oldSingle = $parent['children'][$tag];
               
$parent['children'][$tag] = null;
               
$parent['children'][$tag][] = $oldSingle;
               
            }
             
$parent['children'][$tag][] = $pop['children'];
        }
...
?>
jemptymethod at gmail dot com
16-Jun-2008 10:39
@wickedfather, re: beaudurrant....I just cannot concur that the latter's class is "great"....First of all, it doesn't handle attributes.  Second of all, as my code below demonstrates, there is no need waste processing by determining duplicates within their own loop.  Thirdly, when there are duplicates, it collapses the layer in the array above the duplicates.

Here is some much more consise code, though not (yet) object oriented, and also not yet handling attributes; it maintains a stack for each level:

<?php
function xml_parse_into_assoc($file) {
 
$data = implode("", file($file));
 
$p = xml_parser_create();
 
 
xml_parser_set_option($p, XML_OPTION_CASE_FOLDING, 0);
 
xml_parser_set_option($p, XML_OPTION_SKIP_WHITE, 1);
 
 
xml_parse_into_struct($p, $data, $vals, $index);
 
xml_parser_free($p);

 
$levels = array(null);
 
  foreach (
$vals as $val) {
    if (
$val['type'] == 'open' || $val['type'] == 'complete') {
      if (!
array_key_exists($val['level'], $levels)) {
       
$levels[$val['level']] = array();
      }
    }
   
   
$prevLevel =& $levels[$val['level'] - 1];
   
$parent = $prevLevel[sizeof($prevLevel)-1];
   
    if (
$val['type'] == 'open') {
     
$val['children'] = array();
     
array_push(&$levels[$val['level']], $val);
      continue;
    }
   
    else if (
$val['type'] == 'complete') {
     
$parent['children'][$val['tag']] = $val['value'];
    }
   
    else if (
$val['type'] == 'close') {
     
$pop = array_pop($levels[$val['level']]);
     
$tag = $pop['tag'];
     
      if (
$parent) {
        if (!
array_key_exists($tag, $parent['children'])) {
         
$parent['children'][$tag] = $pop['children'];
        }
        else if (
is_array($parent['children'][$tag])) {
         
$parent['children'][$tag][] = $pop['children'];
        }
      }
      else {
        return(array(
$pop['tag'] => $pop['children']));
      }
    }
   
   
$prevLevel[sizeof($prevLevel)-1] = $parent;
  }
}
?>
jukea
18-Apr-2008 11:09
concerning Adam Tylmad's code, note that the line

 if ($data = xml::cleanString($data))

prevents 0 values to be considered, as this will evaluate to false. I just tracked down this bug in our system .. ouch
wickedfather at hotmail dot com
12-Apr-2008 05:36
To beaudurrant - that class is great and structures things in a very sensible way.  Only problem is that it raises an error if a tag is empty, so would suggest a simple mod to the parse method just to add an isset test.

if (isset($val['value']))
{
    $this->setArrayValue($this->output, $stack, $val['value']);
}
mathiasrav at gmail dot com
10-Mar-2008 05:42
In response to Anonymous' post at 26-Feb-2008 11:50:

Saying that you "don't understand everything" isn't going to get you very popular - you should understand the code you use.
foreach isn't *slow* in PHP, it is actually faster than the equivalent for-construct (which, in many cases, isn't available).

The reason your script is slow is simply your use of xml_parse_into_struct - it reads the whole XML-string and doesn't return until it has parsed and validated it all. If you're looking for efficiency, you'll have to use the more low-level xml_parser_create, xml_set_*_handler functions. Then make sure you don't keep everything in a huge array before outputting it (at least don't if you're going for efficiency).
Anonymous
26-Feb-2008 11:50
Hi, I actually use this parser without understanding everything. I read somewhere that using "foreach" is very slow, and I indeed noticed that this parser was slow, when getting a lot of data, so how should I edit it to make it faster ? (with the exact same output) thanks in advance

    $xml_parser = xml_parser_create();
    $data = $outputone;
   
    xml_parse_into_struct($xml_parser, $data, $vals, $index);
    xml_parser_free($xml_parser);
   
    $params = array();
    $level = array();
    $i="1";
   
    foreach ($vals as $xml_elem) {
        if ($xml_elem['type'] == 'open' && $xml_elem['level'] == '1') {
                $level[$xml_elem['level']] = $xml_elem['tag'];
        }
        if ($xml_elem['type'] == 'open' && $xml_elem['level'] == '2') {
                $level[$xml_elem['level']] = $xml_elem['tag']."".$i;
                $i++;
        }       
        if ($xml_elem['type'] == 'complete') {
            $start_level = 1;
            $php_stmt = '$params';
            while($start_level < $xml_elem['level']) {
                $php_stmt .= '[$level['.$start_level.']]';
                $start_level++;
            }
            $php_stmt .= '[$xml_elem[\'tag\']] = $xml_elem[\'value\'];';
                eval($php_stmt);
        }
    }
cesaralcaide at gmail dot com
10-Jan-2008 11:35
I didn't fount an appropiate xml2array translaction for my purpose, so I wrote this:

(convert an xml string to an associative array allowing multiple elements with the same name)

/////////////////////////////////// Inicio XML
//
//
//
// Convierte un XML en un array asociativo cuyos elementos son arrays
// (para permitir varios elementos del mismo nombre)
//
// Limitación: el elemento "attributos" no puede aparecer en el XML, pues corresponde al
// de attribs de un tag (un Tag empieza con "T",no "t")
//
////////////////////////////////////

function xml_analiza($xml) {
 global $xml_resul,$xml_n,$xml_cont,$xml_attr;
 $xml_n = 0;
 $xml_resul = array();
 $xml_cont = array();
 $xml_attr = array();
 $p = xml_parser_create();
 //Si quisiéramos distinguir Mayúsculas-Minúsculas en los nombres de etiquetas:
 xml_parser_set_option($p, XML_OPTION_CASE_FOLDING, 0);
 xml_set_element_handler($p, "xml_inicio", "xml_fin");
 xml_set_character_data_handler($p, "xml_char");

 // Recorta hasta el primer tag del XML:
 $i = instr($xml,"<" . "?xml");
 if (!$i) return array();
 $j = instr($i,$xml,"?" . ">");
 if (!$j) return array();
 $xml = substr($xml,$j+2);

 if (!xml_parse($p, $xml))
  alarma("URG","xml_analiza","XML error: " . xml_error_string(xml_get_error_code($p))
   . " en la línea " . xml_get_current_line_number($p) . " ($xml)");
 xml_parser_free($p);
 if (!sizeof($xml_resul)) return array();
 return $xml_resul[0];
}

function xml_inicio($p, $nombre, $atributos) {
 global $xml_resul,$xml_n,$xml_cont,$xml_attr;
 $xml_n++;
 $xml_resul[] = array();
 $xml_cont[] = "";
 $xml_attr[] = $atributos;
}

function xml_fin($p, $nombre) {
 global $xml_resul,$xml_n,$xml_cont,$xml_attr;
 $xml_n--;
 $nuevo = array_pop($xml_resul);
 $nombre = $nombre;
 if ($nombre == "attributos") alarma("URG","xml_analiza","Tag con nombre no permitido (attributos)");
 $conte = array_pop($xml_cont);
 $attrib = array_pop($xml_attr);
 if ($conte) $xml_resul[$xml_n][$nombre][] = $conte;
 else {
  if ($nuevo) {
   if ($attrib) $nuevo["attributos"][] = $attrib;
   $xml_resul[$xml_n][$nombre][] = $nuevo;
  }
  else
   $xml_resul[$xml_n][$nombre][] = "";
 }
}

function xml_char($p, $data) {
 global $xml_cont;
 $xml_cont[sizeof($xml_cont)-1] .= trim(str_replace("\n","",$data));
}

function xml_a($a) {
 // Devuelve un elemento del array XML, p.e.: xml_a($v,"FichaCircuito","Red",0)
 $n = func_num_args();
 for ($i=1;$i<$n;$i++) {
  $b = func_get_arg($i);
  if (isset($a[$b]))
   $a = $a[$b];
  else {
   if (!isset($a[0][$b][0])) return "";
   $a = $a[0][$b];
  }
 }
 return $a;
}
beaudurrant at gmail dot com
19-Dec-2007 08:23
This is extending what Alf Marius Foss Olsen had posted above.

It takes into account array keys with the same name and uses an increment for them instead of overwriting the keys.

I am using it for SOAP requests (20K - 150K) and it parses very fast compared to PEAR.

<?

class XMLParser  {
   
   
// raw xml
   
private $rawXML;
   
// xml parser
   
private $parser = null;
   
// array returned by the xml parser
   
private $valueArray = array();
    private
$keyArray = array();
   
   
// arrays for dealing with duplicate keys
   
private $duplicateKeys = array();
   
   
// return data
   
private $output = array();
    private
$status;

    public function
XMLParser($xml){
       
$this->rawXML = $xml;
       
$this->parser = xml_parser_create();
        return
$this->parse();
    }

    private function
parse(){
       
       
$parser = $this->parser;
       
       
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); // Dont mess with my cAsE sEtTings
       
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);     // Dont bother with empty info
       
if(!xml_parse_into_struct($parser, $this->rawXML, $this->valueArray, $this->keyArray)){
           
$this->status = 'error: '.xml_error_string(xml_get_error_code($parser)).' at line '.xml_get_current_line_number($parser);
            return
false;
        }
       
xml_parser_free($parser);

       
$this->findDuplicateKeys();

       
// tmp array used for stacking
       
$stack = array();        
       
$increment = 0;
       
        foreach(
$this->valueArray as $val) {
            if(
$val['type'] == "open") {
               
//if array key is duplicate then send in increment
               
if(array_key_exists($val['tag'], $this->duplicateKeys)){
                   
array_push($stack, $this->duplicateKeys[$val['tag']]);
                   
$this->duplicateKeys[$val['tag']]++;
                }
                else{
                   
// else send in tag
                   
array_push($stack, $val['tag']);
                }
            } elseif(
$val['type'] == "close") {
               
array_pop($stack);
               
// reset the increment if they tag does not exists in the stack
               
if(array_key_exists($val['tag'], $stack)){
                   
$this->duplicateKeys[$val['tag']] = 0;
                }
            } elseif(
$val['type'] == "complete") {
               
//if array key is duplicate then send in increment
               
if(array_key_exists($val['tag'], $this->duplicateKeys)){
                   
array_push($stack, $this->duplicateKeys[$val['tag']]);
                   
$this->duplicateKeys[$val['tag']]++;
                }
                else{               
                   
// else send in tag
                   
array_push($stack$val['tag']);
                }
               
$this->setArrayValue($this->output, $stack, $val['value']);
               
array_pop($stack);
            }
           
$increment++;
        }

       
$this->status = 'success: xml was parsed';
        return
true;

    }
   
    private function
findDuplicateKeys(){
       
        for(
$i=0;$i < count($this->valueArray); $i++) {
           
// duplicate keys are when two complete tags are side by side
           
if($this->valueArray[$i]['type'] == "complete"){
                if(
$i+1 < count($this->valueArray) ){
                    if(
$this->valueArray[$i+1]['tag'] == $this->valueArray[$i]['tag'] && $this->valueArray[$i+1]['type'] == "complete"){
                       
$this->duplicateKeys[$this->valueArray[$i]['tag']] = 0;
                    }
                }
            }
           
// also when a close tag is before an open tag and the tags are the same
           
if($this->valueArray[$i]['type'] == "close"){
                if(
$i+1 < count($this->valueArray) ){
                    if(   
$this->valueArray[$i+1]['type'] == "open" && $this->valueArray[$i+1]['tag'] == $this->valueArray[$i]['tag'])
                       
$this->duplicateKeys[$this->valueArray[$i]['tag']] = 0;
                }
            }
           
        }
       
    }
   
    private function
setArrayValue(&$array, $stack, $value){
        if (
$stack) {
           
$key = array_shift($stack);
           
$this->setArrayValue($array[$key], $stack, $value);
            return
$array;
        } else {
           
$array = $value;
        }
    }
   
    public function
getOutput(){
        return
$this->output;
    }
   
    public function
getStatus(){
        return
$this->status;   
    }
      
}

?>

Usage:

$p = new XMLParser($xml);
$p->getOutput();
php dot net at crazedsanity dot com
24-Oct-2007 04:32
There's an updated version of cs-phpxml (http://sf.net/projects/cs-phpxml, or https://cs-phpxml.svn.sourceforge.net/svnroot/cs-phpxml/releases for the latest out of subversion) which easily converts an XML string into a PHP array.  Using my previous example:::

<?php
/**
 *
 * *********** EXAMPLE ***********
 *
 * Original file contents:
 * <test xmlns="stuff">
 *         <indexOne>hello</indexOne>
 *         <my_single_index testAttribute="hello" />
 *         <multiple_items>
 *             <item>1</item>
 *             <item>2</item>
 *         </multiple_items>
 * </test>
 *
 * Would return:::
 *
 * array(
 *     TEST => array(
 *         indexOne => hello,
 *         my_single_index => NULL,
 *         multiple_items => array(
 *               items => array(
 *                   0 => 1,
 *                   1 => 2
 *               )
 *         ),
 *     ),
 * );
*/
?>

I've been using this in many production environments, and it's been very stable.  The syntax is pretty simple, too:::

<?php
require_once(dirname(__FILE__) ."/cs-phpxml/xmlParserClass.php");
$xmlParser = new xmlParser(file_get_contents("test.xml"));
$myArray = $xmlParser->get_tree(TRUE);

?>
Alf Marius Foss Olsen
12-Sep-2007 03:46
<?php
/*
  An easy lightweight (Array ->) XML -> Array algorithm..

  Typical case: You have an array you want to export to an external server,
                so you make XML out of it, exports it, and "on the other side"
                make it into an array again. These two functions will take care
                of that last part, ie XML -> Array
 
  NOTE! The function XMLToArray assumes that the XML _dont_ have nodes on the
        same level with the same name, then it just wont work. This is not a
        problem, as this case deals with Array -> XML -> Array, and an array
        cant have to identical indexes/keys.
*/

function XMLToArray($xml) {
 
$parser = xml_parser_create('ISO-8859-1'); // For Latin-1 charset
 
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); // Dont mess with my cAsE sEtTings
 
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); // Dont bother with empty info
 
xml_parse_into_struct($parser, $xml, $values);
 
xml_parser_free($parser);
 
 
$return = array(); // The returned array
 
$stack = array(); // tmp array used for stacking
 
foreach($values as $val) {
    if(
$val['type'] == "open") {
     
array_push($stack, $val['tag']);
    } elseif(
$val['type'] == "close") {
     
array_pop($stack);
    } elseif(
$val['type'] == "complete") {
     
array_push($stack, $val['tag']);
     
setArrayValue($return, $stack, $val['value']);
     
array_pop($stack);
    }
//if-elseif
 
}//foreach
 
return $return;
}
//function XMLToArray
 
function setArrayValue(&$array, $stack, $value) {
  if (
$stack) {
   
$key = array_shift($stack);
   
setArrayValue($array[$key], $stack, $value);
    return
$array;
  } else {
   
$array = $value;
  }
//if-else
}//function setArrayValue
 
// USAGE:
$xml = <<<QQQ
<?xml version="1.0"?>
<root>
  <node1>Some text</node1>
  <node2a>
    <node2b>
      <node2c>Some text</node2c>
    </node2b>
  </node2a>
</root>
\n
QQQ;
$array = XMLToArray($xml);
print
"<pre>";
print_r($array);
print
"</pre>";
//  Output:
//
//  Array
//  (
//      [root] => Array
//          (
//              [node1] => Some text
//              [node2a] => Array
//                  (
//                      [node2b] => Array
//                          (
//                              [node2c] => Some text
//                          )
//                  )
//          )
//  )
?>
vinod at citadel-soft dot com
01-Sep-2007 12:43
My previous code was having some bugs in, that is fixed now

<?php
class CSLXmlReader {
    private
$tagstack;
    private
$xmlvals
    private 
$xmlvarArrPos;
    private
$xmlfile;
    function
__construct(