view e2gallerypro/php/xmlparse.v5.php @ 13:f16185fe9ed9 judyates

[svn r14]
author rlm
date Mon, 12 Apr 2010 03:30:00 -0400
parents 3f6b44aa6b35
children
line wrap: on
line source
1 <?php
2 /**
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (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 of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 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. Flynn
23 *
24 * @version 1.3.0
25 */
26 class XMLParser
27 {
28 /**
29 * The XML parser
30 *
31 * @var resource
32 */
33 private $parser;
35 /**
36 * The XML document
37 *
38 * @var string
39 */
40 private $xml;
42 /**
43 * Document tag
44 *
45 * @var object
46 */
47 public $document;
49 /**
50 * Current object depth
51 *
52 * @var array
53 */
54 private $stack;
56 /**
57 * Whether or not to replace dashes and colons in tag
58 * names with underscores.
59 *
60 * @var bool
61 */
62 private $cleanTagNames;
65 /**
66 * Constructor. Loads XML document.
67 *
68 * @param string $xml The string of the XML document
69 * @return XMLParser
70 */
71 function __construct($xml = '', $cleanTagNames = true)
72 {
73 //Load XML document
74 $this->xml = $xml;
76 //Set stack to an array
77 $this->stack = array();
79 //Set whether or not to clean tag names
80 $this->cleanTagNames = $cleanTagNames;
81 }
83 /**
84 * Initiates and runs PHP's XML parser
85 */
86 public function Parse()
87 {
88 //Create the parser resource
89 $this->parser = xml_parser_create();
91 //Set the handlers
92 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 handling
97 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 parser
101 xml_parser_free($this->parser);
102 }
104 /**
105 * Handles an XML parsing error
106 *
107 * @param int $code XML Error Code
108 * @param int $line Line on which the error happened
109 * @param int $col Column on which the error happened
110 */
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->document
119 *
120 * @return string
121 */
122 public function GenerateXML()
123 {
124 return $this->document->GetXML();
125 }
127 /**
128 * Gets the reference to the current direct parent
129 *
130 * @return object
131 */
132 private function GetStackLocation()
133 {
134 //Returns the reference to the current direct parent
135 return end($this->stack);
136 }
138 /**
139 * Handler function for the start of a tag
140 *
141 * @param resource $parser
142 * @param string $name
143 * @param array $attrs
144 */
145 private function StartElement($parser, $name, $attrs = array())
146 {
147 //Make the name of the tag lower case
148 $name = strtolower($name);
150 //Check to see if tag is root-level
151 if (count($this->stack) == 0)
152 {
153 //If so, set the document as the current tag
154 $this->document = new XMLTag($name, $attrs);
156 //And start out the stack with the document tag
157 $this->stack = array(&$this->document);
158 }
159 //If it isn't root level, use the stack to find the parent
160 else
161 {
162 //Get the reference to the current direct parent
163 $parent = $this->GetStackLocation();
165 $parent->AddChild($name, $attrs, count($this->stack), $this->cleanTagNames);
167 //If the cleanTagName feature is on, clean the tag names
168 if($this->cleanTagNames)
169 $name = str_replace(array(':', '-'), '_', $name);
171 //Update the stack
172 $this->stack[] = end($parent->$name);
173 }
174 }
176 /**
177 * Handler function for the end of a tag
178 *
179 * @param resource $parser
180 * @param string $name
181 */
182 private function EndElement($parser, $name)
183 {
184 //Update stack by removing the end value from it as the parent
185 array_pop($this->stack);
186 }
188 /**
189 * Handler function for the character data within a tag
190 *
191 * @param resource $parser
192 * @param string $data
193 */
194 private function CharacterData($parser, $data)
195 {
196 //Get the reference to the current parent object
197 $tag = $this->GetStackLocation();
199 //Assign data to it
200 $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 by
209 * type as arrays. So, if, for example, this tag had 2 <font> tags as children, there would be a class member
210 * 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 easier
215 * 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. Flynn
219 *
220 * @version 1.3.0
221 */
222 class XMLTag
223 {
224 /**
225 * Array with the attributes of this XML tag
226 *
227 * @var array
228 */
229 public $tagAttrs;
231 /**
232 * The name of the tag
233 *
234 * @var string
235 */
236 public $tagName;
238 /**
239 * The data the tag contains
240 *
241 * So, if the tag doesn't contain child tags, and just contains a string, it would go here
242 *
243 * @var stat
244 */
245 public $tagData;
247 /**
248 * Array of references to the objects of all direct children of this XML object
249 *
250 * @var array
251 */
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 XML
258 *
259 * @var int
260 */
261 public $tagParents;
263 /**
264 * Constructor, sets up all the default values
265 *
266 * @param string $name
267 * @param array $attrs
268 * @param int $parents
269 * @return XMLTag
270 */
271 function __construct($name, $attrs = array(), $parents = 0)
272 {
273 //Make the keys of the attr array lower case, and store the value
274 $this->tagAttrs = array_change_key_case($attrs, CASE_LOWER);
276 //Make the name lower case and store the value
277 $this->tagName = strtolower($name);
279 //Set the number of parents
280 $this->tagParents = $parents;
282 //Set the types for children and data
283 $this->tagChildren = array();
284 $this->tagData = '';
285 }
287 /**
288 * Adds a direct child to this object
289 *
290 * @param string $name
291 * @param array $attrs
292 * @param int $parents
293 * @param bool $cleanTagName
294 */
295 public function AddChild($name, $attrs, $parents, $cleanTagName = true)
296 {
297 //If the tag is a reserved name, output an error
298 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 itself
306 $child = new XMLTag($name, $attrs, $parents);
308 //If the cleanTagName feature is on, replace colons and dashes with underscores
309 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 name
313 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 it
318 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 name
322 $this->{$name}[] = &$child;
324 //Add the reference to the children array member
325 $this->tagChildren[] = &$child;
327 //Return a reference to this object for the stack
328 return $this;
329 }
331 /**
332 * Returns the string of the XML document which would be generated from this object
333 *
334 * This function works recursively, so it gets the XML of itself and all of its children, which
335 * 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 for
337 * the XML of the entire document.
338 *
339 * This function does not, however, return a DTD or an XML version/encoding tag. That should be
340 * handled by XMLParser::GetXML()
341 *
342 * @return string
343 */
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 tag
347 $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 else
359 {
360 //If there are children
361 if(!empty($this->tagChildren))
362 {
363 //Close off the start tag
364 $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 tag
371 $out .= "\n".str_repeat("\t", $this->tagParents);
372 }
374 //If there is data, close off the start tag and add the data
375 elseif(!empty($this->tagData))
376 $out .= '>'.$this->tagData;
378 //Add the end tag
379 $out .= '</'.$this->tagName.'>';
380 }
382 //Return the final output
383 return $out;
384 }
386 /**
387 * Deletes this tag's child with a name of $childName and an index
388 * of $childIndex
389 *
390 * @param string $childName
391 * @param int $childIndex
392 */
393 public function Delete($childName, $childIndex = 0)
394 {
395 //Delete all of the children of that child
396 $this->{$childName}[$childIndex]->DeleteChildren();
398 //Destroy the child's value
399 $this->{$childName}[$childIndex] = null;
401 //Remove the child's name from the named array
402 unset($this->{$childName}[$childIndex]);
404 //Loop through the tagChildren array and remove any null
405 //values left behind from the above operation
406 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 value
415 */
416 private function DeleteChildren()
417 {
418 //Loop through all child tags
419 for($x = 0; $x < count($this->tagChildren); $x ++)
420 {
421 //Do this recursively
422 $this->tagChildren[$x]->DeleteChildren();
424 //Delete the name and value
425 $this->tagChildren[$x] = null;
426 unset($this->tagChildren[$x]);
427 }
428 }
429 }
430 ?>