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
368
369 # ---- PRIVATE INTERFACE -------------------------------------------------
370
371 private $TagNames;
372 private $TagAttribs;
373 private $TagData;
374 private $TagParents;
375 private $CurrentParseIndex;
376 private $CurrentSeekIndex;
377 private $CurrentItemIndex;
378 private $CurrentItemList;
379 private $CurrentItemCount;
380 private $DebugLevel;
381 private $NameKeyCache;
382
387 private function SetDebugLevel($NewLevel)
388 {
389 $this->DebugLevel = $NewLevel;
390 }
391
398 private function OpenTag($Parser, $ElementName, $ElementAttribs)
399 {
400 # add new tag to list
401 $NewTagIndex = count($this->TagNames);
402 $this->TagNames[$NewTagIndex] = $ElementName;
403 $this->TagAttribs[$NewTagIndex] = $ElementAttribs;
404 $this->TagParents[$NewTagIndex] = $this->CurrentParseIndex;
405 $this->TagData[$NewTagIndex] = NULL;
406
407 # set current tag to new tag
408 $this->CurrentParseIndex = $NewTagIndex;
409 }
410
416 private function ReceiveData($Parser, $Data)
417 {
418 # add data to currently open tag
419 $this->TagData[$this->CurrentParseIndex] .= $Data;
420 }
421
427 private function CloseTag($Parser, $ElementName)
428 {
429 # if we have an open tag and closing tag matches currently open tag
430 if (($this->CurrentParseIndex >= 0)
431 && ($ElementName == $this->TagNames[$this->CurrentParseIndex]))
432 {
433 # set current tag to parent tag
434 $this->CurrentParseIndex = $this->TagParents[$this->CurrentParseIndex];
435 }
436 }
437
444 private function PerformSeek($SeekArgs, $MoveSeekPointer)
445 {
446 # for each tag name or index in argument list
447 $NewSeekIndex = $this->CurrentSeekIndex;
448 foreach ($SeekArgs as $Arg)
449 {
450 # if argument is string
451 if (is_string($Arg))
452 {
453 # look for tags with given name and current tag as parent
454 $Arg = strtoupper($Arg);
455 if (!isset($this->NameKeyCache[$Arg]))
456 {
457 $this->NameKeyCache[$Arg] = array_keys($this->TagNames, $Arg);
458 $TestArray = array_keys($this->TagNames, $Arg);
459 }
460 $ChildTags = array_keys($this->TagParents, $NewSeekIndex);
461 $NewItemList = array_values(
462 array_intersect($this->NameKeyCache[$Arg], $ChildTags));
463 $NewItemCount = count($NewItemList);
464
465 # if matching tag found
466 if ($NewItemCount > 0)
467 {
468 # update local seek index
469 $NewSeekIndex = $NewItemList[0];
470
471 # save new item index
472 $NewItemIndex = 0;
473 }
474 else
475 {
476 # report seek failure to caller
477 return NULL;
478 }
479 }
480 else
481 {
482 # look for tags with same name and same parent as current tag
483 $NameTags = array_keys($this->TagNames,
484 $this->TagNames[$NewSeekIndex]);
485 $ChildTags = array_keys($this->TagParents,
486 $this->TagParents[$NewSeekIndex]);
487 $NewItemList = array_values(array_intersect($NameTags, $ChildTags));
488 $NewItemCount = count($NewItemList);
489
490 # if enough matching tags were found to contain requested index
491 if ($NewItemCount > $Arg)
492 {
493 # update local seek index
494 $NewSeekIndex = $NewItemList[$Arg];
495
496 # save new item index
497 $NewItemIndex = $Arg;
498 }
499 else
500 {
501 # report seek failure to caller
502 return NULL;
503 }
504 }
505 }
506
507 # if caller requested that seek pointer be moved to reflect seek
508 if ($MoveSeekPointer)
509 {
510 # update seek index
511 $this->CurrentSeekIndex = $NewSeekIndex;
512
513 # update item index and list
514 $this->CurrentItemIndex = $NewItemIndex;
515 $this->CurrentItemList = $NewItemList;
516 $this->CurrentItemCount = $NewItemCount;
517 }
518
519 # return index of found seek
520 return $NewSeekIndex;
521 }
522
530 private function PerformGetAttribute($Args, $GetMultiple)
531 {
532 # assume that we will not be able to retrieve attribute
533 $ReturnVal = NULL;
534
535 # retrieve attribute name and (possibly) seek arguments
536 if (!$GetMultiple)
537 {
538 $AttribName = strtoupper(array_shift($Args));
539 }
540
541 # if arguments were supplied
542 if (count($Args))
543 {
544 # retrieve index for specified point
545 $Index = $this->PerformSeek($Args, FALSE);
546
547 # if valid index was found
548 if ($Index !== NULL)
549 {
550 # if specified attribute exists
551 if (isset($this->TagAttribs[$Index][$AttribName]))
552 {
553 # retrieve attribute(s) at index to be returned to caller
554 if ($GetMultiple)
555 {
556 $ReturnVal = $this->TagAttribs[$Index];
557 }
558 else
559 {
560 $ReturnVal = $this->TagAttribs[$Index][$AttribName];
561 }
562 }
563 }
564 }
565 else
566 {
567 # if current seek index points to valid tag
568 $SeekIndex = $this->CurrentSeekIndex;
569 if ($SeekIndex >= 0)
570 {
571 # if specified attribute exists
572 if (isset($this->TagAttribs[$SeekIndex][$AttribName]))
573 {
574 # retrieve attribute(s) to be returned to caller
575 if ($GetMultiple)
576 {
577 $ReturnVal = $this->TagAttribs[$SeekIndex];
578 }
579 else
580 {
581 $ReturnVal = $this->TagAttribs[$SeekIndex][$AttribName];
582 }
583 }
584 }
585 }
586
587 # return requested attribute to caller
588 return $ReturnVal;
589 }
590
595 private function RebuildItemList()
596 {
597 # get list of tags with the same parent as current tag
598 $SameParentTags = array_keys($this->TagParents,
599 $this->TagParents[$this->CurrentSeekIndex]);
600
601 # get list of tags with the same name as current tag
602 $SameNameTags = array_keys($this->TagNames,
603 $this->TagNames[$this->CurrentSeekIndex]);
604
605 # intersect lists to get tags with both same name and same parent as current
606 $this->CurrentItemList = array_values(
607 array_intersect($SameNameTags, $SameParentTags));
608
609 # find and save index of current tag within item list
610 $this->CurrentItemIndex = array_search(
611 $this->CurrentSeekIndex, $this->CurrentItemList);
612
613 # save length of item list
614 $this->CurrentItemCount = count($this->CurrentItemList);
615 }
616
620 private function DumpInternalArrays()
621 {
622 foreach ($this->TagNames as $Index => $Name)
623 {
624 printf("[%03d] %-12.12s %03d %-30.30s \n", $Index, $Name,
625 $this->TagParents[$Index], trim($this->TagData[$Index]));
626 }
627 }
628 }
629
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
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