1<?php 2/** @file multipleiterator.inc 3 * @ingroup SPL 4 * @brief class MultipleIterator 5 * @author Johannes Schlueter 6 * @author Marcus Boerger 7 * @date 2008 - 2009 8 * 9 * SPL - Standard PHP Library 10 */ 11 12/** @ingroup SPL 13 * @brief Iterator that iterates over several iterators one after the other 14 * @author Johannes Schlueter 15 * @author Marcus Boerger 16 * @version 1.0 17 * @since PHP 5.3 18 */ 19class MultipleIterator implements Iterator 20{ 21 /** Inner Iterators */ 22 private $iterators; 23 24 /** Flags: const MIT_* */ 25 private $flags; 26 27 /** do not require all sub iterators to be valid in iteration */ 28 const MIT_NEED_ANY = 0; 29 30 /** require all sub iterators to be valid in iteration */ 31 const MIT_NEED_ALL = 1; 32 33 /** keys are created from sub iterators position */ 34 const MIT_KEYS_NUMERIC = 0; 35 36 /** keys are created from sub iterators associated infromation */ 37 const MIT_KEYS_ASSOC = 2; 38 39 /** Construct a new empty MultipleIterator 40 * @param flags MIT_* flags 41 */ 42 public function __construct($flags = self::MIT_NEED_ALL|self::MIT_KEYS_NUMERIC) 43 { 44 $this->iterators = new SplObjectStorage(); 45 $this->flags = $flags; 46 } 47 48 /** @return current flags MIT_* */ 49 public function getFlags() 50 { 51 return $this->flags; 52 } 53 54 /** @param $flags new flags. */ 55 public function setFlags($flags) 56 { 57 $this->flags = $flags; 58 } 59 60 /** @param $iter new Iterator to attach. 61 * @param $inf associative info forIteraotr, must be NULL, integer or string 62 * 63 * @throws IllegalValueException if a inf is none of NULL, integer or string 64 * @throws IllegalValueException if a inf is already an associated info 65 */ 66 public function attachIterator(Iterator $iter, $inf = NULL) 67 { 68 69 if (!is_null($inf)) 70 { 71 if (!is_int($inf) && !is_string($inf)) 72 { 73 throw new IllegalValueException('Inf must be NULL, integer or string'); 74 } 75 foreach($this->iterators as $iter) 76 { 77 if ($inf == $this->iterators->getInfo()) 78 { 79 throw new IllegalValueException('Key duplication error'); 80 } 81 } 82 } 83 $this->iterators->attach($iter, $inf); 84 } 85 86 /** @param $iter attached Iterator that should be detached. */ 87 public function detachIterator(Iterator $iter) 88 { 89 $this->iterators->detach($iter); 90 } 91 92 /** @param $iter Iterator to check 93 * @return whether $iter is attached or not 94 */ 95 public function containsIterator(Iterator $iter) 96 { 97 return $this->iterator->contains($iter); 98 } 99 100 /** @return number of attached Iterator instances. */ 101 public function countIterators() 102 { 103 return $this->iterators->count(); 104 } 105 106 /** Rewind all attached Iterator instances. */ 107 public function rewind() 108 { 109 foreach($this->iterators as $iter) 110 { 111 $iter->rewind(); 112 } 113 } 114 115 /** 116 * @return whether all or one sub iterator is valid depending on flags. 117 * In mode MIT_NEED_ALL we expect all sub iterators to be valid and 118 * return flase on the first non valid one. If that flag is not set we 119 * return true on the first valid sub iterator found. If no Iterator 120 * is attached, we always return false. 121 */ 122 public function valid() 123 { 124 if (!sizeof($this->iterators)) { 125 return false; 126 } 127 // The following code is an optimized version that executes as few 128 // valid() calls as necessary and that only checks the flags once. 129 $expect = $this->flags & self::MIT_NEED_ALL ? true : false; 130 foreach($this->iterators as $iter) 131 { 132 if ($expect != $iter->valid()) 133 { 134 return !$expect; 135 } 136 } 137 return $expect; 138 } 139 140 /** Move all attached Iterator instances forward. That is invoke 141 * their next() method regardless of their state. 142 */ 143 public function next() 144 { 145 foreach($this->iterators as $iter) 146 { 147 $iter->next(); 148 } 149 } 150 151 /** @return false if no sub Iterator is attached and an array of 152 * all registered Iterator instances current() result. 153 * @throws RuntimeException if mode MIT_NEED_ALL is set and at least one 154 * attached Iterator is not valid(). 155 * @throws IllegalValueException if a key is NULL and MIT_KEYS_ASSOC is set. 156 */ 157 public function current() 158 { 159 if (!sizeof($this->iterators)) 160 { 161 return false; 162 } 163 $retval = array(); 164 foreach($this->iterators as $iter) 165 { 166 if ($iter->valid()) 167 { 168 if ($this->flags & self::MIT_KEYS_ASSOC) 169 { 170 $key = $this->iterators->getInfo(); 171 if (is_null($key)) 172 { 173 throw new IllegalValueException('Sub-Iterator is associated with NULL'); 174 } 175 $retval[$key] = $iter->current(); 176 } 177 else 178 { 179 $retval[] = $iter->current(); 180 } 181 } 182 else if ($this->flags & self::MIT_NEED_ALL) 183 { 184 throw new RuntimeException('Called current() with non valid sub iterator'); 185 } 186 else 187 { 188 $retval[] = NULL; 189 } 190 } 191 return $retval; 192 } 193 194 /** @return false if no sub Iterator is attached and an array of 195 * all registered Iterator instances key() result. 196 * @throws LogicException if mode MIT_NEED_ALL is set and at least one 197 * attached Iterator is not valid(). 198 */ 199 public function key() 200 { 201 if (!sizeof($this->iterators)) 202 { 203 return false; 204 } 205 $retval = array(); 206 foreach($this->iterators as $iter) 207 { 208 if ($iter->valid()) 209 { 210 $retval[] = $iter->key(); 211 } 212 else if ($this->flags & self::MIT_NEED_ALL) 213 { 214 throw new LogicException('Called key() with non valid sub iterator'); 215 } 216 else 217 { 218 $retval[] = NULL; 219 } 220 } 221 return $retval; 222 } 223} 224