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