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: XMLParser.php
4 #
5 # Part of the ScoutLib application support library
6 # Copyright 2005-2016 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
8 #
9
10 class XMLParser
11 {
12 # ---- PUBLIC INTERFACE --------------------------------------------------
13
18 public function __construct($Encoding="UTF-8")
19 {
20 # set default debug output level
21 $this->DebugLevel = 0;
22
23 # create XML parser and tell it about our methods
24 $this->Parser = xml_parser_create($Encoding);
25 xml_set_object($this->Parser, $this);
26 xml_set_element_handler($this->Parser, "OpenTag", "CloseTag");
27 xml_set_character_data_handler($this->Parser, "ReceiveData");
28
29 # initialize tag storage arrays
30 $this->TagNames = array();
31 $this->TagAttribs = array();
32 $this->TagData = array();
33 $this->TagParents = array();
34
35 # initialize indexes for parsing and retrieving data
36 $this->CurrentParseIndex = -1;
37 $this->CurrentSeekIndex = -1;
38 $this->NameKeyCache = array();
39 }
40
46 public function ParseText($Text, $LastTextToParse = TRUE)
47 {
48 # pass text to PHP XML parser
49 xml_parse($this->Parser, $Text, $LastTextToParse);
50 }
51
57 public function SeekTo()
58 {
59 # perform seek based on arguments passed by caller
60 $SeekResult = $this->PerformSeek(func_get_args(), TRUE);
61
62 # if seek successful
63 if ($SeekResult !== NULL)
64 {
65 # retrieve item count at seek location
66 $ItemCount = count($this->CurrentItemList);
67 }
68 else
69 {
70 # return null value to indicate that seek failed
71 $ItemCount = NULL;
72 }
73
74 # return count of tags found at requested location
75 if ($this->DebugLevel > 0)
76 {
77 print("XMLParser->SeekTo(");
78 $Sep = "";
79 $DbugArgList = "";
80 foreach (func_get_args() as $Arg)
81 {
82 $DbugArgList .= $Sep."\"".$Arg."\"";
83 $Sep = ", ";
84 }
85 print($DbugArgList.") returned ".intval($ItemCount)
86 ." items starting at index ".$this->CurrentSeekIndex."\n");
87 }
88 return $ItemCount;
89 }
90
95 public function SeekToParent()
96 {
97 # if we are not at the root of the tree
98 if ($this->CurrentSeekIndex >= 0)
99 {
100 # move up one level in tree
101 $this->CurrentSeekIndex = $this->TagParents[$this->CurrentSeekIndex];
102
103 # clear item list
104 unset($this->CurrentItemList);
105
106 # return name of new tag to caller
107 $Result = $this->TagNames[$this->CurrentSeekIndex];
108 }
109 else
110 {
111 # return NULL indicating that no parent was found
112 $Result = NULL;
113 }
114
115 # return result to caller
116 if ($this->DebugLevel > 0)
117 {
118 print("XMLParser->SeekToParent() returned ".$Result."<br>\n");
119 }
120 return $Result;
121 }
122
128 public function SeekToChild($ChildIndex = 0)
129 {
130 # look for tags with current tag as parent
131 $ChildTags = array_keys($this->TagParents, $this->CurrentSeekIndex);
132
133 # if child tag was found with requested index
134 if (isset($ChildTags[$ChildIndex]))
135 {
136 # set current seek index to child
137 $this->CurrentSeekIndex = $ChildTags[$ChildIndex];
138
139 # clear item list info
140 unset($this->CurrentItemList);
141
142 # return name of new tag to caller
143 $Result = $this->TagNames[$this->CurrentSeekIndex];
144 }
145 else
146 {
147 # return NULL indicating that no children were found
148 $Result = NULL;
149 }
150
151 # return result to caller
152 if ($this->DebugLevel > 0)
153 {
154 print("XMLParser->SeekToChild() returned ".$Result."<br>\n");
155 }
156 return $Result;
157 }
158
162 public function SeekToRoot()
163 {
164 $this->CurrentSeekIndex = -1;
165 }
166
171 public function NextTag()
172 {
173 # get list of tags with same parent as this tag
174 $LevelTags = array_keys($this->TagParents,
175 $this->TagParents[$this->CurrentSeekIndex]);
176
177 # find position of next tag in list
178 $NextTagPosition = array_search($this->CurrentSeekIndex, $LevelTags) + 1;
179
180 # if there is a next tag
181 if (count($LevelTags) > $NextTagPosition)
182 {
183 # move seek pointer to next tag at this level
184 $this->CurrentSeekIndex = $LevelTags[$NextTagPosition];
185
186 # rebuild item list
187
188 # return name of tag at new position to caller
189 return $this->TagNames[$this->CurrentSeekIndex];
190 }
191 else
192 {
193 # return NULL to caller to indicate no next tag
194 return NULL;
195 }
196 }
197
202 public function NextItem()
203 {
204 # set up item list if necessary
205 if (!isset($this->CurrentItemList)) { $this->RebuildItemList(); }
206
207 # if there are items left to move to
208 if ($this->CurrentItemIndex < ($this->CurrentItemCount - 1))
209 {
210 # move item pointer to next item
211 $this->CurrentItemIndex++;
212
213 # set current seek pointer to next item
214 $this->CurrentSeekIndex =
215 $this->CurrentItemList[$this->CurrentItemIndex];
216
217 # return new item index to caller
218 $Result = $this->CurrentItemIndex;
219 }
220 else
221 {
222 # return NULL value to caller to indicate failure
223 $Result = NULL;
224 }
225
226 # return result to caller
227 return $Result;
228 }
229
234 public function PreviousItem()
235 {
236 # set up item list if necessary
237 if (!isset($this->CurrentItemList)) { $this->RebuildItemList(); }
238
239 # if we are not at the first item
240 if ($this->CurrentItemIndex > 0)
241 {
242 # move item pointer to previous item
243 $this->CurrentItemIndex--;
244
245 # set current seek pointer to next item
246 $this->CurrentSeekIndex =
247 $this->CurrentItemList[$this->CurrentItemIndex];
248
249 # return new item index to caller
250 return $this->CurrentItemIndex;
251 }
252 else
253 {
254 # return NULL value to caller to indicate failure
255 return NULL;
256 }
257 }
258
263 public function GetTagName()
264 {
265 if (isset($this->TagNames[$this->CurrentSeekIndex]))
266 {
267 return $this->TagNames[$this->CurrentSeekIndex];
268 }
269 else
270 {
271 return NULL;
272 }
273 }
274
280 public function GetData()
281 {
282 # assume that we will not be able to retrieve data
283 $Data = NULL;
284
285 # if arguments were supplied
286 if (func_num_args())
287 {
288 # retrieve index for specified point
289 $Index = $this->PerformSeek(func_get_args(), FALSE);
290
291 # if valid index was found
292 if ($Index !== NULL)
293 {
294 # retrieve data at index to be returned to caller
295 $Data = $this->TagData[$Index];
296 }
297 }
298 else
299 {
300 # if current seek index points to valid tag
301 if ($this->CurrentSeekIndex >= 0)
302 {
303 # retrieve data to be returned to caller
304 $Data = $this->TagData[$this->CurrentSeekIndex];
305 }
306 }
307
308 # return data to caller
309 if ($this->DebugLevel > 0)
310 {
311 print("XMLParser->GetData(");
312 if (func_num_args())
313 {
314 $ArgString = "";
315 foreach (func_get_args() as $Arg)
316 {
317 $ArgString .= "\"".$Arg."\", ";
318 }
319 $ArgString = substr($ArgString, 0, strlen($ArgString) - 2);
320 print($ArgString);
321 }
322 print(") returned ".($Data ? "\"".$Data."\"" : "NULL")."<br>\n");
323 }
324 return $Data;
325 }
326
333 public function GetAttribute()
334 {
335 # retrieve attribute
336 $Args = func_get_args();
337 $Attrib = $this->PerformGetAttribute($Args, FALSE);
338
339 # return requested attribute to caller
340 if ($this->DebugLevel > 0)
341 {
342 print("XMLParser->GetAttribute() returned ".$Attrib."<br>\n");
343 }
344 return $Attrib;
345 }
346
353 public function GetAttributes()
354 {
355 # retrieve attribute
356 $Args = func_get_args();
357 $Attribs = $this->PerformGetAttribute($Args, TRUE);
358
359 # return requested attribute to caller
360 if ($this->DebugLevel > 0)
361 {
362 print("XMLParser->GetAttributes() returned ".count($Attribs)
363 ." attributes<br>\n");
364 }
365 return $Attribs;
366 }
367
372 public function SetDebugLevel($NewLevel)
373 {
374 $this->DebugLevel = $NewLevel;
375 }
376
377
378 # ---- PRIVATE INTERFACE -------------------------------------------------
379
380 private $CurrentItemCount;
381 private $CurrentItemIndex;
382 private $CurrentItemList;
383 private $CurrentParseIndex;
384 private $CurrentSeekIndex;
385 private $DebugLevel;
386 private $NameKeyCache;
387 private $Parser;
388 private $TagAttribs;
389 private $TagData;
390 private $TagNames;
391 private $TagParents;
392
399 private function OpenTag($Parser, $ElementName, $ElementAttribs)
400 {
401 # add new tag to list
402 $NewTagIndex = count($this->TagNames);
403 $this->TagNames[$NewTagIndex] = $ElementName;
404 $this->TagAttribs[$NewTagIndex] = $ElementAttribs;
405 $this->TagParents[$NewTagIndex] = $this->CurrentParseIndex;
406 $this->TagData[$NewTagIndex] = NULL;
407
408 # set current tag to new tag
409 $this->CurrentParseIndex = $NewTagIndex;
410 }
411
417 private function ReceiveData($Parser, $Data)
418 {
419 # add data to currently open tag
420 $this->TagData[$this->CurrentParseIndex] .= $Data;
421 }
422
428 private function CloseTag($Parser, $ElementName)
429 {
430 # if we have an open tag and closing tag matches currently open tag
431 if (($this->CurrentParseIndex >= 0)
432 && ($ElementName == $this->TagNames[$this->CurrentParseIndex]))
433 {
434 # set current tag to parent tag
435 $this->CurrentParseIndex = $this->TagParents[$this->CurrentParseIndex];
436 }
437 }
438
445 private function PerformSeek($SeekArgs, $MoveSeekPointer)
446 {
447 # for each tag name or index in argument list
448 $NewSeekIndex = $this->CurrentSeekIndex;
449 foreach ($SeekArgs as $Arg)
450 {
451 # if argument is string
452 if (is_string($Arg))
453 {
454 # look for tags with given name and current tag as parent
455 $Arg = strtoupper($Arg);
456 if (!isset($this->NameKeyCache[$Arg]))
457 {
458 $this->NameKeyCache[$Arg] = array_keys($this->TagNames, $Arg);
459 $TestArray = array_keys($this->TagNames, $Arg);
460 }
461 $ChildTags = array_keys($this->TagParents, $NewSeekIndex);
462 $NewItemList = array_values(
463 array_intersect($this->NameKeyCache[$Arg], $ChildTags));
464 $NewItemCount = count($NewItemList);
465
466 # if matching tag found
467 if ($NewItemCount > 0)
468 {
469 # update local seek index
470 $NewSeekIndex = $NewItemList[0];
471
472 # save new item index
473 $NewItemIndex = 0;
474 }
475 else
476 {
477 # report seek failure to caller
478 return NULL;
479 }
480 }
481 else
482 {
483 # look for tags with same name and same parent as current tag
484 $NameTags = array_keys($this->TagNames,
485 $this->TagNames[$NewSeekIndex]);
486 $ChildTags = array_keys($this->TagParents,
487 $this->TagParents[$NewSeekIndex]);
488 $NewItemList = array_values(array_intersect($NameTags, $ChildTags));
489 $NewItemCount = count($NewItemList);
490
491 # if enough matching tags were found to contain requested index
492 if ($NewItemCount > $Arg)
493 {
494 # update local seek index
495 $NewSeekIndex = $NewItemList[$Arg];
496
497 # save new item index
498 $NewItemIndex = $Arg;
499 }
500 else
501 {
502 # report seek failure to caller
503 return NULL;
504 }
505 }
506 }
507
508 # if caller requested that seek pointer be moved to reflect seek
509 if ($MoveSeekPointer)
510 {
511 # update seek index
512 $this->CurrentSeekIndex = $NewSeekIndex;
513
514 # update item index and list
515 $this->CurrentItemIndex = $NewItemIndex;
516 $this->CurrentItemList = $NewItemList;
517 $this->CurrentItemCount = $NewItemCount;
518 }
519
520 # return index of found seek
521 return $NewSeekIndex;
522 }
523
531 private function PerformGetAttribute($Args, $GetMultiple)
532 {
533 # assume that we will not be able to retrieve attribute
534 $ReturnVal = NULL;
535
536 # retrieve attribute name and (possibly) seek arguments
537 if (!$GetMultiple)
538 {
539 $AttribName = strtoupper(array_shift($Args));
540 }
541
542 # if arguments were supplied
543 if (count($Args))
544 {
545 # retrieve index for specified point
546 $Index = $this->PerformSeek($Args, FALSE);
547
548 # if valid index was found
549 if ($Index !== NULL)
550 {
551 # if specified attribute exists
552 if (isset($this->TagAttribs[$Index][$AttribName]))
553 {
554 # retrieve attribute(s) at index to be returned to caller
555 if ($GetMultiple)
556 {
557 $ReturnVal = $this->TagAttribs[$Index];
558 }
559 else
560 {
561 $ReturnVal = $this->TagAttribs[$Index][$AttribName];
562 }
563 }
564 }
565 }
566 else
567 {
568 # if current seek index points to valid tag
569 $SeekIndex = $this->CurrentSeekIndex;
570 if ($SeekIndex >= 0)
571 {
572 # if specified attribute exists
573 if (isset($this->TagAttribs[$SeekIndex][$AttribName]))
574 {
575 # retrieve attribute(s) to be returned to caller
576 if ($GetMultiple)
577 {
578 $ReturnVal = $this->TagAttribs[$SeekIndex];
579 }
580 else
581 {
582 $ReturnVal = $this->TagAttribs[$SeekIndex][$AttribName];
583 }
584 }
585 }
586 }
587
588 # return requested attribute to caller
589 return $ReturnVal;
590 }
591
596 private function RebuildItemList()
597 {
598 # get list of tags with the same parent as current tag
599 $SameParentTags = array_keys($this->TagParents,
600 $this->TagParents[$this->CurrentSeekIndex]);
601
602 # get list of tags with the same name as current tag
603 $SameNameTags = array_keys($this->TagNames,
604 $this->TagNames[$this->CurrentSeekIndex]);
605
606 # intersect lists to get tags with both same name and same parent as current
607 $this->CurrentItemList = array_values(
608 array_intersect($SameNameTags, $SameParentTags));
609
610 # find and save index of current tag within item list
611 $this->CurrentItemIndex = array_search(
612 $this->CurrentSeekIndex, $this->CurrentItemList);
613
614 # save length of item list
615 $this->CurrentItemCount = count($this->CurrentItemList);
616 }
617
621 private function DumpInternalArrays()
622 {
623 foreach ($this->TagNames as $Index => $Name)
624 {
625 printf("[%03d] %-12.12s %03d %-30.30s \n", $Index, $Name,
626 $this->TagParents[$Index], trim($this->TagData[$Index]));
627 }
628 }
629 }
630
XMLParser\SeekToRoot
SeekToRoot()
Move seek pointer to root of tree.
Definition: XMLParser.php:162
XMLParser\__construct
__construct($Encoding="UTF-8")
Object constructor.
Definition: XMLParser.php:18
XMLParser\NextItem
NextItem()
Move to next instance of current tag.
Definition: XMLParser.php:202
XMLParser\GetTagName
GetTagName()
Retrieve tag name from current seek point.
Definition: XMLParser.php:263
XMLParser\ParseText
ParseText($Text, $LastTextToParse=TRUE)
Parse text stream and store result.
Definition: XMLParser.php:46
XMLParser\GetAttributes
GetAttributes()
Retrieve specified attributes from current seek point or specified point below.
Definition: XMLParser.php:353
XMLParser\GetData
GetData()
Retrieve data from current seek point.
Definition: XMLParser.php:280
XMLParser\SeekTo
SeekTo()
Move current tag pointer to specified item.
Definition: XMLParser.php:57
XMLParser\SetDebugLevel
SetDebugLevel($NewLevel)
Set current debug output level (0-9).
Definition: XMLParser.php:372
PHP
PHP
Definition: RSSClient--Test.php:12
XMLParser
Definition: XMLParser.php:10
XMLParser\SeekToParent
SeekToParent()
Move seek pointer up one level.
Definition: XMLParser.php:95
XMLParser\GetAttribute
GetAttribute()
Retrieve specified attribute from current seek point or specified point below.
Definition: XMLParser.php:333
XMLParser\SeekToChild
SeekToChild($ChildIndex=0)
Move seek pointer to first child of current tag.
Definition: XMLParser.php:128
XMLParser\PreviousItem
PreviousItem()
Move to previous instance of current tag.
Definition: XMLParser.php:234
XMLParser\NextTag
NextTag()
Move to next tag at current level.
Definition: XMLParser.php:171