XMLParser.php
Go to the documentation of this file.00001 <?PHP
00002 #
00003 # FILE: SPT--XMLParser.php
00004 #
00005 # METHODS PROVIDED:
00006 # XMLParser()
00007 # - constructor
00008 # SomeMethod($SomeParameter, $AnotherParameter)
00009 # - short description of method
00010 #
00011 # AUTHOR: Edward Almasy
00012 #
00013 # Part of the Scout Portal Toolkit
00014 # Copyright 2005 Internet Scout Project
00015 # http://scout.wisc.edu
00016 #
00017
00018 class XMLParser {
00019
00020 # ---- PUBLIC INTERFACE --------------------------------------------------
00021
00022 # object constructor
00023 function XMLParser($Encoding="UTF-8")
00024 {
00025 # set default debug output level
00026 $this->DebugLevel = 0;
00027
00028 # create XML parser and tell it about our methods
00029 $this->Parser = xml_parser_create($Encoding);
00030 xml_set_object($this->Parser, $this);
00031 xml_set_element_handler($this->Parser, "OpenTag", "CloseTag");
00032 xml_set_character_data_handler($this->Parser, "ReceiveData");
00033
00034 # initialize tag storage arrays
00035 $this->TagNames = array();
00036 $this->TagAttribs = array();
00037 $this->TagData = array();
00038 $this->TagParents = array();
00039
00040 # initialize indexes for parsing and retrieving data
00041 $this->CurrentParseIndex = -1;
00042 $this->CurrentSeekIndex = -1;
00043 $this->NameKeyCache = array();
00044 }
00045
00046 # parse text stream and store result
00047 function ParseText($Text, $LastTextToParse = TRUE)
00048 {
00049 # pass text to PHP XML parser
00050 xml_parse($this->Parser, $Text, $LastTextToParse);
00051 }
00052
00053 # move current tag pointer to specified item (returns NULL on failure)
00054 function SeekTo() # (args may be tag names or indexes)
00055 {
00056 # perform seek based on arguments passed by caller
00057 $SeekResult = $this->PerformSeek(func_get_args(), TRUE);
00058
00059 # if seek successful
00060 if ($SeekResult !== NULL)
00061 {
00062 # retrieve item count at seek location
00063 $ItemCount = count($this->CurrentItemList);
00064 }
00065 else
00066 {
00067 # return null value to indicate that seek failed
00068 $ItemCount = NULL;
00069 }
00070
00071 # return count of tags found at requested location
00072 if ($this->DebugLevel > 0)
00073 {
00074 print("XMLParser->SeekTo(");
00075 $Sep = "";
00076 $DbugArgList = "";
00077 foreach (func_get_args() as $Arg)
00078 {
00079 $DbugArgList .= $Sep."\"".$Arg."\"";
00080 $Sep = ", ";
00081 }
00082 print($DbugArgList.") returned ".intval($ItemCount)." items starting at index ".$this->CurrentSeekIndex."\n");
00083 }
00084 return $ItemCount;
00085 }
00086
00087 # move seek pointer up one level (returns tag name or NULL if no parent)
00088 function SeekToParent()
00089 {
00090 # if we are not at the root of the tree
00091 if ($this->CurrentSeekIndex >= 0)
00092 {
00093 # move up one level in tree
00094 $this->CurrentSeekIndex = $this->TagParents[$this->CurrentSeekIndex];
00095
00096 # clear item list
00097 unset($this->CurrentItemList);
00098
00099 # return name of new tag to caller
00100 $Result = $this->TagNames[$this->CurrentSeekIndex];
00101 }
00102 else
00103 {
00104 # return NULL indicating that no parent was found
00105 $Result = NULL;
00106 }
00107
00108 # return result to caller
00109 if ($this->DebugLevel > 0) { print("XMLParser->SeekToParent() returned ".$Result."<br>\n"); }
00110 return $Result;
00111 }
00112
00113 # move seek pointer to first child of current tag (returns tag name or NULL if no children)
00114 function SeekToChild($ChildIndex = 0)
00115 {
00116 # look for tags with current tag as parent
00117 $ChildTags = array_keys($this->TagParents, $this->CurrentSeekIndex);
00118
00119 # if child tag was found with requested index
00120 if (isset($ChildTags[$ChildIndex]))
00121 {
00122 # set current seek index to child
00123 $this->CurrentSeekIndex = $ChildTags[$ChildIndex];
00124
00125 # clear item list info
00126 unset($this->CurrentItemList);
00127
00128 # return name of new tag to caller
00129 $Result = $this->TagNames[$this->CurrentSeekIndex];
00130 }
00131 else
00132 {
00133 # return NULL indicating that no children were found
00134 $Result = NULL;
00135 }
00136
00137 # return result to caller
00138 if ($this->DebugLevel > 0) { print("XMLParser->SeekToChild() returned ".$Result."<br>\n"); }
00139 return $Result;
00140 }
00141
00142 # move seek pointer to root of tree
00143 function SeekToRoot()
00144 {
00145 $this->CurrentSeekIndex = -1;
00146 }
00147
00148 # move to next tag at current level (returns tag name or NULL if no next)
00149 function NextTag()
00150 {
00151 # get list of tags with same parent as this tag
00152 $LevelTags = array_keys($this->TagParents,
00153 $this->TagParents[$this->CurrentSeekIndex]);
00154
00155 # find position of next tag in list
00156 $NextTagPosition = array_search($this->CurrentSeekIndex, $LevelTags) + 1;
00157
00158 # if there is a next tag
00159 if (count($LevelTags) > $NextTagPosition)
00160 {
00161 # move seek pointer to next tag at this level
00162 $this->CurrentSeekIndex = $LevelTags[$NextTagPosition];
00163
00164 # rebuild item list
00165
00166 # return name of tag at new position to caller
00167 return $this->TagNames[$this->CurrentSeekIndex];
00168 }
00169 else
00170 {
00171 # return NULL to caller to indicate no next tag
00172 return NULL;
00173 }
00174 }
00175
00176 # move to next instance of current tag (returns index or NULL if no next)
00177 function NextItem()
00178 {
00179 # set up item list if necessary
00180 if (!isset($this->CurrentItemList)) { $this->RebuildItemList(); }
00181
00182 # if there are items left to move to
00183 if ($this->CurrentItemIndex < ($this->CurrentItemCount - 1))
00184 {
00185 # move item pointer to next item
00186 $this->CurrentItemIndex++;
00187
00188 # set current seek pointer to next item
00189 $this->CurrentSeekIndex =
00190 $this->CurrentItemList[$this->CurrentItemIndex];
00191
00192 # return new item index to caller
00193 $Result = $this->CurrentItemIndex;
00194 }
00195 else
00196 {
00197 # return NULL value to caller to indicate failure
00198 $Result = NULL;
00199 }
00200
00201 # return result to caller
00202 return $Result;
00203 }
00204
00205 # move to previous instance of current tag (returns index or NULL on fail)
00206 function PreviousItem()
00207 {
00208 # set up item list if necessary
00209 if (!isset($this->CurrentItemList)) { $this->RebuildItemList(); }
00210
00211 # if we are not at the first item
00212 if ($this->CurrentItemIndex > 0)
00213 {
00214 # move item pointer to previous item
00215 $this->CurrentItemIndex--;
00216
00217 # set current seek pointer to next item
00218 $this->CurrentSeekIndex =
00219 $this->CurrentItemList[$this->CurrentItemIndex];
00220
00221 # return new item index to caller
00222 return $this->CurrentItemIndex;
00223 }
00224 else
00225 {
00226 # return NULL value to caller to indicate failure
00227 return NULL;
00228 }
00229 }
00230
00231 # retrieve tag name from current seek point
00232 function GetTagName()
00233 {
00234 if (isset($this->TagNames[$this->CurrentSeekIndex]))
00235 {
00236 return $this->TagNames[$this->CurrentSeekIndex];
00237 }
00238 else
00239 {
00240 return NULL;
00241 }
00242 }
00243
00244 # retrieve data from current seek point
00245 function GetData()
00246 {
00247 # assume that we will not be able to retrieve data
00248 $Data = NULL;
00249
00250 # if arguments were supplied
00251 if (func_num_args())
00252 {
00253 # retrieve index for specified point
00254 $Index = $this->PerformSeek(func_get_args(), FALSE);
00255
00256 # if valid index was found
00257 if ($Index !== NULL)
00258 {
00259 # retrieve data at index to be returned to caller
00260 $Data = $this->TagData[$Index];
00261 }
00262 }
00263 else
00264 {
00265 # if current seek index points to valid tag
00266 if ($this->CurrentSeekIndex >= 0)
00267 {
00268 # retrieve data to be returned to caller
00269 $Data = $this->TagData[$this->CurrentSeekIndex];
00270 }
00271 }
00272
00273 # return data to caller
00274 if ($this->DebugLevel > 0)
00275 {
00276 print("XMLParser->GetData(");
00277 if (func_num_args()) { $ArgString = ""; foreach (func_get_args() as $Arg) { $ArgString .= "\"".$Arg."\", "; } $ArgString = substr($ArgString, 0, strlen($ArgString) - 2); print($ArgString); }
00278 print(") returned ".($Data ? "\"".$Data."\"" : "NULL")."<br>\n");
00279 }
00280 return $Data;
00281 }
00282
00283 # retrieve specified attribute(s) from current seek point or specified point below
00284 # (first arg is attribute name and optional subsequent args tell where to seek to)
00285 # (returns NULL if no such attribute for current or specified tag)
00286 function GetAttribute()
00287 {
00288 # retrieve attribute
00289 $Args = func_get_args();
00290 $Attrib = $this->PerformGetAttribute($Args, FALSE);
00291
00292 # return requested attribute to caller
00293 if ($this->DebugLevel > 0) { print("XMLParser->GetAttribute() returned ".$Attrib."<br>\n"); }
00294 return $Attrib;
00295 }
00296 function GetAttributes()
00297 {
00298 # retrieve attribute
00299 $Args = func_get_args();
00300 $Attribs = $this->PerformGetAttribute($Args, TRUE);
00301
00302 # return requested attribute to caller
00303 if ($this->DebugLevel > 0) { print("XMLParser->GetAttributes() returned ".count($Attribs)." attributes<br>\n"); }
00304 return $Attribs;
00305 }
00306
00307
00308 # ---- PRIVATE INTERFACE -------------------------------------------------
00309
00310 var $TagNames;
00311 var $TagAttribs;
00312 var $TagData;
00313 var $TagParents;
00314 var $CurrentParseIndex;
00315 var $CurrentSeekIndex;
00316 var $CurrentItemIndex;
00317 var $CurrentItemList;
00318 var $CurrentItemCount;
00319 var $DebugLevel;
00320 var $NameKeyCache;
00321
00322 # set current debug output level (0-9)
00323 function SetDebugLevel($NewLevel)
00324 {
00325 $this->DebugLevel = $NewLevel;
00326 }
00327
00328 # callback function for handling open tags
00329 function OpenTag($Parser, $ElementName, $ElementAttribs)
00330 {
00331 # add new tag to list
00332 $NewTagIndex = count($this->TagNames);
00333 $this->TagNames[$NewTagIndex] = $ElementName;
00334 $this->TagAttribs[$NewTagIndex] = $ElementAttribs;
00335 $this->TagParents[$NewTagIndex] = $this->CurrentParseIndex;
00336 $this->TagData[$NewTagIndex] = NULL;
00337
00338 # set current tag to new tag
00339 $this->CurrentParseIndex = $NewTagIndex;
00340 }
00341
00342 # callback function for receiving data between tags
00343 function ReceiveData($Parser, $Data)
00344 {
00345 # add data to currently open tag
00346 $this->TagData[$this->CurrentParseIndex] .= $Data;
00347 }
00348
00349 # callback function for handling close tags
00350 function CloseTag($Parser, $ElementName)
00351 {
00352 # if we have an open tag and closing tag matches currently open tag
00353 if (($this->CurrentParseIndex >= 0)
00354 && ($ElementName == $this->TagNames[$this->CurrentParseIndex]))
00355 {
00356 # set current tag to parent tag
00357 $this->CurrentParseIndex = $this->TagParents[$this->CurrentParseIndex];
00358 }
00359 }
00360
00361 # perform seek to point in tag tree and update seek pointer (if requested)
00362 function PerformSeek($SeekArgs, $MoveSeekPointer)
00363 {
00364 # for each tag name or index in argument list
00365 $NewSeekIndex = $this->CurrentSeekIndex;
00366 foreach ($SeekArgs as $Arg)
00367 {
00368 # if argument is string
00369 if (is_string($Arg))
00370 {
00371 # look for tags with given name and current tag as parent
00372 $Arg = strtoupper($Arg);
00373 if (!isset($this->NameKeyCache[$Arg]))
00374 {
00375 $this->NameKeyCache[$Arg] = array_keys($this->TagNames, $Arg);
00376 $TestArray = array_keys($this->TagNames, $Arg);
00377 }
00378 $ChildTags = array_keys($this->TagParents, $NewSeekIndex);
00379 $NewItemList = array_values(
00380 array_intersect($this->NameKeyCache[$Arg], $ChildTags));
00381 $NewItemCount = count($NewItemList);
00382
00383 # if matching tag found
00384 if ($NewItemCount > 0)
00385 {
00386 # update local seek index
00387 $NewSeekIndex = $NewItemList[0];
00388
00389 # save new item index
00390 $NewItemIndex = 0;
00391 }
00392 else
00393 {
00394 # report seek failure to caller
00395 return NULL;
00396 }
00397 }
00398 else
00399 {
00400 # look for tags with same name and same parent as current tag
00401 $NameTags = array_keys($this->TagNames, $this->TagNames[$NewSeekIndex]);
00402 $ChildTags = array_keys($this->TagParents, $this->TagParents[$NewSeekIndex]);
00403 $NewItemList = array_values(array_intersect($NameTags, $ChildTags));
00404 $NewItemCount = count($NewItemList);
00405
00406 # if enough matching tags were found to contain requested index
00407 if ($NewItemCount > $Arg)
00408 {
00409 # update local seek index
00410 $NewSeekIndex = $NewItemList[$Arg];
00411
00412 # save new item index
00413 $NewItemIndex = $Arg;
00414 }
00415 else
00416 {
00417 # report seek failure to caller
00418 return NULL;
00419 }
00420 }
00421 }
00422
00423 # if caller requested that seek pointer be moved to reflect seek
00424 if ($MoveSeekPointer)
00425 {
00426 # update seek index
00427 $this->CurrentSeekIndex = $NewSeekIndex;
00428
00429 # update item index and list
00430 $this->CurrentItemIndex = $NewItemIndex;
00431 $this->CurrentItemList = $NewItemList;
00432 $this->CurrentItemCount = $NewItemCount;
00433 }
00434
00435 # return index of found seek
00436 return $NewSeekIndex;
00437 }
00438
00439 function PerformGetAttribute($Args, $GetMultiple)
00440 {
00441 # assume that we will not be able to retrieve attribute
00442 $ReturnVal = NULL;
00443
00444 # retrieve attribute name and (possibly) seek arguments
00445 if (!$GetMultiple)
00446 {
00447 $AttribName = strtoupper(array_shift($Args));
00448 }
00449
00450 # if arguments were supplied
00451 if (count($Args))
00452 {
00453 # retrieve index for specified point
00454 $Index = $this->PerformSeek($Args, FALSE);
00455
00456 # if valid index was found
00457 if ($Index !== NULL)
00458 {
00459 # if specified attribute exists
00460 if (isset($this->TagAttribs[$Index][$AttribName]))
00461 {
00462 # retrieve attribute(s) at index to be returned to caller
00463 if ($GetMultiple)
00464 {
00465 $ReturnVal = $this->TagAttribs[$Index];
00466 }
00467 else
00468 {
00469 $ReturnVal = $this->TagAttribs[$Index][$AttribName];
00470 }
00471 }
00472 }
00473 }
00474 else
00475 {
00476 # if current seek index points to valid tag
00477 if ($this->CurrentSeekIndex >= 0)
00478 {
00479 # if specified attribute exists
00480 if (isset($this->TagAttribs[$this->CurrentSeekIndex][$AttribName]))
00481 {
00482 # retrieve attribute(s) to be returned to caller
00483 if ($GetMultiple)
00484 {
00485 $ReturnVal = $this->TagAttribs[$this->CurrentSeekIndex];
00486 }
00487 else
00488 {
00489 $ReturnVal = $this->TagAttribs[$this->CurrentSeekIndex][$AttribName];
00490 }
00491 }
00492 }
00493 }
00494
00495 # return requested attribute to caller
00496 return $ReturnVal;
00497 }
00498
00499 # rebuild internal list of tags with the same tag name and same parent as current
00500 function RebuildItemList()
00501 {
00502 # get list of tags with the same parent as current tag
00503 $SameParentTags = array_keys($this->TagParents,
00504 $this->TagParents[$this->CurrentSeekIndex]);
00505
00506 # get list of tags with the same name as current tag
00507 $SameNameTags = array_keys($this->TagNames,
00508 $this->TagNames[$this->CurrentSeekIndex]);
00509
00510 # intersect lists to get tags with both same name and same parent as current
00511 $this->CurrentItemList = array_values(
00512 array_intersect($SameNameTags, $SameParentTags));
00513
00514 # find and save index of current tag within item list
00515 $this->CurrentItemIndex = array_search(
00516 $this->CurrentSeekIndex, $this->CurrentItemList);
00517
00518 # save length of item list
00519 $this->CurrentItemCount = count($this->CurrentItemList);
00520 }
00521
00522 # internal method for debugging
00523 function DumpInternalArrays()
00524 {
00525 foreach ($this->TagNames as $Index => $Name)
00526 {
00527 printf("[%03d] %-12.12s %03d %-30.30s \n", $Index, $Name, $this->TagParents[$Index], trim($this->TagData[$Index]));
00528 }
00529 }
00530 }
00531
00532
00533 ?>