summaryrefslogtreecommitdiff
path: root/ext/spl/internal/cachingiterator.inc
blob: 4d4bf8dbff879840f79162bf7c77176dc48acd1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<?php

/** @file cachingiterator.inc
 * @ingroup SPL
 * @brief class CachingIterator
 * @author  Marcus Boerger
 * @date    2003 - 2009
 *
 * SPL - Standard PHP Library
 */

/**
 * @brief   Cached iteration over another Iterator
 * @author  Marcus Boerger
 * @version 1.2
 * @since PHP 5.0
 *
 * This iterator wrapper does a one ahead iteration. This way it knows whether
 * the inner iterator has one more element.
 *
 * @note If you want to convert the elements into strings and the inner 
 *       Iterator is an internal Iterator then you need to provide the 
 *       flag CALL_TOSTRING to do the conversion when the actual element
 *       is being fetched. Otherwise the conversion would happen with the
 *       already changed iterator. If you do not need this then it you should
 *       omit this flag because it costs unnecessary work and time.
 */
class CachingIterator implements OuterIterator
{
	const CALL_TOSTRING        = 0x00000001;
	const CATCH_GET_CHILD      = 0x00000002;
	const TOSTRING_USE_KEY     = 0x00000010;
	const TOSTRING_USE_CURRENT = 0x00000020;

	private $it;
	private $current;
	private $key;
	private $valid;
	private $strValue;

	/** Construct from another iterator
	 *
	 * @param it    Iterator to cache
	 * @param flags Bitmask: 
	 *              - CALL_TOSTRING  (whether to call __toString() for every element)
	 */
	function __construct(Iterator $it, $flags = self::CALL_TOSTRING)
	{
		if ((($flags & self::CALL_TOSTRING) && ($flags & (self::TOSTRING_USE_KEY|self::TOSTRING_USE_CURRENT)))
		|| ((flags & (self::CIT_TOSTRING_USE_KEY|self::CIT_TOSTRING_USE_CURRENT)) == (self::CIT_TOSTRING_USE_KEY|self::CIT_TOSTRING_USE_CURRENT)))
		{
			throw new InvalidArgumentException('Flags must contain only one of CIT_CALL_TOSTRING, CIT_TOSTRING_USE_KEY, CIT_TOSTRING_USE_CURRENT');
		}
		$this->it = $it;
		$this->flags = $flags & (0x0000FFFF);
		$this->next();
	}

	/** Rewind the Iterator
	 */
	function rewind()
	{
		$this->it->rewind();
		$this->next();
	}
	
	/** Forward to the next element
	 */
	function next()
	{
		if ($this->valid = $this->it->valid()) {
			$this->current = $this->it->current();
			$this->key = $this->it->key();
			if ($this->flags & self::CALL_TOSTRING) {
				if (is_object($this->current)) {
					$this->strValue = $this->current->__toString();
				} else {
					$this->strValue = (string)$this->current;
				}
			}
		} else {
			$this->current = NULL;
			$this->key = NULL;
			$this->strValue = NULL;
		}
		$this->it->next();
	}
	
	/** @return whether the iterator is valid
	 */
	function valid()
	{
		return $this->valid;
	}

	/** @return whether there is one more element
	 */
	function hasNext()
	{
		return $this->it->valid();
	}
	
	/** @return the current element
	 */
	function current()
	{
		return $this->current;
	}

	/** @return the current key
	 */
	function key()
	{
		return $this->key;
	}

	/** Aggregate the inner iterator
	 *
	 * @param func    Name of method to invoke
	 * @param params  Array of parameters to pass to method
	 */
	function __call($func, $params)
	{
		return call_user_func_array(array($this->it, $func), $params);
	}
	
	/** @return the string represenatation that was generated for the current 
	 *          element
	 * @throw exception when CALL_TOSTRING was not specified in constructor
	 */
	function __toString()
	{
		if ($this->flags & self::TOSTRING_USE_KEY)
		{
			return $this->key;
		}
		else if ($this->flags & self::TOSTRING_USE_CURRENT)
		{
			return $this->current;
		}
		if (!$this->flags & self::CALL_TOSTRING)
		{
			throw new exception('CachingIterator does not fetch string value (see CachingIterator::__construct)');
		}
		return $this->strValue;
	}
	
	/**
	 * @return The inner iterator
	 */	
	function getInnerIterator()
	{
		return $this->it;
	}
}

?>