1<?php
2
3//
4// Based on code found online at:
5// http://php.net/manual/en/function.xml-parse-into-struct.php
6//
7// Author: Eric Pollmann
8// Released into public domain September 2003
9// http://eric.pollmann.net/work/public_domain/
10// 2006-12-22, 2007-02-09: Patch for PHP 5 compatibility to fix handling
11//             of tags with no properties, thanks to Jan De Luyck and
12//             John McGuire (JDL + JM)
13// 2007-02-10: Preserve parent tag name if unclosed tag is present (EP)
14
15class XMLParser {
16    var $data;              // Input XML data buffer
17    var $vals;              // Struct created by xml_parse_into_struct
18    var $collapse_dups;     // If there is only one tag of a given name,
19                            //   shall we store as scalar or array?
20    var $index_numeric;     // Index tags by numeric position, not name.
21                            //   useful for ordered XML like CallXML.
22
23    // Read in XML on object creation.
24    // We can take raw XML data, a stream, a filename, or a url.
25    function XMLParser($data_source, $data_source_type='raw', $collapse_dups=0, $index_numeric=0) {
26        $this->collapse_dups = $collapse_dups;
27        $this->index_numeric = $index_numeric;
28        $this->data = '';
29        if ($data_source_type == 'raw')
30            $this->data = $data_source;
31
32        elseif ($data_source_type == 'stream') {
33            while (!feof($data_source))
34                $this->data .= fread($data_source, 1000);
35
36        // try filename, then if that fails...
37        } elseif (file_exists($data_source))
38            $this->data = implode('', file($data_source));
39
40        // try url
41        else {
42            $fp = fopen($data_source,'r');
43            while (!feof($fp))
44                $this->data .= fread($fp, 1000);
45            fclose($fp);
46        }
47    }
48
49    // Parse the XML file into a verbose, flat array struct.
50    // Then, coerce that into a simple nested array.
51    function getTree() {
52        $parser = xml_parser_create('UTF-8');
53        xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
54        xml_parse_into_struct($parser, $this->data, $vals, $index);
55        xml_parser_free($parser);
56
57        $i = -1;
58        return $this->getchildren($vals, $i);
59    }
60
61    // internal function: build a node of the tree
62    function buildtag($thisvals, $vals, &$i, $type) {
63        // JDL + JM: PHP 5 requires initialization
64        $tag = array();
65
66        if (isset($thisvals['attributes']))
67            $tag['ATTRIBUTES'] = $thisvals['attributes'];
68
69        // complete tag, just return it for storage in array
70        if ($type === 'complete')
71            $tag['VALUE'] = $thisvals['value'];
72
73        // open tag, recurse
74        else
75            $tag = array_merge($tag, $this->getchildren($vals, $i));
76
77        return $tag;
78    }
79
80    // internal function: build an nested array representing children
81    function getchildren($vals, &$i) {
82        $children = array();     // Contains node data
83
84        // Node has CDATA before it's children
85        if ($i > -1 && isset($vals[$i]['value']))
86            $children['VALUE'] = $vals[$i]['value'];
87
88        // Loop through children, until hit close tag or run out of tags
89        while (++$i < count($vals)) {
90
91            $type = $vals[$i]['type'];
92
93            // 'cdata':     Node has CDATA after one of it's children
94            //              (Add to cdata found before in this case)
95            if ($type === 'cdata')
96                $children['VALUE'] .= $vals[$i]['value'];
97
98            // 'complete':  At end of current branch
99            // 'open':      Node has children, recurse
100            elseif ($type === 'complete' || $type === 'open') {
101                // EP: Preserve parent tag name
102                $name = $vals[$i]['tag'];
103                $tag = $this->buildtag($vals[$i], $vals, $i, $type);
104                if ($this->index_numeric) {
105                    $tag['TAG'] = $name;
106                    $children[] = $tag;
107                } else
108                    $children[$name][] = $tag;
109            }
110
111            // 'close:      End of node, return collected data
112            //              Do not increment $i or nodes disappear!
113            elseif ($type === 'close')
114                break;
115        }
116        if ($this->collapse_dups)
117            foreach($children as $key => $value)
118                if (is_array($value) && (count($value) == 1))
119                    $children[$key] = $value[0];
120        return $children;
121    }
122}
123?>
124