CWIS Developer Documentation
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
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 
313  var $TagData;
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
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 ?>
DumpInternalArrays()
Definition: XMLParser.php:524
ParseText($Text, $LastTextToParse=TRUE)
Definition: XMLParser.php:47
GetAttributes()
Definition: XMLParser.php:297
CloseTag($Parser, $ElementName)
Definition: XMLParser.php:351
XMLParser($Encoding="UTF-8")
Definition: XMLParser.php:23
ReceiveData($Parser, $Data)
Definition: XMLParser.php:344
SetDebugLevel($NewLevel)
Definition: XMLParser.php:324
SeekToParent()
Definition: XMLParser.php:89
GetAttribute()
Definition: XMLParser.php:287
RebuildItemList()
Definition: XMLParser.php:501
SeekToChild($ChildIndex=0)
Definition: XMLParser.php:115
PerformSeek($SeekArgs, $MoveSeekPointer)
Definition: XMLParser.php:363
OpenTag($Parser, $ElementName, $ElementAttribs)
Definition: XMLParser.php:330
PreviousItem()
Definition: XMLParser.php:207
PerformGetAttribute($Args, $GetMultiple)
Definition: XMLParser.php:440
$CurrentParseIndex
Definition: XMLParser.php:315