annotate e2gallerypro/php/xmlparse.v5.php @ 9:325fe78243c9 judyates

[svn r10] all the good stuff from my week at mom's.
author rlm
date Sun, 11 Apr 2010 22:05:32 -0400
parents 3f6b44aa6b35
children
rev   line source
rlm@3 1 <?php
rlm@3 2 /**
rlm@3 3 This program is free software; you can redistribute it and/or modify
rlm@3 4 it under the terms of the GNU General Public License as published by
rlm@3 5 the Free Software Foundation; either version 2 of the License, or
rlm@3 6 (at your option) any later version.
rlm@3 7
rlm@3 8 This program is distributed in the hope that it will be useful,
rlm@3 9 but WITHOUT ANY WARRANTY; without even the implied warranty of
rlm@3 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
rlm@3 11 GNU General Public License for more details.
rlm@3 12
rlm@3 13 For support, please visit http://www.criticaldevelopment.net/xml/
rlm@3 14 */
rlm@3 15
rlm@3 16 /**
rlm@3 17 * XML Parser Class (php5)
rlm@3 18 *
rlm@3 19 * Parses an XML document into an object structure much like the SimpleXML extension.
rlm@3 20 *
rlm@3 21 * @author Adam A. Flynn <adamaflynn@criticaldevelopment.net>
rlm@3 22 * @copyright Copyright (c) 2005-2007, Adam A. Flynn
rlm@3 23 *
rlm@3 24 * @version 1.3.0
rlm@3 25 */
rlm@3 26 class XMLParser
rlm@3 27 {
rlm@3 28 /**
rlm@3 29 * The XML parser
rlm@3 30 *
rlm@3 31 * @var resource
rlm@3 32 */
rlm@3 33 private $parser;
rlm@3 34
rlm@3 35 /**
rlm@3 36 * The XML document
rlm@3 37 *
rlm@3 38 * @var string
rlm@3 39 */
rlm@3 40 private $xml;
rlm@3 41
rlm@3 42 /**
rlm@3 43 * Document tag
rlm@3 44 *
rlm@3 45 * @var object
rlm@3 46 */
rlm@3 47 public $document;
rlm@3 48
rlm@3 49 /**
rlm@3 50 * Current object depth
rlm@3 51 *
rlm@3 52 * @var array
rlm@3 53 */
rlm@3 54 private $stack;
rlm@3 55
rlm@3 56 /**
rlm@3 57 * Whether or not to replace dashes and colons in tag
rlm@3 58 * names with underscores.
rlm@3 59 *
rlm@3 60 * @var bool
rlm@3 61 */
rlm@3 62 private $cleanTagNames;
rlm@3 63
rlm@3 64
rlm@3 65 /**
rlm@3 66 * Constructor. Loads XML document.
rlm@3 67 *
rlm@3 68 * @param string $xml The string of the XML document
rlm@3 69 * @return XMLParser
rlm@3 70 */
rlm@3 71 function __construct($xml = '', $cleanTagNames = true)
rlm@3 72 {
rlm@3 73 //Load XML document
rlm@3 74 $this->xml = $xml;
rlm@3 75
rlm@3 76 //Set stack to an array
rlm@3 77 $this->stack = array();
rlm@3 78
rlm@3 79 //Set whether or not to clean tag names
rlm@3 80 $this->cleanTagNames = $cleanTagNames;
rlm@3 81 }
rlm@3 82
rlm@3 83 /**
rlm@3 84 * Initiates and runs PHP's XML parser
rlm@3 85 */
rlm@3 86 public function Parse()
rlm@3 87 {
rlm@3 88 //Create the parser resource
rlm@3 89 $this->parser = xml_parser_create();
rlm@3 90
rlm@3 91 //Set the handlers
rlm@3 92 xml_set_object($this->parser, $this);
rlm@3 93 xml_set_element_handler($this->parser, 'StartElement', 'EndElement');
rlm@3 94 xml_set_character_data_handler($this->parser, 'CharacterData');
rlm@3 95
rlm@3 96 //Error handling
rlm@3 97 if (!xml_parse($this->parser, $this->xml))
rlm@3 98 $this->HandleError(xml_get_error_code($this->parser), xml_get_current_line_number($this->parser), xml_get_current_column_number($this->parser));
rlm@3 99
rlm@3 100 //Free the parser
rlm@3 101 xml_parser_free($this->parser);
rlm@3 102 }
rlm@3 103
rlm@3 104 /**
rlm@3 105 * Handles an XML parsing error
rlm@3 106 *
rlm@3 107 * @param int $code XML Error Code
rlm@3 108 * @param int $line Line on which the error happened
rlm@3 109 * @param int $col Column on which the error happened
rlm@3 110 */
rlm@3 111 private function HandleError($code, $line, $col)
rlm@3 112 {
rlm@3 113 trigger_error('XML Parsing Error at '.$line.':'.$col.'. Error '.$code.': '.xml_error_string($code));
rlm@3 114 }
rlm@3 115
rlm@3 116
rlm@3 117 /**
rlm@3 118 * Gets the XML output of the PHP structure within $this->document
rlm@3 119 *
rlm@3 120 * @return string
rlm@3 121 */
rlm@3 122 public function GenerateXML()
rlm@3 123 {
rlm@3 124 return $this->document->GetXML();
rlm@3 125 }
rlm@3 126
rlm@3 127 /**
rlm@3 128 * Gets the reference to the current direct parent
rlm@3 129 *
rlm@3 130 * @return object
rlm@3 131 */
rlm@3 132 private function GetStackLocation()
rlm@3 133 {
rlm@3 134 //Returns the reference to the current direct parent
rlm@3 135 return end($this->stack);
rlm@3 136 }
rlm@3 137
rlm@3 138 /**
rlm@3 139 * Handler function for the start of a tag
rlm@3 140 *
rlm@3 141 * @param resource $parser
rlm@3 142 * @param string $name
rlm@3 143 * @param array $attrs
rlm@3 144 */
rlm@3 145 private function StartElement($parser, $name, $attrs = array())
rlm@3 146 {
rlm@3 147 //Make the name of the tag lower case
rlm@3 148 $name = strtolower($name);
rlm@3 149
rlm@3 150 //Check to see if tag is root-level
rlm@3 151 if (count($this->stack) == 0)
rlm@3 152 {
rlm@3 153 //If so, set the document as the current tag
rlm@3 154 $this->document = new XMLTag($name, $attrs);
rlm@3 155
rlm@3 156 //And start out the stack with the document tag
rlm@3 157 $this->stack = array(&$this->document);
rlm@3 158 }
rlm@3 159 //If it isn't root level, use the stack to find the parent
rlm@3 160 else
rlm@3 161 {
rlm@3 162 //Get the reference to the current direct parent
rlm@3 163 $parent = $this->GetStackLocation();
rlm@3 164
rlm@3 165 $parent->AddChild($name, $attrs, count($this->stack), $this->cleanTagNames);
rlm@3 166
rlm@3 167 //If the cleanTagName feature is on, clean the tag names
rlm@3 168 if($this->cleanTagNames)
rlm@3 169 $name = str_replace(array(':', '-'), '_', $name);
rlm@3 170
rlm@3 171 //Update the stack
rlm@3 172 $this->stack[] = end($parent->$name);
rlm@3 173 }
rlm@3 174 }
rlm@3 175
rlm@3 176 /**
rlm@3 177 * Handler function for the end of a tag
rlm@3 178 *
rlm@3 179 * @param resource $parser
rlm@3 180 * @param string $name
rlm@3 181 */
rlm@3 182 private function EndElement($parser, $name)
rlm@3 183 {
rlm@3 184 //Update stack by removing the end value from it as the parent
rlm@3 185 array_pop($this->stack);
rlm@3 186 }
rlm@3 187
rlm@3 188 /**
rlm@3 189 * Handler function for the character data within a tag
rlm@3 190 *
rlm@3 191 * @param resource $parser
rlm@3 192 * @param string $data
rlm@3 193 */
rlm@3 194 private function CharacterData($parser, $data)
rlm@3 195 {
rlm@3 196 //Get the reference to the current parent object
rlm@3 197 $tag = $this->GetStackLocation();
rlm@3 198
rlm@3 199 //Assign data to it
rlm@3 200 $tag->tagData .= trim($data);
rlm@3 201 }
rlm@3 202 }
rlm@3 203
rlm@3 204
rlm@3 205 /**
rlm@3 206 * XML Tag Object (php5)
rlm@3 207 *
rlm@3 208 * This object stores all of the direct children of itself in the $children array. They are also stored by
rlm@3 209 * type as arrays. So, if, for example, this tag had 2 <font> tags as children, there would be a class member
rlm@3 210 * called $font created as an array. $font[0] would be the first font tag, and $font[1] would be the second.
rlm@3 211 *
rlm@3 212 * To loop through all of the direct children of this object, the $children member should be used.
rlm@3 213 *
rlm@3 214 * To loop through all of the direct children of a specific tag for this object, it is probably easier
rlm@3 215 * to use the arrays of the specific tag names, as explained above.
rlm@3 216 *
rlm@3 217 * @author Adam A. Flynn <adamaflynn@criticaldevelopment.net>
rlm@3 218 * @copyright Copyright (c) 2005-2007, Adam A. Flynn
rlm@3 219 *
rlm@3 220 * @version 1.3.0
rlm@3 221 */
rlm@3 222 class XMLTag
rlm@3 223 {
rlm@3 224 /**
rlm@3 225 * Array with the attributes of this XML tag
rlm@3 226 *
rlm@3 227 * @var array
rlm@3 228 */
rlm@3 229 public $tagAttrs;
rlm@3 230
rlm@3 231 /**
rlm@3 232 * The name of the tag
rlm@3 233 *
rlm@3 234 * @var string
rlm@3 235 */
rlm@3 236 public $tagName;
rlm@3 237
rlm@3 238 /**
rlm@3 239 * The data the tag contains
rlm@3 240 *
rlm@3 241 * So, if the tag doesn't contain child tags, and just contains a string, it would go here
rlm@3 242 *
rlm@3 243 * @var stat
rlm@3 244 */
rlm@3 245 public $tagData;
rlm@3 246
rlm@3 247 /**
rlm@3 248 * Array of references to the objects of all direct children of this XML object
rlm@3 249 *
rlm@3 250 * @var array
rlm@3 251 */
rlm@3 252 public $tagChildren;
rlm@3 253
rlm@3 254 /**
rlm@3 255 * The number of parents this XML object has (number of levels from this tag to the root tag)
rlm@3 256 *
rlm@3 257 * Used presently only to set the number of tabs when outputting XML
rlm@3 258 *
rlm@3 259 * @var int
rlm@3 260 */
rlm@3 261 public $tagParents;
rlm@3 262
rlm@3 263 /**
rlm@3 264 * Constructor, sets up all the default values
rlm@3 265 *
rlm@3 266 * @param string $name
rlm@3 267 * @param array $attrs
rlm@3 268 * @param int $parents
rlm@3 269 * @return XMLTag
rlm@3 270 */
rlm@3 271 function __construct($name, $attrs = array(), $parents = 0)
rlm@3 272 {
rlm@3 273 //Make the keys of the attr array lower case, and store the value
rlm@3 274 $this->tagAttrs = array_change_key_case($attrs, CASE_LOWER);
rlm@3 275
rlm@3 276 //Make the name lower case and store the value
rlm@3 277 $this->tagName = strtolower($name);
rlm@3 278
rlm@3 279 //Set the number of parents
rlm@3 280 $this->tagParents = $parents;
rlm@3 281
rlm@3 282 //Set the types for children and data
rlm@3 283 $this->tagChildren = array();
rlm@3 284 $this->tagData = '';
rlm@3 285 }
rlm@3 286
rlm@3 287 /**
rlm@3 288 * Adds a direct child to this object
rlm@3 289 *
rlm@3 290 * @param string $name
rlm@3 291 * @param array $attrs
rlm@3 292 * @param int $parents
rlm@3 293 * @param bool $cleanTagName
rlm@3 294 */
rlm@3 295 public function AddChild($name, $attrs, $parents, $cleanTagName = true)
rlm@3 296 {
rlm@3 297 //If the tag is a reserved name, output an error
rlm@3 298 if(in_array($name, array('tagChildren', 'tagAttrs', 'tagParents', 'tagData', 'tagName')))
rlm@3 299 {
rlm@3 300 trigger_error('You have used a reserved name as the name of an XML tag. Please consult the documentation (http://www.criticaldevelopment.net/xml/) and rename the tag named "'.$name.'" to something other than a reserved name.', E_USER_ERROR);
rlm@3 301
rlm@3 302 return;
rlm@3 303 }
rlm@3 304
rlm@3 305 //Create the child object itself
rlm@3 306 $child = new XMLTag($name, $attrs, $parents);
rlm@3 307
rlm@3 308 //If the cleanTagName feature is on, replace colons and dashes with underscores
rlm@3 309 if($cleanTagName)
rlm@3 310 $name = str_replace(array(':', '-'), '_', $name);
rlm@3 311
rlm@3 312 //Toss up a notice if someone's trying to to use a colon or dash in a tag name
rlm@3 313 elseif(strstr($name, ':') || strstr($name, '-'))
rlm@3 314 trigger_error('Your tag named "'.$name.'" contains either a dash or a colon. Neither of these characters are friendly with PHP variable names, and, as such, you may have difficulty accessing them. You might want to think about enabling the cleanTagName feature (pass true as the second argument of the XMLParser constructor). For more details, see http://www.criticaldevelopment.net/xml/', E_USER_NOTICE);
rlm@3 315
rlm@3 316 //If there is no array already set for the tag name being added,
rlm@3 317 //create an empty array for it
rlm@3 318 if(!isset($this->$name))
rlm@3 319 $this->$name = array();
rlm@3 320
rlm@3 321 //Add the reference of it to the end of an array member named for the tag's name
rlm@3 322 $this->{$name}[] = &$child;
rlm@3 323
rlm@3 324 //Add the reference to the children array member
rlm@3 325 $this->tagChildren[] = &$child;
rlm@3 326
rlm@3 327 //Return a reference to this object for the stack
rlm@3 328 return $this;
rlm@3 329 }
rlm@3 330
rlm@3 331 /**
rlm@3 332 * Returns the string of the XML document which would be generated from this object
rlm@3 333 *
rlm@3 334 * This function works recursively, so it gets the XML of itself and all of its children, which
rlm@3 335 * in turn gets the XML of all their children, which in turn gets the XML of all thier children,
rlm@3 336 * and so on. So, if you call GetXML from the document root object, it will return a string for
rlm@3 337 * the XML of the entire document.
rlm@3 338 *
rlm@3 339 * This function does not, however, return a DTD or an XML version/encoding tag. That should be
rlm@3 340 * handled by XMLParser::GetXML()
rlm@3 341 *
rlm@3 342 * @return string
rlm@3 343 */
rlm@3 344 public function GetXML()
rlm@3 345 {
rlm@3 346 //Start a new line, indent by the number indicated in $this->parents, add a <, and add the name of the tag
rlm@3 347 $out = "\n".str_repeat("\t", $this->tagParents).'<'.$this->tagName;
rlm@3 348
rlm@3 349 //For each attribute, add attr="value"
rlm@3 350 foreach($this->tagAttrs as $attr => $value)
rlm@3 351 $out .= ' '.$attr.'="'.$value.'"';
rlm@3 352
rlm@3 353 //If there are no children and it contains no data, end it off with a />
rlm@3 354 if(empty($this->tagChildren) && empty($this->tagData))
rlm@3 355 $out .= " />";
rlm@3 356
rlm@3 357 //Otherwise...
rlm@3 358 else
rlm@3 359 {
rlm@3 360 //If there are children
rlm@3 361 if(!empty($this->tagChildren))
rlm@3 362 {
rlm@3 363 //Close off the start tag
rlm@3 364 $out .= '>';
rlm@3 365
rlm@3 366 //For each child, call the GetXML function (this will ensure that all children are added recursively)
rlm@3 367 foreach($this->tagChildren as $child)
rlm@3 368 $out .= $child->GetXML();
rlm@3 369
rlm@3 370 //Add the newline and indentation to go along with the close tag
rlm@3 371 $out .= "\n".str_repeat("\t", $this->tagParents);
rlm@3 372 }
rlm@3 373
rlm@3 374 //If there is data, close off the start tag and add the data
rlm@3 375 elseif(!empty($this->tagData))
rlm@3 376 $out .= '>'.$this->tagData;
rlm@3 377
rlm@3 378 //Add the end tag
rlm@3 379 $out .= '</'.$this->tagName.'>';
rlm@3 380 }
rlm@3 381
rlm@3 382 //Return the final output
rlm@3 383 return $out;
rlm@3 384 }
rlm@3 385
rlm@3 386 /**
rlm@3 387 * Deletes this tag's child with a name of $childName and an index
rlm@3 388 * of $childIndex
rlm@3 389 *
rlm@3 390 * @param string $childName
rlm@3 391 * @param int $childIndex
rlm@3 392 */
rlm@3 393 public function Delete($childName, $childIndex = 0)
rlm@3 394 {
rlm@3 395 //Delete all of the children of that child
rlm@3 396 $this->{$childName}[$childIndex]->DeleteChildren();
rlm@3 397
rlm@3 398 //Destroy the child's value
rlm@3 399 $this->{$childName}[$childIndex] = null;
rlm@3 400
rlm@3 401 //Remove the child's name from the named array
rlm@3 402 unset($this->{$childName}[$childIndex]);
rlm@3 403
rlm@3 404 //Loop through the tagChildren array and remove any null
rlm@3 405 //values left behind from the above operation
rlm@3 406 for($x = 0; $x < count($this->tagChildren); $x ++)
rlm@3 407 {
rlm@3 408 if(is_null($this->tagChildren[$x]))
rlm@3 409 unset($this->tagChildren[$x]);
rlm@3 410 }
rlm@3 411 }
rlm@3 412
rlm@3 413 /**
rlm@3 414 * Removes all of the children of this tag in both name and value
rlm@3 415 */
rlm@3 416 private function DeleteChildren()
rlm@3 417 {
rlm@3 418 //Loop through all child tags
rlm@3 419 for($x = 0; $x < count($this->tagChildren); $x ++)
rlm@3 420 {
rlm@3 421 //Do this recursively
rlm@3 422 $this->tagChildren[$x]->DeleteChildren();
rlm@3 423
rlm@3 424 //Delete the name and value
rlm@3 425 $this->tagChildren[$x] = null;
rlm@3 426 unset($this->tagChildren[$x]);
rlm@3 427 }
rlm@3 428 }
rlm@3 429 }
rlm@3 430 ?>