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 ?>