3 # FILE: SPT--XMLParser.php
8 # SomeMethod($SomeParameter, $AnotherParameter)
9 # - short description of method
11 # AUTHOR: Edward Almasy
13 # Part of the Scout Portal Toolkit
14 # Copyright 2005 Internet Scout Project
15 # http://scout.wisc.edu
20 # ---- PUBLIC INTERFACE --------------------------------------------------
25 # set default debug output level
26 $this->DebugLevel = 0;
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");
34 # initialize tag storage arrays
35 $this->TagNames = array();
36 $this->TagAttribs = array();
37 $this->TagData = array();
38 $this->TagParents = array();
40 # initialize indexes for parsing and retrieving data
41 $this->CurrentParseIndex = -1;
42 $this->CurrentSeekIndex = -1;
43 $this->NameKeyCache = array();
46 # parse text stream and store result
49 # pass text to PHP XML parser
50 xml_parse($this->Parser, $Text, $LastTextToParse);
53 # move current tag pointer to specified item (returns NULL on failure)
54 # (args may be tag names or indexes)
57 # perform seek based on arguments passed by caller
58 $SeekResult = $this->
PerformSeek(func_get_args(), TRUE);
61 if ($SeekResult !== NULL)
63 # retrieve item count at seek location
64 $ItemCount = count($this->CurrentItemList);
68 # return null value to indicate that seek failed
72 # return count of tags found at requested location
73 if ($this->DebugLevel > 0)
75 print(
"XMLParser->SeekTo(");
78 foreach (func_get_args() as $Arg)
80 $DbugArgList .= $Sep.
"\"".$Arg.
"\"";
83 print($DbugArgList.
") returned ".intval($ItemCount).
" items starting at index ".$this->CurrentSeekIndex.
"\n");
88 # move seek pointer up one level (returns tag name or NULL if no parent)
91 # if we are not at the root of the tree
92 if ($this->CurrentSeekIndex >= 0)
94 # move up one level in tree
98 unset($this->CurrentItemList);
100 # return name of new tag to caller
105 # return NULL indicating that no parent was found
109 # return result to caller
110 if ($this->DebugLevel > 0) { print(
"XMLParser->SeekToParent() returned ".$Result.
"<br>\n"); }
114 # move seek pointer to first child of current tag (returns tag name or NULL if no children)
117 # look for tags with current tag as parent
118 $ChildTags = array_keys($this->TagParents, $this->CurrentSeekIndex);
120 # if child tag was found with requested index
121 if (isset($ChildTags[$ChildIndex]))
123 # set current seek index to child
124 $this->CurrentSeekIndex = $ChildTags[$ChildIndex];
126 # clear item list info
127 unset($this->CurrentItemList);
129 # return name of new tag to caller
134 # return NULL indicating that no children were found
138 # return result to caller
139 if ($this->DebugLevel > 0) { print(
"XMLParser->SeekToChild() returned ".$Result.
"<br>\n"); }
143 # move seek pointer to root of tree
146 $this->CurrentSeekIndex = -1;
149 # move to next tag at current level (returns tag name or NULL if no next)
152 # get list of tags with same parent as this tag
153 $LevelTags = array_keys($this->TagParents,
154 $this->TagParents[$this->CurrentSeekIndex]);
156 # find position of next tag in list
157 $NextTagPosition = array_search($this->CurrentSeekIndex, $LevelTags) + 1;
159 # if there is a next tag
160 if (count($LevelTags) > $NextTagPosition)
162 # move seek pointer to next tag at this level
163 $this->CurrentSeekIndex = $LevelTags[$NextTagPosition];
167 # return name of tag at new position to caller
172 # return NULL to caller to indicate no next tag
177 # move to next instance of current tag (returns index or NULL if no next)
180 # set up item list if necessary
183 # if there are items left to move to
184 if ($this->CurrentItemIndex < ($this->CurrentItemCount - 1))
186 # move item pointer to next item
187 $this->CurrentItemIndex++;
189 # set current seek pointer to next item
190 $this->CurrentSeekIndex =
193 # return new item index to caller
198 # return NULL value to caller to indicate failure
202 # return result to caller
206 # move to previous instance of current tag (returns index or NULL on fail)
209 # set up item list if necessary
212 # if we are not at the first item
213 if ($this->CurrentItemIndex > 0)
215 # move item pointer to previous item
216 $this->CurrentItemIndex--;
218 # set current seek pointer to next item
219 $this->CurrentSeekIndex =
222 # return new item index to caller
227 # return NULL value to caller to indicate failure
232 # retrieve tag name from current seek point
235 if (isset($this->TagNames[$this->CurrentSeekIndex]))
245 # retrieve data from current seek point
248 # assume that we will not be able to retrieve data
251 # if arguments were supplied
254 # retrieve index for specified point
255 $Index = $this->
PerformSeek(func_get_args(), FALSE);
257 # if valid index was found
260 # retrieve data at index to be returned to caller
261 $Data = $this->TagData[$Index];
266 # if current seek index points to valid tag
267 if ($this->CurrentSeekIndex >= 0)
269 # retrieve data to be returned to caller
274 # return data to caller
275 if ($this->DebugLevel > 0)
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");
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)
290 $Args = func_get_args();
293 # return requested attribute to caller
294 if ($this->DebugLevel > 0) { print(
"XMLParser->GetAttribute() returned ".$Attrib.
"<br>\n"); }
300 $Args = func_get_args();
303 # return requested attribute to caller
304 if ($this->DebugLevel > 0) { print(
"XMLParser->GetAttributes() returned ".count($Attribs).
" attributes<br>\n"); }
309 # ---- PRIVATE INTERFACE -------------------------------------------------
323 # set current debug output level (0-9)
326 $this->DebugLevel = $NewLevel;
329 # callback function for handling open tags
330 function OpenTag($Parser, $ElementName, $ElementAttribs)
332 # add new tag to list
333 $NewTagIndex = count($this->TagNames);
334 $this->TagNames[$NewTagIndex] = $ElementName;
335 $this->TagAttribs[$NewTagIndex] = $ElementAttribs;
337 $this->TagData[$NewTagIndex] = NULL;
339 # set current tag to new tag
340 $this->CurrentParseIndex = $NewTagIndex;
343 # callback function for receiving data between tags
346 # add data to currently open tag
350 # callback function for handling close tags
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]))
357 # set current tag to parent tag
362 # perform seek to point in tag tree and update seek pointer (if requested)
365 # for each tag name or index in argument list
367 foreach ($SeekArgs as $Arg)
369 # if argument is string
372 # look for tags with given name and current tag as parent
373 $Arg = strtoupper($Arg);
374 if (!isset($this->NameKeyCache[$Arg]))
376 $this->NameKeyCache[$Arg] = array_keys($this->TagNames, $Arg);
377 $TestArray = array_keys($this->TagNames, $Arg);
379 $ChildTags = array_keys($this->TagParents, $NewSeekIndex);
380 $NewItemList = array_values(
381 array_intersect($this->NameKeyCache[$Arg], $ChildTags));
382 $NewItemCount = count($NewItemList);
384 # if matching tag found
385 if ($NewItemCount > 0)
387 # update local seek index
388 $NewSeekIndex = $NewItemList[0];
390 # save new item index
395 # report seek failure to caller
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);
407 # if enough matching tags were found to contain requested index
408 if ($NewItemCount > $Arg)
410 # update local seek index
411 $NewSeekIndex = $NewItemList[$Arg];
413 # save new item index
414 $NewItemIndex = $Arg;
418 # report seek failure to caller
424 # if caller requested that seek pointer be moved to reflect seek
425 if ($MoveSeekPointer)
428 $this->CurrentSeekIndex = $NewSeekIndex;
430 # update item index and list
431 $this->CurrentItemIndex = $NewItemIndex;
432 $this->CurrentItemList = $NewItemList;
433 $this->CurrentItemCount = $NewItemCount;
436 # return index of found seek
437 return $NewSeekIndex;
442 # assume that we will not be able to retrieve attribute
445 # retrieve attribute name and (possibly) seek arguments
448 $AttribName = strtoupper(array_shift($Args));
451 # if arguments were supplied
454 # retrieve index for specified point
457 # if valid index was found
460 # if specified attribute exists
461 if (isset($this->TagAttribs[$Index][$AttribName]))
463 # retrieve attribute(s) at index to be returned to caller
466 $ReturnVal = $this->TagAttribs[$Index];
470 $ReturnVal = $this->TagAttribs[$Index][$AttribName];
477 # if current seek index points to valid tag
478 if ($this->CurrentSeekIndex >= 0)
480 # if specified attribute exists
481 if (isset($this->TagAttribs[$this->CurrentSeekIndex][$AttribName]))
483 # retrieve attribute(s) to be returned to caller
496 # return requested attribute to caller
500 # rebuild internal list of tags with the same tag name and same parent as current
503 # get list of tags with the same parent as current tag
504 $SameParentTags = array_keys($this->TagParents,
505 $this->TagParents[$this->CurrentSeekIndex]);
507 # get list of tags with the same name as current tag
508 $SameNameTags = array_keys($this->TagNames,
509 $this->TagNames[$this->CurrentSeekIndex]);
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));
515 # find and save index of current tag within item list
516 $this->CurrentItemIndex = array_search(
517 $this->CurrentSeekIndex, $this->CurrentItemList);
519 # save length of item list
520 $this->CurrentItemCount = count($this->CurrentItemList);
523 # internal method for debugging
526 foreach ($this->TagNames as $Index => $Name)
528 printf(
"[%03d] %-12.12s %03d %-30.30s \n", $Index, $Name, $this->TagParents[$Index], trim($this->TagData[$Index]));
ParseText($Text, $LastTextToParse=TRUE)
CloseTag($Parser, $ElementName)
XMLParser($Encoding="UTF-8")
ReceiveData($Parser, $Data)
SeekToChild($ChildIndex=0)
PerformSeek($SeekArgs, $MoveSeekPointer)
OpenTag($Parser, $ElementName, $ElementAttribs)
PerformGetAttribute($Args, $GetMultiple)