annotate e2gallerypro/php/xmlparse.v4.php @ 8:1b77e8657088 judyates

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