1<?php 2 3/** @file cachingiterator.inc 4 * @ingroup SPL 5 * @brief class CachingIterator 6 * @author Marcus Boerger 7 * @date 2003 - 2009 8 * 9 * SPL - Standard PHP Library 10 */ 11 12/** 13 * @brief Cached iteration over another Iterator 14 * @author Marcus Boerger 15 * @version 1.2 16 * @since PHP 5.0 17 * 18 * This iterator wrapper does a one ahead iteration. This way it knows whether 19 * the inner iterator has one more element. 20 * 21 * @note If you want to convert the elements into strings and the inner 22 * Iterator is an internal Iterator then you need to provide the 23 * flag CALL_TOSTRING to do the conversion when the actual element 24 * is being fetched. Otherwise the conversion would happen with the 25 * already changed iterator. If you do not need this then it you should 26 * omit this flag because it costs unnecessary work and time. 27 */ 28class CachingIterator implements OuterIterator 29{ 30 const CALL_TOSTRING = 0x00000001; 31 const CATCH_GET_CHILD = 0x00000002; 32 const TOSTRING_USE_KEY = 0x00000010; 33 const TOSTRING_USE_CURRENT = 0x00000020; 34 35 private $it; 36 private $current; 37 private $key; 38 private $valid; 39 private $strValue; 40 41 /** Construct from another iterator 42 * 43 * @param it Iterator to cache 44 * @param flags Bitmask: 45 * - CALL_TOSTRING (whether to call __toString() for every element) 46 */ 47 function __construct(Iterator $it, $flags = self::CALL_TOSTRING) 48 { 49 if ((($flags & self::CALL_TOSTRING) && ($flags & (self::TOSTRING_USE_KEY|self::TOSTRING_USE_CURRENT))) 50 || ((flags & (self::CIT_TOSTRING_USE_KEY|self::CIT_TOSTRING_USE_CURRENT)) == (self::CIT_TOSTRING_USE_KEY|self::CIT_TOSTRING_USE_CURRENT))) 51 { 52 throw new InvalidArgumentException('Flags must contain only one of CIT_CALL_TOSTRING, CIT_TOSTRING_USE_KEY, CIT_TOSTRING_USE_CURRENT'); 53 } 54 $this->it = $it; 55 $this->flags = $flags & (0x0000FFFF); 56 $this->next(); 57 } 58 59 /** Rewind the Iterator 60 */ 61 function rewind() 62 { 63 $this->it->rewind(); 64 $this->next(); 65 } 66 67 /** Forward to the next element 68 */ 69 function next() 70 { 71 if ($this->valid = $this->it->valid()) { 72 $this->current = $this->it->current(); 73 $this->key = $this->it->key(); 74 if ($this->flags & self::CALL_TOSTRING) { 75 if (is_object($this->current)) { 76 $this->strValue = $this->current->__toString(); 77 } else { 78 $this->strValue = (string)$this->current; 79 } 80 } 81 } else { 82 $this->current = NULL; 83 $this->key = NULL; 84 $this->strValue = NULL; 85 } 86 $this->it->next(); 87 } 88 89 /** @return whether the iterator is valid 90 */ 91 function valid() 92 { 93 return $this->valid; 94 } 95 96 /** @return whether there is one more element 97 */ 98 function hasNext() 99 { 100 return $this->it->valid(); 101 } 102 103 /** @return the current element 104 */ 105 function current() 106 { 107 return $this->current; 108 } 109 110 /** @return the current key 111 */ 112 function key() 113 { 114 return $this->key; 115 } 116 117 /** Aggregate the inner iterator 118 * 119 * @param func Name of method to invoke 120 * @param params Array of parameters to pass to method 121 */ 122 function __call($func, $params) 123 { 124 return call_user_func_array(array($this->it, $func), $params); 125 } 126 127 /** @return the string represenatation that was generated for the current 128 * element 129 * @throw exception when CALL_TOSTRING was not specified in constructor 130 */ 131 function __toString() 132 { 133 if ($this->flags & self::TOSTRING_USE_KEY) 134 { 135 return $this->key; 136 } 137 else if ($this->flags & self::TOSTRING_USE_CURRENT) 138 { 139 return $this->current; 140 } 141 if (!$this->flags & self::CALL_TOSTRING) 142 { 143 throw new exception('CachingIterator does not fetch string value (see CachingIterator::__construct)'); 144 } 145 return $this->strValue; 146 } 147 148 /** 149 * @return The inner iterator 150 */ 151 function getInnerIterator() 152 { 153 return $this->it; 154 } 155} 156 157?> 158