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