CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

XMLParser.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: SPT--XMLParser.php
4 #
5 # METHODS PROVIDED:
6 # XMLParser()
7 # - constructor
8 # SomeMethod($SomeParameter, $AnotherParameter)
9 # - short description of method
10 #
11 # AUTHOR: Edward Almasy
12 #
13 # Part of the Scout Portal Toolkit
14 # Copyright 2005 Internet Scout Project
15 # http://scout.wisc.edu
16 #
17
18 class XMLParser {
19
20 # ---- PUBLIC INTERFACE --------------------------------------------------
21
22 # object constructor
23 function XMLParser($Encoding="UTF-8")
24 {
25 # set default debug output level
26 $this->DebugLevel = 0;
27
28 # create XML parser and tell it about our methods
29 $this->Parser = xml_parser_create($Encoding);
30 xml_set_object($this->Parser, $this);
31 xml_set_element_handler($this->Parser, "OpenTag", "CloseTag");
32 xml_set_character_data_handler($this->Parser, "ReceiveData");
33
34 # initialize tag storage arrays
35 $this->TagNames = array();
36 $this->TagAttribs = array();
37 $this->TagData = array();
38 $this->TagParents = array();
39
40 # initialize indexes for parsing and retrieving data
41 $this->CurrentParseIndex = -1;
42 $this->CurrentSeekIndex = -1;
43 $this->NameKeyCache = array();
44 }
45
46 # parse text stream and store result
47 function ParseText($Text, $LastTextToParse = TRUE)
48 {
49 # pass text to PHP XML parser
50 xml_parse($this->Parser, $Text, $LastTextToParse);
51 }
52
53 # move current tag pointer to specified item (returns NULL on failure)
54 # (args may be tag names or indexes)
55 function SeekTo()
56 {
57 # perform seek based on arguments passed by caller
58 $SeekResult = $this->PerformSeek(func_get_args(), TRUE);
59
60 # if seek successful
61 if ($SeekResult !== NULL)
62 {
63 # retrieve item count at seek location
64 $ItemCount = count($this->CurrentItemList);
65 }
66 else
67 {
68 # return null value to indicate that seek failed
69 $ItemCount = NULL;
70 }
71
72 # return count of tags found at requested location
73 if ($this->DebugLevel > 0)
74 {
75 print("XMLParser->SeekTo(");
76 $Sep = "";
77 $DbugArgList = "";
78 foreach (func_get_args() as $Arg)
79 {
80 $DbugArgList .= $Sep."\"".$Arg."\"";
81 $Sep = ", ";
82 }
83 print($DbugArgList.") returned ".intval($ItemCount)." items starting at index ".$this->CurrentSeekIndex."\n");
84 }
85 return $ItemCount;
86 }
87
88 # move seek pointer up one level (returns tag name or NULL if no parent)
89 function SeekToParent()
90 {
91 # if we are not at the root of the tree
92 if ($this->CurrentSeekIndex >= 0)
93 {
94 # move up one level in tree
95 $this->CurrentSeekIndex = $this->TagParents[$this->CurrentSeekIndex];
96
97 # clear item list
98 unset($this->CurrentItemList);
99
100 # return name of new tag to caller
101 $Result = $this->TagNames[$this->CurrentSeekIndex];
102 }
103 else
104 {
105 # return NULL indicating that no parent was found
106 $Result = NULL;
107 }
108
109 # return result to caller
110 if ($this->DebugLevel > 0) { print("XMLParser->SeekToParent() returned ".$Result."<br>\n"); }
111 return $Result;
112 }
113
114 # move seek pointer to first child of current tag (returns tag name or NULL if no children)
115 function SeekToChild($ChildIndex = 0)
116 {
117 # look for tags with current tag as parent
118 $ChildTags = array_keys($this->TagParents, $this->CurrentSeekIndex);
119
120 # if child tag was found with requested index
121 if (isset($ChildTags[$ChildIndex]))
122 {
123 # set current seek index to child
124 $this->CurrentSeekIndex = $ChildTags[$ChildIndex];
125
126 # clear item list info
127 unset($this->CurrentItemList);
128
129 # return name of new tag to caller
130 $Result = $this->TagNames[$this->CurrentSeekIndex];
131 }
132 else
133 {
134 # return NULL indicating that no children were found
135 $Result = NULL;
136 }
137
138 # return result to caller
139 if ($this->DebugLevel > 0) { print("XMLParser->SeekToChild() returned ".$Result."<br>\n"); }
140 return $Result;
141 }
142
143 # move seek pointer to root of tree
144 function SeekToRoot()
145 {
146 $this->CurrentSeekIndex = -1;
147 }
148
149 # move to next tag at current level (returns tag name or NULL if no next)
150 function NextTag()
151 {
152 # get list of tags with same parent as this tag
153 $LevelTags = array_keys($this->TagParents,
154 $this->TagParents[$this->CurrentSeekIndex]);
155
156 # find position of next tag in list
157 $NextTagPosition = array_search($this->CurrentSeekIndex, $LevelTags) + 1;
158
159 # if there is a next tag
160 if (count($LevelTags) > $NextTagPosition)
161 {
162 # move seek pointer to next tag at this level
163 $this->CurrentSeekIndex = $LevelTags[$NextTagPosition];
164
165 # rebuild item list
166
167 # return name of tag at new position to caller
168 return $this->TagNames[$this->CurrentSeekIndex];
169 }
170 else
171 {
172 # return NULL to caller to indicate no next tag
173 return NULL;
174 }
175 }
176
177 # move to next instance of current tag (returns index or NULL if no next)
178 function NextItem()
179 {
180 # set up item list if necessary
181 if (!isset($this->CurrentItemList)) { $this->RebuildItemList(); }
182
183 # if there are items left to move to
184 if ($this->CurrentItemIndex < ($this->CurrentItemCount - 1))
185 {
186 # move item pointer to next item
187 $this->CurrentItemIndex++;
188
189 # set current seek pointer to next item
190 $this->CurrentSeekIndex =
191 $this->CurrentItemList[$this->CurrentItemIndex];
192
193 # return new item index to caller
194 $Result = $this->CurrentItemIndex;
195 }
196 else
197 {
198 # return NULL value to caller to indicate failure
199 $Result = NULL;
200 }
201
202 # return result to caller
203 return $Result;
204 }
205
206 # move to previous instance of current tag (returns index or NULL on fail)
207 function PreviousItem()
208 {
209 # set up item list if necessary
210 if (!isset($this->CurrentItemList)) { $this->RebuildItemList(); }
211
212 # if we are not at the first item
213 if ($this->CurrentItemIndex > 0)
214 {
215 # move item pointer to previous item
216 $this->CurrentItemIndex--;
217
218 # set current seek pointer to next item
219 $this->CurrentSeekIndex =
220 $this->CurrentItemList[$this->CurrentItemIndex];
221
222 # return new item index to caller
223 return $this->CurrentItemIndex;
224 }
225 else
226 {
227 # return NULL value to caller to indicate failure
228 return NULL;
229 }
230 }
231
232 # retrieve tag name from current seek point
233 function GetTagName()
234 {
235 if (isset($this->TagNames[$this->CurrentSeekIndex]))
236 {
237 return $this->TagNames[$this->CurrentSeekIndex];
238 }
239 else
240 {
241 return NULL;
242 }
243 }
244
245 # retrieve data from current seek point
246 function GetData()
247 {
248 # assume that we will not be able to retrieve data
249 $Data = NULL;
250
251 # if arguments were supplied
252 if (func_num_args())
253 {
254 # retrieve index for specified point
255 $Index = $this->PerformSeek(func_get_args(), FALSE);
256
257 # if valid index was found
258 if ($Index !== NULL)
259 {
260 # retrieve data at index to be returned to caller
261 $Data = $this->TagData[$Index];
262 }
263 }
264 else
265 {
266 # if current seek index points to valid tag
267 if ($this->CurrentSeekIndex >= 0)
268 {
269 # retrieve data to be returned to caller
270 $Data = $this->TagData[$this->CurrentSeekIndex];
271 }
272 }
273
274 # return data to caller
275 if ($this->DebugLevel > 0)
276 {
277 print("XMLParser->GetData(");
278 if (func_num_args()) { $ArgString = ""; foreach (func_get_args() as $Arg) { $ArgString .= "\"".$Arg."\", "; } $ArgString = substr($ArgString, 0, strlen($ArgString) - 2); print($ArgString); }
279 print(") returned ".($Data ? "\"".$Data."\"" : "NULL")."<br>\n");
280 }
281 return $Data;
282 }
283
284 # retrieve specified attribute(s) from current seek point or specified point below
285 # (first arg is attribute name and optional subsequent args tell where to seek to)
286 # (returns NULL if no such attribute for current or specified tag)
287 function GetAttribute()
288 {
289 # retrieve attribute
290 $Args = func_get_args();
291 $Attrib = $this->PerformGetAttribute($Args, FALSE);
292
293 # return requested attribute to caller
294 if ($this->DebugLevel > 0) { print("XMLParser->GetAttribute() returned ".$Attrib."<br>\n"); }
295 return $Attrib;
296 }
297 function GetAttributes()
298 {
299 # retrieve attribute
300 $Args = func_get_args();
301 $Attribs = $this->PerformGetAttribute($Args, TRUE);
302
303 # return requested attribute to caller
304 if ($this->DebugLevel > 0) { print("XMLParser->GetAttributes() returned ".count($Attribs)." attributes<br>\n"); }
305 return $Attribs;
306 }
307
308
309 # ---- PRIVATE INTERFACE -------------------------------------------------
310
311 var $TagNames;
312 var $TagAttribs;
313 var $TagData;
314 var $TagParents;
315 var $CurrentParseIndex;
316 var $CurrentSeekIndex;
317 var $CurrentItemIndex;
318 var $CurrentItemList;
319 var $CurrentItemCount;
320 var $DebugLevel;
321 var $NameKeyCache;
322
323 # set current debug output level (0-9)
324 function SetDebugLevel($NewLevel)
325 {
326 $this->DebugLevel = $NewLevel;
327 }
328
329 # callback function for handling open tags
330 function OpenTag($Parser, $ElementName, $ElementAttribs)
331 {
332 # add new tag to list
333 $NewTagIndex = count($this->TagNames);
334 $this->TagNames[$NewTagIndex] = $ElementName;
335 $this->TagAttribs[$NewTagIndex] = $ElementAttribs;
336 $this->TagParents[$NewTagIndex] = $this->CurrentParseIndex;
337 $this->TagData[$NewTagIndex] = NULL;
338
339 # set current tag to new tag
340 $this->CurrentParseIndex = $NewTagIndex;
341 }
342
343 # callback function for receiving data between tags
344 function ReceiveData($Parser, $Data)
345 {
346 # add data to currently open tag
347 $this->TagData[$this->CurrentParseIndex] .= $Data;
348 }
349
350 # callback function for handling close tags
351 function CloseTag($Parser, $ElementName)
352 {
353 # if we have an open tag and closing tag matches currently open tag
354 if (($this->CurrentParseIndex >= 0)
355 && ($ElementName == $this->TagNames[$this->CurrentParseIndex]))
356 {
357 # set current tag to parent tag
358 $this->CurrentParseIndex = $this->TagParents[$this->CurrentParseIndex];
359 }
360 }
361
362 # perform seek to point in tag tree and update seek pointer (if requested)
363 function PerformSeek($SeekArgs, $MoveSeekPointer)
364 {
365 # for each tag name or index in argument list
366 $NewSeekIndex = $this->CurrentSeekIndex;
367 foreach ($SeekArgs as $Arg)
368 {
369 # if argument is string
370 if (is_string($Arg))
371 {
372 # look for tags with given name and current tag as parent
373 $Arg = strtoupper($Arg);
374 if (!isset($this->NameKeyCache[$Arg]))
375 {
376 $this->NameKeyCache[$Arg] = array_keys($this->TagNames, $Arg);
377 $TestArray = array_keys($this->TagNames, $Arg);
378 }
379 $ChildTags = array_keys($this->TagParents, $NewSeekIndex);
380 $NewItemList = array_values(
381 array_intersect($this->NameKeyCache[$Arg], $ChildTags));
382 $NewItemCount = count($NewItemList);
383
384 # if matching tag found
385 if ($NewItemCount > 0)
386 {
387 # update local seek index
388 $NewSeekIndex = $NewItemList[0];
389
390 # save new item index
391 $NewItemIndex = 0;
392 }
393 else
394 {
395 # report seek failure to caller
396 return NULL;
397 }
398 }
399 else
400 {
401 # look for tags with same name and same parent as current tag
402 $NameTags = array_keys($this->TagNames, $this->TagNames[$NewSeekIndex]);
403 $ChildTags = array_keys($this->TagParents, $this->TagParents[$NewSeekIndex]);
404 $NewItemList = array_values(array_intersect($NameTags, $ChildTags));
405 $NewItemCount = count($NewItemList);
406
407 # if enough matching tags were found to contain requested index
408 if ($NewItemCount > $Arg)
409 {
410 # update local seek index
411 $NewSeekIndex = $NewItemList[$Arg];
412
413 # save new item index
414 $NewItemIndex = $Arg;
415 }
416 else
417 {
418 # report seek failure to caller
419 return NULL;
420 }
421 }
422 }
423
424 # if caller requested that seek pointer be moved to reflect seek
425 if ($MoveSeekPointer)
426 {
427 # update seek index
428 $this->CurrentSeekIndex = $NewSeekIndex;
429
430 # update item index and list
431 $this->CurrentItemIndex = $NewItemIndex;
432 $this->CurrentItemList = $NewItemList;
433 $this->CurrentItemCount = $NewItemCount;
434 }
435
436 # return index of found seek
437 return $NewSeekIndex;
438 }
439
440 function PerformGetAttribute($Args, $GetMultiple)
441 {
442 # assume that we will not be able to retrieve attribute
443 $ReturnVal = NULL;
444
445 # retrieve attribute name and (possibly) seek arguments
446 if (!$GetMultiple)
447 {
448 $AttribName = strtoupper(array_shift($Args));
449 }
450
451 # if arguments were supplied
452 if (count($Args))
453 {
454 # retrieve index for specified point
455 $Index = $this->PerformSeek($Args, FALSE);
456
457 # if valid index was found
458 if ($Index !== NULL)
459 {
460 # if specified attribute exists
461 if (isset($this->TagAttribs[$Index][$AttribName]))
462 {
463 # retrieve attribute(s) at index to be returned to caller
464 if ($GetMultiple)
465 {
466 $ReturnVal = $this->TagAttribs[$Index];
467 }
468 else
469 {
470 $ReturnVal = $this->TagAttribs[$Index][$AttribName];
471 }
472 }
473 }
474 }
475 else
476 {
477 # if current seek index points to valid tag
478 if ($this->CurrentSeekIndex >= 0)
479 {
480 # if specified attribute exists
481 if (isset($this->TagAttribs[$this->CurrentSeekIndex][$AttribName]))
482 {
483 # retrieve attribute(s) to be returned to caller
484 if ($GetMultiple)
485 {
486 $ReturnVal = $this->TagAttribs[$this->CurrentSeekIndex];
487 }
488 else
489 {
490 $ReturnVal = $this->TagAttribs[$this->CurrentSeekIndex][$AttribName];
491 }
492 }
493 }
494 }
495
496 # return requested attribute to caller
497 return $ReturnVal;
498 }
499
500 # rebuild internal list of tags with the same tag name and same parent as current
501 function RebuildItemList()
502 {
503 # get list of tags with the same parent as current tag
504 $SameParentTags = array_keys($this->TagParents,
505 $this->TagParents[$this->CurrentSeekIndex]);
506
507 # get list of tags with the same name as current tag
508 $SameNameTags = array_keys($this->TagNames,
509 $this->TagNames[$this->CurrentSeekIndex]);
510
511 # intersect lists to get tags with both same name and same parent as current
512 $this->CurrentItemList = array_values(
513 array_intersect($SameNameTags, $SameParentTags));
514
515 # find and save index of current tag within item list
516 $this->CurrentItemIndex = array_search(
517 $this->CurrentSeekIndex, $this->CurrentItemList);
518
519 # save length of item list
520 $this->CurrentItemCount = count($this->CurrentItemList);
521 }
522
523 # internal method for debugging
524 function DumpInternalArrays()
525 {
526 foreach ($this->TagNames as $Index => $Name)
527 {
528 printf("[%03d] %-12.12s %03d %-30.30s \n", $Index, $Name, $this->TagParents[$Index], trim($this->TagData[$Index]));
529 }
530 }
531 }
532
533
534 ?>