1<?php 2 3/** @file recursiveiteratoriterator.inc 4 * @ingroup SPL 5 * @brief class RecursiveIteratorIterator 6 * @author Marcus Boerger 7 * @date 2003 - 2009 8 * 9 * SPL - Standard PHP Library 10 */ 11 12/** 13 * @brief Iterates through recursive iterators 14 * @author Marcus Boerger 15 * @version 1.2 16 * @since PHP 5.0 17 * 18 * The objects of this class are created by instances of RecursiveIterator. 19 * Elements of those iterators may be traversable themselves. If so these 20 * sub elements are recursed into. 21 */ 22class RecursiveIteratorIterator implements OuterIterator 23{ 24 /** Mode: Only show leaves */ 25 const LEAVES_ONLY = 0; 26 /** Mode: Show parents prior to their children */ 27 const SELF_FIRST = 1; 28 /** Mode: Show all children prior to their parent */ 29 const CHILD_FIRST = 2; 30 31 /** Flag: Catches exceptions during getChildren() calls and simply jumps 32 * to the next element. */ 33 const CATCH_GET_CHILD = 0x00000002; 34 35 private $ait = array(); 36 private $count = 0; 37 private $mode = self::LEAVES_ONLY; 38 private $flags = 0; 39 40 /** Construct from RecursiveIterator 41 * 42 * @param it RecursiveIterator to iterate 43 * @param mode Operation mode (one of): 44 * - LEAVES_ONLY only show leaves 45 * - SELF_FIRST show parents prior to their childs 46 * - CHILD_FIRST show all children prior to their parent 47 * @param flags Control flags, zero or any combination of the following 48 * (since PHP 5.1). 49 * - CATCH_GET_CHILD which catches exceptions during 50 * getChildren() calls and simply jumps to the next 51 * element. 52 */ 53 function __construct(RecursiveIterator $it, $mode = self::LEAVES_ONLY, $flags = 0) 54 { 55 $this->ait[0] = $it; 56 $this->mode = $mode; 57 $this->flags = $flags; 58 } 59 60 /** Rewind to top iterator as set in constructor 61 */ 62 function rewind() 63 { 64 while ($this->count) { 65 unset($this->ait[$this->count--]); 66 $this->endChildren(); 67 } 68 $this->ait[0]->rewind(); 69 $this->ait[0]->recursed = false; 70 callNextElement(true); 71 } 72 73 /** @return whether iterator is valid 74 */ 75 function valid() 76 { 77 $count = $this->count; 78 while ($count) { 79 $it = $this->ait[$count]; 80 if ($it->valid()) { 81 return true; 82 } 83 $count--; 84 $this->endChildren(); 85 } 86 return false; 87 } 88 89 /** @return current key 90 */ 91 function key() 92 { 93 $it = $this->ait[$this->count]; 94 return $it->key(); 95 } 96 97 /** @return current element 98 */ 99 function current() 100 { 101 $it = $this->ait[$this->count]; 102 return $it->current(); 103 } 104 105 /** Forward to next element 106 */ 107 function next() 108 { 109 while ($this->count) { 110 $it = $this->ait[$this->count]; 111 if ($it->valid()) { 112 if (!$it->recursed && callHasChildren()) { 113 $it->recursed = true; 114 try 115 { 116 $sub = callGetChildren(); 117 } 118 catch (Exception $e) 119 { 120 if (!($this->flags & self::CATCH_GET_CHILD)) 121 { 122 throw $e; 123 } 124 $it->next(); 125 continue; 126 } 127 $sub->recursed = false; 128 $sub->rewind(); 129 if ($sub->valid()) { 130 $this->ait[++$this->count] = $sub; 131 if (!$sub instanceof RecursiveIterator) { 132 throw new Exception(get_class($sub).'::getChildren() must return an object that implements RecursiveIterator'); 133 } 134 $this->beginChildren(); 135 return; 136 } 137 unset($sub); 138 } 139 $it->next(); 140 $it->recursed = false; 141 if ($it->valid()) { 142 return; 143 } 144 $it->recursed = false; 145 } 146 if ($this->count) { 147 unset($this->ait[$this->count--]); 148 $it = $this->ait[$this->count]; 149 $this->endChildren(); 150 callNextElement(false); 151 } 152 } 153 callNextElement(true); 154 } 155 156 /** @return Sub Iterator at given level or if unspecified the current sub 157 * Iterator 158 */ 159 function getSubIterator($level = NULL) 160 { 161 if (is_null($level)) { 162 $level = $this->count; 163 } 164 return @$this->ait[$level]; 165 } 166 167 /** 168 * @return The inner iterator 169 */ 170 function getInnerIterator() 171 { 172 return $this->it; 173 } 174 175 /** @return Current Depth (Number of parents) 176 */ 177 function getDepth() 178 { 179 return $this->level; 180 } 181 182 /** @return whether current sub iterators current element has children 183 * @since PHP 5.1 184 */ 185 function callHasChildren() 186 { 187 return $this->ait[$this->count]->hasChildren(); 188 } 189 190 /** @return current sub iterators current children 191 * @since PHP 5.1 192 */ 193 function callGetChildren() 194 { 195 return $this->ait[$this->count]->getChildren(); 196 } 197 198 /** Called right after calling getChildren() and its rewind(). 199 * @since PHP 5.1 200 */ 201 function beginChildren() 202 { 203 } 204 205 /** Called after current child iterator is invalid and right before it 206 * gets destructed. 207 * @since PHP 5.1 208 */ 209 function endChildren() 210 { 211 } 212 213 private function callNextElement($after_move) 214 { 215 if ($this->valid()) 216 { 217 if ($after_move) 218 { 219 if (($this->mode == self::SELF_FIRST && $this->callHasChildren()) 220 || $this->mode == self::LEAVES_ONLY) 221 $this->nextElement(); 222 } 223 else 224 { 225 $this->nextElement(); 226 } 227 } 228 } 229 230 /** Called when the next element is available 231 */ 232 function nextElement() 233 { 234 } 235} 236 237?> 238