comparison e2gallerypro/php/xmlparse.v4.php @ 3:3f6b44aa6b35 judyates

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