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?>