1<?php
2/*************************
3  Coppermine Photo Gallery
4  ************************
5  Copyright (c) 2003-2016 Coppermine Dev Team
6  v1.0 originally written by Gregory Demar
7
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License version 3
10  as published by the Free Software Foundation.
11
12  ********************************************
13  Coppermine version: 1.6.03
14  $HeadURL$
15  ********************************************
16  Code below has been taken from lib.xml.php and slightly modified for use with coppermine
17  Orginal: http://www.phpclasses.org/browse/file/17412.html
18**********************************************/
19
20####### GNU General Public License #############################################
21#                                                                              #
22# This file is part of HOA Open Accessibility.                                 #
23# Copyright (c) 2007 Ivan ENDERLIN. All rights reserved.                       #
24#                                                                              #
25# HOA Open Accessibility is free software; you can redistribute it and/or      #
26# modify it under the terms of the GNU General Public License as published by  #
27# the Free Software Foundation; either version 2 of the License, or            #
28# (at your option) any later version.                                          #
29#                                                                              #
30# HOA Open Accessibility is distributed in the hope that it will be useful,    #
31# but WITHOUT ANY WARRANTY; without even the implied warranty of               #
32# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                #
33# GNU General Public License for more details.                                 #
34#                                                                              #
35# You should have received a copy of the GNU General Public License            #
36# along with HOA Open Accessibility; if not, write to the Free Software        #
37# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA   #
38#                                                                              #
39####### !GNU General Public License ############################################
40
41/**
42 * Class Xml.
43 *
44 * Parse a XML document into a nested array.
45 * @author ENDERLIN Ivan <ivan.enderlin@wanadoo.fr>
46 * @copyright 2007 ENDERLIN Ivan.
47 * @since PHP4
48 * @version 0.3
49 * @package Xml
50 * @licence GNU GPL
51 */
52
53class Xml {
54
55  /**
56   * Xml parser container.
57   *
58   * @var resource parser
59   */
60  var $parser;
61
62  /**
63   * Parse result.
64   *
65   * @var array pOut
66   */
67  var $pOut = array();
68
69  /**
70   * Contain the overlap tag temporarily .
71   *
72   * @var array track
73   */
74  var $track = array();
75
76  /**
77   * Current tag level.
78   *
79   * @var string tmpLevel
80   */
81  var $tmpLevel = '';
82
83  /**
84   * Attribut of current tag.
85   *
86   * @var array tmpAttrLevel
87   */
88  var $tmpAttrLevel = array();
89
90  /**
91   * Write result.
92   *
93   * @var string wOut
94   */
95  var $wOut = '';
96
97
98
99
100  /**
101   * parse
102   * Set the parser Xml and theses options.
103   * Xml file could be a string, a file, or curl.
104   * When the source is loaded, we run the parse.
105   * After, we clean all the memory and variables,
106   * and return the result in an array.
107   *
108   * @access  public
109   * @param   data      string    Source
110   * @return  array
111   */
112  function parse ($data) {
113
114    // ini;
115    $encoding = 'UTF-8';
116    // (re)set array;
117    $this->pOut = array();
118    $this->parser = xml_parser_create();
119
120    xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
121    xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $encoding);
122
123    xml_set_object($this->parser, $this);
124    xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
125    xml_set_character_data_handler($this->parser, 'contentHandler');
126
127    // format source;
128    if($data == '') {
129      return trigger_error('Xml parser need data.', E_USER_ERROR);
130    }
131
132    // parse $data;
133    $parse = xml_parse($this->parser, $data);
134    if(!$parse)
135      return trigger_error('XML Error : %s at line %d.', E_USER_ERROR,
136        array(xml_error_string(xml_get_error_code($this->parser)),
137          xml_get_current_line_number($this->parser)));
138
139    // destroy parser;
140    xml_parser_free($this->parser);
141
142    // unset extra vars;
143    unset($data,
144        $this->track,
145        $this->tmpLevel);
146
147    // remove global tag and return the result;
148    return $this->pOut[0][key($this->pOut[0])];
149  }
150
151
152
153  /**
154   * startHandler
155   * Manage the open tag, and these attributs by callback.
156   * The purpose is to create a pointer : {{int ptr}}.
157   * If the pointer exists, we have a multi-tag situation.
158   * Tag name  is stocked like : '<tag>'
159   * Attributs is stocked like : '<tag>-ATTR'
160   * Return true but built $this->pOut.
161   *
162   * @access  private
163   * @param   parser  resource    Parser resource.
164   * @param   tag     string      Tag name.
165   * @param   attr    array       Attribut.
166   * @return  bool
167   */
168  function startHandler ( $parser, $tag, $attr ) {
169
170    // built $this->track;
171    $this->track[] = $tag;
172    // place pointer to the end;
173    end($this->track);
174    // temp level;
175    $this->tmpLevel = key($this->track);
176
177    // built attrLevel into $this->tmpAttrLevel
178    if(isset($this->tmpAttrLevel[$this->tmpLevel]['attrLevel']))
179      $this->tmpAttrLevel[$this->tmpLevel]['attrLevel']++;
180
181    // built $this->pOut;
182    if(!isset($this->pOut[key($this->track)][$tag])) {
183      $this->pOut[key($this->track)][$tag] = '{{'.key($this->track).'}}';
184
185      if(!isset($this->tmpAttrLevel[$this->tmpLevel]['attrLevel']))
186        $this->tmpAttrLevel[$this->tmpLevel]['attrLevel'] = 0;
187    }
188
189    // built attributs;
190    if(!empty($attr)) {
191
192      $this->tmpAttrLevel[$this->tmpLevel][] = $this->tmpAttrLevel[$this->tmpLevel]['attrLevel'];
193      end($this->tmpAttrLevel[$this->tmpLevel]);
194
195      // it's the first attribut;
196      if(!isset($this->pOut[key($this->track)][$tag.'-ATTR']))
197          $this->pOut[key($this->track)][$tag.'-ATTR'] = $attr;
198
199      // or it's not the first;
200      else {
201        // so it's the second;
202        if(!prev($this->tmpAttrLevel[$this->tmpLevel])) {
203          $this->pOut[key($this->track)][$tag.'-ATTR'] = array(
204            current($this->tmpAttrLevel[$this->tmpLevel]) => $this->pOut[key($this->track)][$tag.'-ATTR'],
205            next($this->tmpAttrLevel[$this->tmpLevel])    => $attr
206          );
207        }
208        // or one other;
209        else
210          $this->pOut[key($this->track)][$tag.'-ATTR'][$this->tmpAttrLevel[$this->tmpLevel]['attrLevel']] = $attr;
211      }
212    }
213
214    return true;
215  }
216
217
218
219  /**
220   * contentHandler
221   * Detect the pointer, or the multi-tag by callback.
222   * If we have a pointer, the method replaces this pointer by the content.
223   * Else we have a multi-tag, the method add a element to this array.
224   * Return true but built $this->pOut.
225   *
226   * @access  private
227   * @param   parser          resource    Parser resource.
228   * @param   contentHandler  string      Tag content.
229   * @return  bool
230   */
231  function contentHandler ( $parser, $contentHandler ) {
232
233    // remove all spaces;
234    if(!preg_match('#^\s*$#', $contentHandler)) {
235
236      // $contentHandler is a string;
237      if(is_string($this->pOut[key($this->track)][current($this->track)])) {
238
239        // then $contentHandler is a pointer : {{int ptr}}     case 1;
240        if(preg_match('#{{([0-9]+)}}#', $this->pOut[key($this->track)][current($this->track)]))
241          $this->pOut[key($this->track)][current($this->track)] = $contentHandler;
242
243        // or then $contentHandler is a multi-tag content      case 2;
244        else {
245          $this->pOut[key($this->track)][current($this->track)] = array(
246            0 => $this->pOut[key($this->track)][current($this->track)],
247            1 => $contentHandler
248          );
249        }
250      }
251      // or $contentHandler is an array;
252      else {
253
254        // then $contentHandler is the multi-tag array         case 1;
255        if(isset($this->pOut[key($this->track)][current($this->track)][0]))
256          $this->pOut[key($this->track)][current($this->track)][] = $contentHandler;
257
258        // or then $contentHandler is a node-tag               case 2;
259        else
260          $this->pOut[key($this->track)][current($this->track)] = array(
261            0 => $this->pOut[key($this->track)][current($this->track)],
262            1 => $contentHandler
263          );
264      }
265
266    }
267
268    return true;
269  }
270
271
272
273  /**
274   * endHandler
275   * Detect the last pointer by callback.
276   * Move the last tags block up.
277   * And reset some temp variables.
278   * Return true but built $this->pOut.
279   *
280   * @access  private
281   * @param   parser  resource    Parser resource.
282   * @param   tag     string      Tag name.
283   * @return  bool
284   */
285  function endHandler ( $parser, $tag ) {
286
287    // if level--;
288    if(key($this->track) == $this->tmpLevel-1) {
289      // search up tag;
290      // use array_keys if an empty tag exists (taking the last tag);
291
292      // if it's a normal framaset;
293      $keyBack = array_keys($this->pOut[key($this->track)], '{{'.key($this->track).'}}');
294      $count = count($keyBack);
295
296      if($count != 0) {
297        $keyBack = $keyBack{$count-1};
298        // move this level up;
299        $this->pOut[key($this->track)][$keyBack] = $this->pOut[key($this->track)+1];
300      }
301
302      // if we have a multi-tag framaset ($count == 0);
303      else {
304        // if place is set;
305        if(isset($this->pOut[key($this->track)][current($this->track)][0])) {
306
307          // if it's a string, we built an array;
308          if(is_string($this->pOut[key($this->track)][current($this->track)]))
309            $this->pOut[key($this->track)][current($this->track)] = array(
310              0 => $this->pOut[key($this->track)][current($this->track)],
311              1 => $this->pOut[key($this->track)+1]
312            );
313
314          // else add an index into the array;
315          else
316            $this->pOut[key($this->track)][current($this->track)][] = $this->pOut[key($this->track)+1];
317        }
318        // else set the place;
319        else
320          $this->pOut[key($this->track)][current($this->track)] = array(
321            0 => $this->pOut[key($this->track)][current($this->track)],
322            1 => $this->pOut[key($this->track)+1]
323          );
324      }
325
326      // kick $this->pOut level out;
327      array_pop($this->pOut);
328      end($this->pOut);
329    }
330
331    // re-temp level;
332    $this->tmpLevel = key($this->track);
333
334    while(isset($this->tmpAttrLevel[$this->tmpLevel+1]))
335      array_pop($this->tmpAttrLevel);
336
337    // kick $this->track level out;
338    array_pop($this->track);
339    end($this->track);
340
341    return true;
342  }
343}
344//EOF