Mercurial > judyates
view e2gallerypro/php/xmlparse.v5.php @ 20:1038db2374ec judyates
change address
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 08 Sep 2013 00:47:09 -0400 |
parents | 3f6b44aa6b35 |
children |
line wrap: on
line source
1 <?php2 /**3 This program is free software; you can redistribute it and/or modify4 it under the terms of the GNU General Public License as published by5 the Free Software Foundation; either version 2 of the License, or6 (at your option) any later version.8 This program is distributed in the hope that it will be useful,9 but WITHOUT ANY WARRANTY; without even the implied warranty of10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11 GNU General Public License for more details.13 For support, please visit http://www.criticaldevelopment.net/xml/14 */16 /**17 * XML Parser Class (php5)18 *19 * Parses an XML document into an object structure much like the SimpleXML extension.20 *21 * @author Adam A. Flynn <adamaflynn@criticaldevelopment.net>22 * @copyright Copyright (c) 2005-2007, Adam A. Flynn23 *24 * @version 1.3.025 */26 class XMLParser27 {28 /**29 * The XML parser30 *31 * @var resource32 */33 private $parser;35 /**36 * The XML document37 *38 * @var string39 */40 private $xml;42 /**43 * Document tag44 *45 * @var object46 */47 public $document;49 /**50 * Current object depth51 *52 * @var array53 */54 private $stack;56 /**57 * Whether or not to replace dashes and colons in tag58 * names with underscores.59 *60 * @var bool61 */62 private $cleanTagNames;65 /**66 * Constructor. Loads XML document.67 *68 * @param string $xml The string of the XML document69 * @return XMLParser70 */71 function __construct($xml = '', $cleanTagNames = true)72 {73 //Load XML document74 $this->xml = $xml;76 //Set stack to an array77 $this->stack = array();79 //Set whether or not to clean tag names80 $this->cleanTagNames = $cleanTagNames;81 }83 /**84 * Initiates and runs PHP's XML parser85 */86 public function Parse()87 {88 //Create the parser resource89 $this->parser = xml_parser_create();91 //Set the handlers92 xml_set_object($this->parser, $this);93 xml_set_element_handler($this->parser, 'StartElement', 'EndElement');94 xml_set_character_data_handler($this->parser, 'CharacterData');96 //Error handling97 if (!xml_parse($this->parser, $this->xml))98 $this->HandleError(xml_get_error_code($this->parser), xml_get_current_line_number($this->parser), xml_get_current_column_number($this->parser));100 //Free the parser101 xml_parser_free($this->parser);102 }104 /**105 * Handles an XML parsing error106 *107 * @param int $code XML Error Code108 * @param int $line Line on which the error happened109 * @param int $col Column on which the error happened110 */111 private function HandleError($code, $line, $col)112 {113 trigger_error('XML Parsing Error at '.$line.':'.$col.'. Error '.$code.': '.xml_error_string($code));114 }117 /**118 * Gets the XML output of the PHP structure within $this->document119 *120 * @return string121 */122 public function GenerateXML()123 {124 return $this->document->GetXML();125 }127 /**128 * Gets the reference to the current direct parent129 *130 * @return object131 */132 private function GetStackLocation()133 {134 //Returns the reference to the current direct parent135 return end($this->stack);136 }138 /**139 * Handler function for the start of a tag140 *141 * @param resource $parser142 * @param string $name143 * @param array $attrs144 */145 private function StartElement($parser, $name, $attrs = array())146 {147 //Make the name of the tag lower case148 $name = strtolower($name);150 //Check to see if tag is root-level151 if (count($this->stack) == 0)152 {153 //If so, set the document as the current tag154 $this->document = new XMLTag($name, $attrs);156 //And start out the stack with the document tag157 $this->stack = array(&$this->document);158 }159 //If it isn't root level, use the stack to find the parent160 else161 {162 //Get the reference to the current direct parent163 $parent = $this->GetStackLocation();165 $parent->AddChild($name, $attrs, count($this->stack), $this->cleanTagNames);167 //If the cleanTagName feature is on, clean the tag names168 if($this->cleanTagNames)169 $name = str_replace(array(':', '-'), '_', $name);171 //Update the stack172 $this->stack[] = end($parent->$name);173 }174 }176 /**177 * Handler function for the end of a tag178 *179 * @param resource $parser180 * @param string $name181 */182 private function EndElement($parser, $name)183 {184 //Update stack by removing the end value from it as the parent185 array_pop($this->stack);186 }188 /**189 * Handler function for the character data within a tag190 *191 * @param resource $parser192 * @param string $data193 */194 private function CharacterData($parser, $data)195 {196 //Get the reference to the current parent object197 $tag = $this->GetStackLocation();199 //Assign data to it200 $tag->tagData .= trim($data);201 }202 }205 /**206 * XML Tag Object (php5)207 *208 * This object stores all of the direct children of itself in the $children array. They are also stored by209 * type as arrays. So, if, for example, this tag had 2 <font> tags as children, there would be a class member210 * called $font created as an array. $font[0] would be the first font tag, and $font[1] would be the second.211 *212 * To loop through all of the direct children of this object, the $children member should be used.213 *214 * To loop through all of the direct children of a specific tag for this object, it is probably easier215 * to use the arrays of the specific tag names, as explained above.216 *217 * @author Adam A. Flynn <adamaflynn@criticaldevelopment.net>218 * @copyright Copyright (c) 2005-2007, Adam A. Flynn219 *220 * @version 1.3.0221 */222 class XMLTag223 {224 /**225 * Array with the attributes of this XML tag226 *227 * @var array228 */229 public $tagAttrs;231 /**232 * The name of the tag233 *234 * @var string235 */236 public $tagName;238 /**239 * The data the tag contains240 *241 * So, if the tag doesn't contain child tags, and just contains a string, it would go here242 *243 * @var stat244 */245 public $tagData;247 /**248 * Array of references to the objects of all direct children of this XML object249 *250 * @var array251 */252 public $tagChildren;254 /**255 * The number of parents this XML object has (number of levels from this tag to the root tag)256 *257 * Used presently only to set the number of tabs when outputting XML258 *259 * @var int260 */261 public $tagParents;263 /**264 * Constructor, sets up all the default values265 *266 * @param string $name267 * @param array $attrs268 * @param int $parents269 * @return XMLTag270 */271 function __construct($name, $attrs = array(), $parents = 0)272 {273 //Make the keys of the attr array lower case, and store the value274 $this->tagAttrs = array_change_key_case($attrs, CASE_LOWER);276 //Make the name lower case and store the value277 $this->tagName = strtolower($name);279 //Set the number of parents280 $this->tagParents = $parents;282 //Set the types for children and data283 $this->tagChildren = array();284 $this->tagData = '';285 }287 /**288 * Adds a direct child to this object289 *290 * @param string $name291 * @param array $attrs292 * @param int $parents293 * @param bool $cleanTagName294 */295 public function AddChild($name, $attrs, $parents, $cleanTagName = true)296 {297 //If the tag is a reserved name, output an error298 if(in_array($name, array('tagChildren', 'tagAttrs', 'tagParents', 'tagData', 'tagName')))299 {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);302 return;303 }305 //Create the child object itself306 $child = new XMLTag($name, $attrs, $parents);308 //If the cleanTagName feature is on, replace colons and dashes with underscores309 if($cleanTagName)310 $name = str_replace(array(':', '-'), '_', $name);312 //Toss up a notice if someone's trying to to use a colon or dash in a tag name313 elseif(strstr($name, ':') || strstr($name, '-'))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);316 //If there is no array already set for the tag name being added,317 //create an empty array for it318 if(!isset($this->$name))319 $this->$name = array();321 //Add the reference of it to the end of an array member named for the tag's name322 $this->{$name}[] = &$child;324 //Add the reference to the children array member325 $this->tagChildren[] = &$child;327 //Return a reference to this object for the stack328 return $this;329 }331 /**332 * Returns the string of the XML document which would be generated from this object333 *334 * This function works recursively, so it gets the XML of itself and all of its children, which335 * in turn gets the XML of all their children, which in turn gets the XML of all thier children,336 * and so on. So, if you call GetXML from the document root object, it will return a string for337 * the XML of the entire document.338 *339 * This function does not, however, return a DTD or an XML version/encoding tag. That should be340 * handled by XMLParser::GetXML()341 *342 * @return string343 */344 public function GetXML()345 {346 //Start a new line, indent by the number indicated in $this->parents, add a <, and add the name of the tag347 $out = "\n".str_repeat("\t", $this->tagParents).'<'.$this->tagName;349 //For each attribute, add attr="value"350 foreach($this->tagAttrs as $attr => $value)351 $out .= ' '.$attr.'="'.$value.'"';353 //If there are no children and it contains no data, end it off with a />354 if(empty($this->tagChildren) && empty($this->tagData))355 $out .= " />";357 //Otherwise...358 else359 {360 //If there are children361 if(!empty($this->tagChildren))362 {363 //Close off the start tag364 $out .= '>';366 //For each child, call the GetXML function (this will ensure that all children are added recursively)367 foreach($this->tagChildren as $child)368 $out .= $child->GetXML();370 //Add the newline and indentation to go along with the close tag371 $out .= "\n".str_repeat("\t", $this->tagParents);372 }374 //If there is data, close off the start tag and add the data375 elseif(!empty($this->tagData))376 $out .= '>'.$this->tagData;378 //Add the end tag379 $out .= '</'.$this->tagName.'>';380 }382 //Return the final output383 return $out;384 }386 /**387 * Deletes this tag's child with a name of $childName and an index388 * of $childIndex389 *390 * @param string $childName391 * @param int $childIndex392 */393 public function Delete($childName, $childIndex = 0)394 {395 //Delete all of the children of that child396 $this->{$childName}[$childIndex]->DeleteChildren();398 //Destroy the child's value399 $this->{$childName}[$childIndex] = null;401 //Remove the child's name from the named array402 unset($this->{$childName}[$childIndex]);404 //Loop through the tagChildren array and remove any null405 //values left behind from the above operation406 for($x = 0; $x < count($this->tagChildren); $x ++)407 {408 if(is_null($this->tagChildren[$x]))409 unset($this->tagChildren[$x]);410 }411 }413 /**414 * Removes all of the children of this tag in both name and value415 */416 private function DeleteChildren()417 {418 //Loop through all child tags419 for($x = 0; $x < count($this->tagChildren); $x ++)420 {421 //Do this recursively422 $this->tagChildren[$x]->DeleteChildren();424 //Delete the name and value425 $this->tagChildren[$x] = null;426 unset($this->tagChildren[$x]);427 }428 }429 }430 ?>