1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2018 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Marcus Boerger <helly@php.net>                              |
16   +----------------------------------------------------------------------+
17 */
18
19#ifdef HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include "php.h"
24#include "php_ini.h"
25#include "ext/standard/info.h"
26#include "zend_exceptions.h"
27#include "zend_interfaces.h"
28
29#include "php_spl.h"
30#include "spl_functions.h"
31#include "spl_engine.h"
32#include "spl_iterators.h"
33#include "spl_directory.h"
34#include "spl_array.h"
35#include "spl_exceptions.h"
36#include "zend_smart_str.h"
37
38#ifdef accept
39#undef accept
40#endif
41
42PHPAPI zend_class_entry *spl_ce_RecursiveIterator;
43PHPAPI zend_class_entry *spl_ce_RecursiveIteratorIterator;
44PHPAPI zend_class_entry *spl_ce_FilterIterator;
45PHPAPI zend_class_entry *spl_ce_CallbackFilterIterator;
46PHPAPI zend_class_entry *spl_ce_RecursiveFilterIterator;
47PHPAPI zend_class_entry *spl_ce_RecursiveCallbackFilterIterator;
48PHPAPI zend_class_entry *spl_ce_ParentIterator;
49PHPAPI zend_class_entry *spl_ce_SeekableIterator;
50PHPAPI zend_class_entry *spl_ce_LimitIterator;
51PHPAPI zend_class_entry *spl_ce_CachingIterator;
52PHPAPI zend_class_entry *spl_ce_RecursiveCachingIterator;
53PHPAPI zend_class_entry *spl_ce_OuterIterator;
54PHPAPI zend_class_entry *spl_ce_IteratorIterator;
55PHPAPI zend_class_entry *spl_ce_NoRewindIterator;
56PHPAPI zend_class_entry *spl_ce_InfiniteIterator;
57PHPAPI zend_class_entry *spl_ce_EmptyIterator;
58PHPAPI zend_class_entry *spl_ce_AppendIterator;
59PHPAPI zend_class_entry *spl_ce_RegexIterator;
60PHPAPI zend_class_entry *spl_ce_RecursiveRegexIterator;
61PHPAPI zend_class_entry *spl_ce_RecursiveTreeIterator;
62
63ZEND_BEGIN_ARG_INFO(arginfo_recursive_it_void, 0)
64ZEND_END_ARG_INFO()
65
66static const zend_function_entry spl_funcs_RecursiveIterator[] = {
67	SPL_ABSTRACT_ME(RecursiveIterator, hasChildren,  arginfo_recursive_it_void)
68	SPL_ABSTRACT_ME(RecursiveIterator, getChildren,  arginfo_recursive_it_void)
69	PHP_FE_END
70};
71
72typedef enum {
73	RIT_LEAVES_ONLY = 0,
74	RIT_SELF_FIRST  = 1,
75	RIT_CHILD_FIRST = 2
76} RecursiveIteratorMode;
77
78#define RIT_CATCH_GET_CHILD CIT_CATCH_GET_CHILD
79
80typedef enum {
81	RTIT_BYPASS_CURRENT = 4,
82	RTIT_BYPASS_KEY	    = 8
83} RecursiveTreeIteratorFlags;
84
85typedef enum {
86	RS_NEXT  = 0,
87	RS_TEST  = 1,
88	RS_SELF  = 2,
89	RS_CHILD = 3,
90	RS_START = 4
91} RecursiveIteratorState;
92
93typedef struct _spl_sub_iterator {
94	zend_object_iterator    *iterator;
95	zval                    zobject;
96	zend_class_entry        *ce;
97	RecursiveIteratorState  state;
98} spl_sub_iterator;
99
100typedef struct _spl_recursive_it_object {
101	spl_sub_iterator         *iterators;
102	int                      level;
103	RecursiveIteratorMode    mode;
104	int                      flags;
105	int                      max_depth;
106	zend_bool                in_iteration;
107	zend_function            *beginIteration;
108	zend_function            *endIteration;
109	zend_function            *callHasChildren;
110	zend_function            *callGetChildren;
111	zend_function            *beginChildren;
112	zend_function            *endChildren;
113	zend_function            *nextElement;
114	zend_class_entry         *ce;
115	smart_str                prefix[6];
116	smart_str                postfix[1];
117	zend_object              std;
118} spl_recursive_it_object;
119
120typedef struct _spl_recursive_it_iterator {
121	zend_object_iterator   intern;
122} spl_recursive_it_iterator;
123
124static zend_object_handlers spl_handlers_rec_it_it;
125static zend_object_handlers spl_handlers_dual_it;
126
127static inline spl_recursive_it_object *spl_recursive_it_from_obj(zend_object *obj) /* {{{ */ {
128	return (spl_recursive_it_object*)((char*)(obj) - XtOffsetOf(spl_recursive_it_object, std));
129}
130/* }}} */
131
132#define Z_SPLRECURSIVE_IT_P(zv)  spl_recursive_it_from_obj(Z_OBJ_P((zv)))
133
134#define SPL_FETCH_AND_CHECK_DUAL_IT(var, objzval) 												\
135	do { 																						\
136		spl_dual_it_object *it = Z_SPLDUAL_IT_P(objzval); 										\
137		if (it->dit_type == DIT_Unknown) { 														\
138			zend_throw_exception_ex(spl_ce_LogicException, 0, 						\
139				"The object is in an invalid state as the parent constructor was not called"); 	\
140			return; 																			\
141		} 																						\
142		(var) = it; 																			\
143	} while (0)
144
145#define SPL_FETCH_SUB_ELEMENT(var, object, element) \
146	do { \
147		if(!(object)->iterators) { \
148			zend_throw_exception_ex(spl_ce_LogicException, 0, \
149				"The object is in an invalid state as the parent constructor was not called"); \
150			return; \
151		} \
152		(var) = (object)->iterators[(object)->level].element; \
153	} while (0)
154
155#define SPL_FETCH_SUB_ELEMENT_ADDR(var, object, element) \
156	do { \
157		if(!(object)->iterators) { \
158			zend_throw_exception_ex(spl_ce_LogicException, 0, \
159				"The object is in an invalid state as the parent constructor was not called"); \
160			return; \
161		} \
162		(var) = &(object)->iterators[(object)->level].element; \
163	} while (0)
164
165#define SPL_FETCH_SUB_ITERATOR(var, object) SPL_FETCH_SUB_ELEMENT(var, object, iterator)
166
167
168static void spl_recursive_it_dtor(zend_object_iterator *_iter)
169{
170	spl_recursive_it_iterator *iter   = (spl_recursive_it_iterator*)_iter;
171	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(&iter->intern.data);
172	zend_object_iterator      *sub_iter;
173
174	while (object->level > 0) {
175		if (!Z_ISUNDEF(object->iterators[object->level].zobject)) {
176			sub_iter = object->iterators[object->level].iterator;
177			zend_iterator_dtor(sub_iter);
178			zval_ptr_dtor(&object->iterators[object->level].zobject);
179		}
180		object->level--;
181	}
182	object->iterators = erealloc(object->iterators, sizeof(spl_sub_iterator));
183	object->level = 0;
184
185	zval_ptr_dtor(&iter->intern.data);
186}
187
188static int spl_recursive_it_valid_ex(spl_recursive_it_object *object, zval *zthis)
189{
190	zend_object_iterator      *sub_iter;
191	int                       level = object->level;
192
193	if(!object->iterators) {
194		return FAILURE;
195	}
196	while (level >=0) {
197		sub_iter = object->iterators[level].iterator;
198		if (sub_iter->funcs->valid(sub_iter) == SUCCESS) {
199			return SUCCESS;
200		}
201		level--;
202	}
203	if (object->endIteration && object->in_iteration) {
204		zend_call_method_with_0_params(zthis, object->ce, &object->endIteration, "endIteration", NULL);
205	}
206	object->in_iteration = 0;
207	return FAILURE;
208}
209
210static int spl_recursive_it_valid(zend_object_iterator *iter)
211{
212	return spl_recursive_it_valid_ex(Z_SPLRECURSIVE_IT_P(&iter->data), &iter->data);
213}
214
215static zval *spl_recursive_it_get_current_data(zend_object_iterator *iter)
216{
217	spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(&iter->data);
218	zend_object_iterator *sub_iter = object->iterators[object->level].iterator;
219
220	return sub_iter->funcs->get_current_data(sub_iter);
221}
222
223static void spl_recursive_it_get_current_key(zend_object_iterator *iter, zval *key)
224{
225	spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(&iter->data);
226	zend_object_iterator *sub_iter = object->iterators[object->level].iterator;
227
228	if (sub_iter->funcs->get_current_key) {
229		sub_iter->funcs->get_current_key(sub_iter, key);
230	} else {
231		ZVAL_LONG(key, iter->index);
232	}
233}
234
235static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zval *zthis)
236{
237	zend_object_iterator      *iterator;
238	zval                      *zobject;
239	zend_class_entry          *ce;
240	zval                      retval, child;
241	zend_object_iterator      *sub_iter;
242	int                       has_children;
243
244	SPL_FETCH_SUB_ITERATOR(iterator, object);
245
246	while (!EG(exception)) {
247next_step:
248		iterator = object->iterators[object->level].iterator;
249		switch (object->iterators[object->level].state) {
250			case RS_NEXT:
251				iterator->funcs->move_forward(iterator);
252				if (EG(exception)) {
253					if (!(object->flags & RIT_CATCH_GET_CHILD)) {
254						return;
255					} else {
256						zend_clear_exception();
257					}
258				}
259				/* fall through */
260			case RS_START:
261				if (iterator->funcs->valid(iterator) == FAILURE) {
262					break;
263				}
264				object->iterators[object->level].state = RS_TEST;
265				/* break; */
266			case RS_TEST:
267				ce = object->iterators[object->level].ce;
268				zobject = &object->iterators[object->level].zobject;
269				if (object->callHasChildren) {
270					zend_call_method_with_0_params(zthis, object->ce, &object->callHasChildren, "callHasChildren", &retval);
271				} else {
272					zend_call_method_with_0_params(zobject, ce, NULL, "haschildren", &retval);
273				}
274				if (EG(exception)) {
275					if (!(object->flags & RIT_CATCH_GET_CHILD)) {
276						object->iterators[object->level].state = RS_NEXT;
277						return;
278					} else {
279						zend_clear_exception();
280					}
281				}
282				if (Z_TYPE(retval) != IS_UNDEF) {
283					has_children = zend_is_true(&retval);
284					zval_ptr_dtor(&retval);
285					if (has_children) {
286						if (object->max_depth == -1 || object->max_depth > object->level) {
287							switch (object->mode) {
288							case RIT_LEAVES_ONLY:
289							case RIT_CHILD_FIRST:
290								object->iterators[object->level].state = RS_CHILD;
291								goto next_step;
292							case RIT_SELF_FIRST:
293								object->iterators[object->level].state = RS_SELF;
294								goto next_step;
295							}
296						} else {
297							/* do not recurse into */
298							if (object->mode == RIT_LEAVES_ONLY) {
299								/* this is not a leave, so skip it */
300								object->iterators[object->level].state = RS_NEXT;
301								goto next_step;
302							}
303						}
304					}
305				}
306				if (object->nextElement) {
307					zend_call_method_with_0_params(zthis, object->ce, &object->nextElement, "nextelement", NULL);
308				}
309				object->iterators[object->level].state = RS_NEXT;
310				if (EG(exception)) {
311					if (!(object->flags & RIT_CATCH_GET_CHILD)) {
312						return;
313					} else {
314						zend_clear_exception();
315					}
316				}
317				return /* self */;
318			case RS_SELF:
319				if (object->nextElement && (object->mode == RIT_SELF_FIRST || object->mode == RIT_CHILD_FIRST)) {
320					zend_call_method_with_0_params(zthis, object->ce, &object->nextElement, "nextelement", NULL);
321				}
322				if (object->mode == RIT_SELF_FIRST) {
323					object->iterators[object->level].state = RS_CHILD;
324				} else {
325					object->iterators[object->level].state = RS_NEXT;
326				}
327				return /* self */;
328			case RS_CHILD:
329				ce = object->iterators[object->level].ce;
330				zobject = &object->iterators[object->level].zobject;
331				if (object->callGetChildren) {
332					zend_call_method_with_0_params(zthis, object->ce, &object->callGetChildren, "callGetChildren", &child);
333				} else {
334					zend_call_method_with_0_params(zobject, ce, NULL, "getchildren", &child);
335				}
336
337				if (EG(exception)) {
338					if (!(object->flags & RIT_CATCH_GET_CHILD)) {
339						return;
340					} else {
341						zend_clear_exception();
342						zval_ptr_dtor(&child);
343						object->iterators[object->level].state = RS_NEXT;
344						goto next_step;
345					}
346				}
347
348				if (Z_TYPE(child) == IS_UNDEF || Z_TYPE(child) != IS_OBJECT ||
349						!((ce = Z_OBJCE(child)) && instanceof_function(ce, spl_ce_RecursiveIterator))) {
350					zval_ptr_dtor(&child);
351					zend_throw_exception(spl_ce_UnexpectedValueException, "Objects returned by RecursiveIterator::getChildren() must implement RecursiveIterator", 0);
352					return;
353				}
354
355				if (object->mode == RIT_CHILD_FIRST) {
356					object->iterators[object->level].state = RS_SELF;
357				} else {
358					object->iterators[object->level].state = RS_NEXT;
359				}
360				object->iterators = erealloc(object->iterators, sizeof(spl_sub_iterator) * (++object->level+1));
361				sub_iter = ce->get_iterator(ce, &child, 0);
362				ZVAL_COPY_VALUE(&object->iterators[object->level].zobject, &child);
363				object->iterators[object->level].iterator = sub_iter;
364				object->iterators[object->level].ce = ce;
365				object->iterators[object->level].state = RS_START;
366				if (sub_iter->funcs->rewind) {
367					sub_iter->funcs->rewind(sub_iter);
368				}
369				if (object->beginChildren) {
370					zend_call_method_with_0_params(zthis, object->ce, &object->beginChildren, "beginchildren", NULL);
371					if (EG(exception)) {
372						if (!(object->flags & RIT_CATCH_GET_CHILD)) {
373							return;
374						} else {
375							zend_clear_exception();
376						}
377					}
378				}
379				goto next_step;
380		}
381		/* no more elements */
382		if (object->level > 0) {
383			if (object->endChildren) {
384				zend_call_method_with_0_params(zthis, object->ce, &object->endChildren, "endchildren", NULL);
385				if (EG(exception)) {
386					if (!(object->flags & RIT_CATCH_GET_CHILD)) {
387						return;
388					} else {
389						zend_clear_exception();
390					}
391				}
392			}
393			if (object->level > 0) {
394				zval garbage;
395				ZVAL_COPY_VALUE(&garbage, &object->iterators[object->level].zobject);
396				ZVAL_UNDEF(&object->iterators[object->level].zobject);
397				zval_ptr_dtor(&garbage);
398				zend_iterator_dtor(iterator);
399				object->level--;
400			}
401		} else {
402			return; /* done completeley */
403		}
404	}
405}
406
407static void spl_recursive_it_rewind_ex(spl_recursive_it_object *object, zval *zthis)
408{
409	zend_object_iterator *sub_iter;
410
411	SPL_FETCH_SUB_ITERATOR(sub_iter, object);
412
413	while (object->level) {
414		sub_iter = object->iterators[object->level].iterator;
415		zend_iterator_dtor(sub_iter);
416		zval_ptr_dtor(&object->iterators[object->level--].zobject);
417		if (!EG(exception) && (!object->endChildren || object->endChildren->common.scope != spl_ce_RecursiveIteratorIterator)) {
418			zend_call_method_with_0_params(zthis, object->ce, &object->endChildren, "endchildren", NULL);
419		}
420	}
421	object->iterators = erealloc(object->iterators, sizeof(spl_sub_iterator));
422	object->iterators[0].state = RS_START;
423	sub_iter = object->iterators[0].iterator;
424	if (sub_iter->funcs->rewind) {
425		sub_iter->funcs->rewind(sub_iter);
426	}
427	if (!EG(exception) && object->beginIteration && !object->in_iteration) {
428		zend_call_method_with_0_params(zthis, object->ce, &object->beginIteration, "beginIteration", NULL);
429	}
430	object->in_iteration = 1;
431	spl_recursive_it_move_forward_ex(object, zthis);
432}
433
434static void spl_recursive_it_move_forward(zend_object_iterator *iter)
435{
436	spl_recursive_it_move_forward_ex(Z_SPLRECURSIVE_IT_P(&iter->data), &iter->data);
437}
438
439static void spl_recursive_it_rewind(zend_object_iterator *iter)
440{
441	spl_recursive_it_rewind_ex(Z_SPLRECURSIVE_IT_P(&iter->data), &iter->data);
442}
443
444static const zend_object_iterator_funcs spl_recursive_it_iterator_funcs = {
445	spl_recursive_it_dtor,
446	spl_recursive_it_valid,
447	spl_recursive_it_get_current_data,
448	spl_recursive_it_get_current_key,
449	spl_recursive_it_move_forward,
450	spl_recursive_it_rewind,
451	NULL
452};
453
454static zend_object_iterator *spl_recursive_it_get_iterator(zend_class_entry *ce, zval *zobject, int by_ref)
455{
456	spl_recursive_it_iterator *iterator;
457	spl_recursive_it_object *object;
458
459	if (by_ref) {
460		zend_throw_exception(spl_ce_RuntimeException, "An iterator cannot be used with foreach by reference", 0);
461		return NULL;
462	}
463	iterator = emalloc(sizeof(spl_recursive_it_iterator));
464	object   = Z_SPLRECURSIVE_IT_P(zobject);
465	if (object->iterators == NULL) {
466		zend_error(E_ERROR, "The object to be iterated is in an invalid state: "
467				"the parent constructor has not been called");
468	}
469
470	zend_iterator_init((zend_object_iterator*)iterator);
471
472	ZVAL_COPY(&iterator->intern.data, zobject);
473	iterator->intern.funcs = &spl_recursive_it_iterator_funcs;
474	return (zend_object_iterator*)iterator;
475}
476
477static void spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_base, zend_class_entry *ce_inner, recursive_it_it_type rit_type)
478{
479	zval *object = getThis();
480	spl_recursive_it_object *intern;
481	zval *iterator;
482	zend_class_entry *ce_iterator;
483	zend_long mode, flags;
484	zend_error_handling error_handling;
485	zval caching_it, aggregate_retval;
486
487	zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling);
488
489	switch (rit_type) {
490		case RIT_RecursiveTreeIterator: {
491			zval caching_it_flags, *user_caching_it_flags = NULL;
492			mode = RIT_SELF_FIRST;
493			flags = RTIT_BYPASS_KEY;
494
495			if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "o|lzl", &iterator, &flags, &user_caching_it_flags, &mode) == SUCCESS) {
496				if (instanceof_function(Z_OBJCE_P(iterator), zend_ce_aggregate)) {
497					zend_call_method_with_0_params(iterator, Z_OBJCE_P(iterator), &Z_OBJCE_P(iterator)->iterator_funcs_ptr->zf_new_iterator, "getiterator", &aggregate_retval);
498					iterator = &aggregate_retval;
499				} else {
500					Z_ADDREF_P(iterator);
501				}
502
503				if (user_caching_it_flags) {
504					ZVAL_COPY(&caching_it_flags, user_caching_it_flags);
505				} else {
506					ZVAL_LONG(&caching_it_flags, CIT_CATCH_GET_CHILD);
507				}
508				spl_instantiate_arg_ex2(spl_ce_RecursiveCachingIterator, &caching_it, iterator, &caching_it_flags);
509				zval_ptr_dtor(&caching_it_flags);
510
511				zval_ptr_dtor(iterator);
512				iterator = &caching_it;
513			} else {
514				iterator = NULL;
515			}
516			break;
517		}
518		case RIT_RecursiveIteratorIterator:
519		default: {
520			mode = RIT_LEAVES_ONLY;
521			flags = 0;
522
523			if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "o|ll", &iterator, &mode, &flags) == SUCCESS) {
524				if (instanceof_function(Z_OBJCE_P(iterator), zend_ce_aggregate)) {
525					zend_call_method_with_0_params(iterator, Z_OBJCE_P(iterator), &Z_OBJCE_P(iterator)->iterator_funcs_ptr->zf_new_iterator, "getiterator", &aggregate_retval);
526					iterator = &aggregate_retval;
527				} else {
528					Z_ADDREF_P(iterator);
529				}
530			} else {
531				iterator = NULL;
532			}
533			break;
534		}
535	}
536	if (!iterator || !instanceof_function(Z_OBJCE_P(iterator), spl_ce_RecursiveIterator)) {
537		if (iterator) {
538			zval_ptr_dtor(iterator);
539		}
540		zend_throw_exception(spl_ce_InvalidArgumentException, "An instance of RecursiveIterator or IteratorAggregate creating it is required", 0);
541		zend_restore_error_handling(&error_handling);
542		return;
543	}
544
545	intern = Z_SPLRECURSIVE_IT_P(object);
546	intern->iterators = emalloc(sizeof(spl_sub_iterator));
547	intern->level = 0;
548	intern->mode = mode;
549	intern->flags = (int)flags;
550	intern->max_depth = -1;
551	intern->in_iteration = 0;
552	intern->ce = Z_OBJCE_P(object);
553
554	intern->beginIteration = zend_hash_str_find_ptr(&intern->ce->function_table, "beginiteration", sizeof("beginiteration") - 1);
555	if (intern->beginIteration->common.scope == ce_base) {
556		intern->beginIteration = NULL;
557	}
558	intern->endIteration = zend_hash_str_find_ptr(&intern->ce->function_table, "enditeration", sizeof("enditeration") - 1);
559	if (intern->endIteration->common.scope == ce_base) {
560		intern->endIteration = NULL;
561	}
562	intern->callHasChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "callhaschildren", sizeof("callHasChildren") - 1);
563	if (intern->callHasChildren->common.scope == ce_base) {
564		intern->callHasChildren = NULL;
565	}
566	intern->callGetChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "callgetchildren", sizeof("callGetChildren") - 1);
567	if (intern->callGetChildren->common.scope == ce_base) {
568		intern->callGetChildren = NULL;
569	}
570	intern->beginChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "beginchildren", sizeof("beginchildren") - 1);
571	if (intern->beginChildren->common.scope == ce_base) {
572		intern->beginChildren = NULL;
573	}
574	intern->endChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "endchildren", sizeof("endchildren") - 1);
575	if (intern->endChildren->common.scope == ce_base) {
576		intern->endChildren = NULL;
577	}
578	intern->nextElement = zend_hash_str_find_ptr(&intern->ce->function_table, "nextelement", sizeof("nextElement") - 1);
579	if (intern->nextElement->common.scope == ce_base) {
580		intern->nextElement = NULL;
581	}
582
583	ce_iterator = Z_OBJCE_P(iterator); /* respect inheritance, don't use spl_ce_RecursiveIterator */
584	intern->iterators[0].iterator = ce_iterator->get_iterator(ce_iterator, iterator, 0);
585	ZVAL_COPY_VALUE(&intern->iterators[0].zobject, iterator);
586	intern->iterators[0].ce = ce_iterator;
587	intern->iterators[0].state = RS_START;
588
589	zend_restore_error_handling(&error_handling);
590
591	if (EG(exception)) {
592		zend_object_iterator *sub_iter;
593
594		while (intern->level >= 0) {
595			sub_iter = intern->iterators[intern->level].iterator;
596			zend_iterator_dtor(sub_iter);
597			zval_ptr_dtor(&intern->iterators[intern->level--].zobject);
598		}
599		efree(intern->iterators);
600		intern->iterators = NULL;
601	}
602}
603
604/* {{{ proto RecursiveIteratorIterator::__construct(RecursiveIterator|IteratorAggregate it [, int mode = RIT_LEAVES_ONLY [, int flags = 0]]) throws InvalidArgumentException
605   Creates a RecursiveIteratorIterator from a RecursiveIterator. */
606SPL_METHOD(RecursiveIteratorIterator, __construct)
607{
608	spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveIteratorIterator, zend_ce_iterator, RIT_RecursiveIteratorIterator);
609} /* }}} */
610
611/* {{{ proto void RecursiveIteratorIterator::rewind()
612   Rewind the iterator to the first element of the top level inner iterator. */
613SPL_METHOD(RecursiveIteratorIterator, rewind)
614{
615	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
616
617	if (zend_parse_parameters_none() == FAILURE) {
618		return;
619	}
620
621	spl_recursive_it_rewind_ex(object, getThis());
622} /* }}} */
623
624/* {{{ proto bool RecursiveIteratorIterator::valid()
625   Check whether the current position is valid */
626SPL_METHOD(RecursiveIteratorIterator, valid)
627{
628	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
629
630	if (zend_parse_parameters_none() == FAILURE) {
631		return;
632	}
633
634	RETURN_BOOL(spl_recursive_it_valid_ex(object, getThis()) == SUCCESS);
635} /* }}} */
636
637/* {{{ proto mixed RecursiveIteratorIterator::key()
638   Access the current key */
639SPL_METHOD(RecursiveIteratorIterator, key)
640{
641	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
642	zend_object_iterator      *iterator;
643
644	if (zend_parse_parameters_none() == FAILURE) {
645		return;
646	}
647
648	SPL_FETCH_SUB_ITERATOR(iterator, object);
649
650	if (iterator->funcs->get_current_key) {
651		iterator->funcs->get_current_key(iterator, return_value);
652	} else {
653		RETURN_NULL();
654	}
655} /* }}} */
656
657/* {{{ proto mixed RecursiveIteratorIterator::current()
658   Access the current element value */
659SPL_METHOD(RecursiveIteratorIterator, current)
660{
661	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
662	zend_object_iterator      *iterator;
663	zval                      *data;
664
665	if (zend_parse_parameters_none() == FAILURE) {
666		return;
667	}
668
669	SPL_FETCH_SUB_ITERATOR(iterator, object);
670
671	data = iterator->funcs->get_current_data(iterator);
672	if (data) {
673		ZVAL_COPY_DEREF(return_value, data);
674	}
675} /* }}} */
676
677/* {{{ proto void RecursiveIteratorIterator::next()
678   Move forward to the next element */
679SPL_METHOD(RecursiveIteratorIterator, next)
680{
681	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
682
683	if (zend_parse_parameters_none() == FAILURE) {
684		return;
685	}
686
687	spl_recursive_it_move_forward_ex(object, getThis());
688} /* }}} */
689
690/* {{{ proto int RecursiveIteratorIterator::getDepth()
691   Get the current depth of the recursive iteration */
692SPL_METHOD(RecursiveIteratorIterator, getDepth)
693{
694	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
695
696	if (zend_parse_parameters_none() == FAILURE) {
697		return;
698	}
699
700	RETURN_LONG(object->level);
701} /* }}} */
702
703/* {{{ proto RecursiveIterator RecursiveIteratorIterator::getSubIterator([int level])
704   The current active sub iterator or the iterator at specified level */
705SPL_METHOD(RecursiveIteratorIterator, getSubIterator)
706{
707	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
708	zend_long  level = object->level;
709	zval *value;
710
711	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &level) == FAILURE) {
712		return;
713	}
714	if (level < 0 || level > object->level) {
715		RETURN_NULL();
716	}
717
718	if(!object->iterators) {
719		zend_throw_exception_ex(spl_ce_LogicException, 0,
720			"The object is in an invalid state as the parent constructor was not called");
721		return;
722	}
723
724	value = &object->iterators[level].zobject;
725	ZVAL_COPY_DEREF(return_value, value);
726} /* }}} */
727
728/* {{{ proto RecursiveIterator RecursiveIteratorIterator::getInnerIterator()
729   The current active sub iterator */
730SPL_METHOD(RecursiveIteratorIterator, getInnerIterator)
731{
732	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
733	zval      *zobject;
734
735	if (zend_parse_parameters_none() == FAILURE) {
736		return;
737	}
738
739	SPL_FETCH_SUB_ELEMENT_ADDR(zobject, object, zobject);
740
741	ZVAL_COPY_DEREF(return_value, zobject);
742} /* }}} */
743
744/* {{{ proto RecursiveIterator RecursiveIteratorIterator::beginIteration()
745   Called when iteration begins (after first rewind() call) */
746SPL_METHOD(RecursiveIteratorIterator, beginIteration)
747{
748	if (zend_parse_parameters_none() == FAILURE) {
749		return;
750	}
751	/* nothing to do */
752} /* }}} */
753
754/* {{{ proto RecursiveIterator RecursiveIteratorIterator::endIteration()
755   Called when iteration ends (when valid() first returns false */
756SPL_METHOD(RecursiveIteratorIterator, endIteration)
757{
758	if (zend_parse_parameters_none() == FAILURE) {
759		return;
760	}
761	/* nothing to do */
762} /* }}} */
763
764/* {{{ proto bool RecursiveIteratorIterator::callHasChildren()
765   Called for each element to test whether it has children */
766SPL_METHOD(RecursiveIteratorIterator, callHasChildren)
767{
768	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
769	zend_class_entry *ce;
770	zval *zobject;
771
772	if (zend_parse_parameters_none() == FAILURE) {
773		return;
774	}
775
776	if (!object->iterators) {
777		RETURN_NULL();
778	}
779
780	SPL_FETCH_SUB_ELEMENT(ce, object, ce);
781
782	zobject = &object->iterators[object->level].zobject;
783	if (Z_TYPE_P(zobject) == IS_UNDEF) {
784		RETURN_FALSE;
785	} else {
786		zend_call_method_with_0_params(zobject, ce, NULL, "haschildren", return_value);
787		if (Z_TYPE_P(return_value) == IS_UNDEF) {
788			RETURN_FALSE;
789		}
790	}
791} /* }}} */
792
793/* {{{ proto RecursiveIterator RecursiveIteratorIterator::callGetChildren()
794   Return children of current element */
795SPL_METHOD(RecursiveIteratorIterator, callGetChildren)
796{
797	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
798	zend_class_entry *ce;
799	zval *zobject;
800
801	if (zend_parse_parameters_none() == FAILURE) {
802		return;
803	}
804
805	SPL_FETCH_SUB_ELEMENT(ce, object, ce);
806
807	zobject = &object->iterators[object->level].zobject;
808	if (Z_TYPE_P(zobject) == IS_UNDEF) {
809		return;
810	} else {
811		zend_call_method_with_0_params(zobject, ce, NULL, "getchildren", return_value);
812		if (Z_TYPE_P(return_value) == IS_UNDEF) {
813			RETURN_NULL();
814		}
815	}
816} /* }}} */
817
818/* {{{ proto void RecursiveIteratorIterator::beginChildren()
819   Called when recursing one level down */
820SPL_METHOD(RecursiveIteratorIterator, beginChildren)
821{
822	if (zend_parse_parameters_none() == FAILURE) {
823		return;
824	}
825	/* nothing to do */
826} /* }}} */
827
828/* {{{ proto void RecursiveIteratorIterator::endChildren()
829   Called when end recursing one level */
830SPL_METHOD(RecursiveIteratorIterator, endChildren)
831{
832	if (zend_parse_parameters_none() == FAILURE) {
833		return;
834	}
835	/* nothing to do */
836} /* }}} */
837
838/* {{{ proto void RecursiveIteratorIterator::nextElement()
839   Called when the next element is available */
840SPL_METHOD(RecursiveIteratorIterator, nextElement)
841{
842	if (zend_parse_parameters_none() == FAILURE) {
843		return;
844	}
845	/* nothing to do */
846} /* }}} */
847
848/* {{{ proto void RecursiveIteratorIterator::setMaxDepth([$max_depth = -1])
849   Set the maximum allowed depth (or any depth if pmax_depth = -1] */
850SPL_METHOD(RecursiveIteratorIterator, setMaxDepth)
851{
852	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
853	zend_long  max_depth = -1;
854
855	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &max_depth) == FAILURE) {
856		return;
857	}
858	if (max_depth < -1) {
859		zend_throw_exception(spl_ce_OutOfRangeException, "Parameter max_depth must be >= -1", 0);
860		return;
861	} else if (max_depth > INT_MAX) {
862		max_depth = INT_MAX;
863	}
864
865	object->max_depth = (int)max_depth;
866} /* }}} */
867
868/* {{{ proto int|false RecursiveIteratorIterator::getMaxDepth()
869   Return the maximum accepted depth or false if any depth is allowed */
870SPL_METHOD(RecursiveIteratorIterator, getMaxDepth)
871{
872	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
873
874	if (zend_parse_parameters_none() == FAILURE) {
875		return;
876	}
877
878	if (object->max_depth == -1) {
879		RETURN_FALSE;
880	} else {
881		RETURN_LONG(object->max_depth);
882	}
883} /* }}} */
884
885static union _zend_function *spl_recursive_it_get_method(zend_object **zobject, zend_string *method, const zval *key)
886{
887	union _zend_function    *function_handler;
888	spl_recursive_it_object *object = spl_recursive_it_from_obj(*zobject);
889	zend_long                     level = object->level;
890	zval                    *zobj;
891
892	if (!object->iterators) {
893		php_error_docref(NULL, E_ERROR, "The %s instance wasn't initialized properly", ZSTR_VAL((*zobject)->ce->name));
894	}
895	zobj = &object->iterators[level].zobject;
896
897	function_handler = zend_std_get_method(zobject, method, key);
898	if (!function_handler) {
899		if ((function_handler = zend_hash_find_ptr(&Z_OBJCE_P(zobj)->function_table, method)) == NULL) {
900			if (Z_OBJ_HT_P(zobj)->get_method) {
901				*zobject = Z_OBJ_P(zobj);
902				function_handler = (*zobject)->handlers->get_method(zobject, method, key);
903			}
904		} else {
905			*zobject = Z_OBJ_P(zobj);
906		}
907	}
908	return function_handler;
909}
910
911/* {{{ spl_RecursiveIteratorIterator_dtor */
912static void spl_RecursiveIteratorIterator_dtor(zend_object *_object)
913{
914	spl_recursive_it_object *object = spl_recursive_it_from_obj(_object);
915	zend_object_iterator *sub_iter;
916
917	/* call standard dtor */
918	zend_objects_destroy_object(_object);
919
920	if (object->iterators) {
921		while (object->level >= 0) {
922			sub_iter = object->iterators[object->level].iterator;
923			zend_iterator_dtor(sub_iter);
924			zval_ptr_dtor(&object->iterators[object->level--].zobject);
925		}
926		efree(object->iterators);
927		object->iterators = NULL;
928	}
929}
930/* }}} */
931
932/* {{{ spl_RecursiveIteratorIterator_free_storage */
933static void spl_RecursiveIteratorIterator_free_storage(zend_object *_object)
934{
935	spl_recursive_it_object *object = spl_recursive_it_from_obj(_object);
936
937	if (object->iterators) {
938		efree(object->iterators);
939		object->iterators = NULL;
940		object->level     = 0;
941	}
942
943	zend_object_std_dtor(&object->std);
944	smart_str_free(&object->prefix[0]);
945	smart_str_free(&object->prefix[1]);
946	smart_str_free(&object->prefix[2]);
947	smart_str_free(&object->prefix[3]);
948	smart_str_free(&object->prefix[4]);
949	smart_str_free(&object->prefix[5]);
950
951	smart_str_free(&object->postfix[0]);
952}
953/* }}} */
954
955/* {{{ spl_RecursiveIteratorIterator_new_ex */
956static zend_object *spl_RecursiveIteratorIterator_new_ex(zend_class_entry *class_type, int init_prefix)
957{
958	spl_recursive_it_object *intern;
959
960	intern = zend_object_alloc(sizeof(spl_recursive_it_object), class_type);
961
962	if (init_prefix) {
963		smart_str_appendl(&intern->prefix[0], "",    0);
964		smart_str_appendl(&intern->prefix[1], "| ",  2);
965		smart_str_appendl(&intern->prefix[2], "  ",  2);
966		smart_str_appendl(&intern->prefix[3], "|-",  2);
967		smart_str_appendl(&intern->prefix[4], "\\-", 2);
968		smart_str_appendl(&intern->prefix[5], "",    0);
969
970		smart_str_appendl(&intern->postfix[0], "",    0);
971	}
972
973	zend_object_std_init(&intern->std, class_type);
974	object_properties_init(&intern->std, class_type);
975
976	intern->std.handlers = &spl_handlers_rec_it_it;
977	return &intern->std;
978}
979/* }}} */
980
981/* {{{ spl_RecursiveIteratorIterator_new */
982static zend_object *spl_RecursiveIteratorIterator_new(zend_class_entry *class_type)
983{
984	return spl_RecursiveIteratorIterator_new_ex(class_type, 0);
985}
986/* }}} */
987
988/* {{{ spl_RecursiveTreeIterator_new */
989static zend_object *spl_RecursiveTreeIterator_new(zend_class_entry *class_type)
990{
991	return spl_RecursiveIteratorIterator_new_ex(class_type, 1);
992}
993/* }}} */
994
995ZEND_BEGIN_ARG_INFO_EX(arginfo_recursive_it___construct, 0, 0, 1)
996	ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
997	ZEND_ARG_INFO(0, mode)
998	ZEND_ARG_INFO(0, flags)
999ZEND_END_ARG_INFO();
1000
1001ZEND_BEGIN_ARG_INFO_EX(arginfo_recursive_it_getSubIterator, 0, 0, 0)
1002	ZEND_ARG_INFO(0, level)
1003ZEND_END_ARG_INFO();
1004
1005ZEND_BEGIN_ARG_INFO_EX(arginfo_recursive_it_setMaxDepth, 0, 0, 0)
1006	ZEND_ARG_INFO(0, max_depth)
1007ZEND_END_ARG_INFO();
1008
1009static const zend_function_entry spl_funcs_RecursiveIteratorIterator[] = {
1010	SPL_ME(RecursiveIteratorIterator, __construct,       arginfo_recursive_it___construct,    ZEND_ACC_PUBLIC)
1011	SPL_ME(RecursiveIteratorIterator, rewind,            arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1012	SPL_ME(RecursiveIteratorIterator, valid,             arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1013	SPL_ME(RecursiveIteratorIterator, key,               arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1014	SPL_ME(RecursiveIteratorIterator, current,           arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1015	SPL_ME(RecursiveIteratorIterator, next,              arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1016	SPL_ME(RecursiveIteratorIterator, getDepth,          arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1017	SPL_ME(RecursiveIteratorIterator, getSubIterator,    arginfo_recursive_it_getSubIterator, ZEND_ACC_PUBLIC)
1018	SPL_ME(RecursiveIteratorIterator, getInnerIterator,  arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1019	SPL_ME(RecursiveIteratorIterator, beginIteration,    arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1020	SPL_ME(RecursiveIteratorIterator, endIteration,      arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1021	SPL_ME(RecursiveIteratorIterator, callHasChildren,   arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1022	SPL_ME(RecursiveIteratorIterator, callGetChildren,   arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1023	SPL_ME(RecursiveIteratorIterator, beginChildren,     arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1024	SPL_ME(RecursiveIteratorIterator, endChildren,       arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1025	SPL_ME(RecursiveIteratorIterator, nextElement,       arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1026	SPL_ME(RecursiveIteratorIterator, setMaxDepth,       arginfo_recursive_it_setMaxDepth,    ZEND_ACC_PUBLIC)
1027	SPL_ME(RecursiveIteratorIterator, getMaxDepth,       arginfo_recursive_it_void,           ZEND_ACC_PUBLIC)
1028	PHP_FE_END
1029};
1030
1031static void spl_recursive_tree_iterator_get_prefix(spl_recursive_it_object *object, zval *return_value)
1032{
1033	smart_str  str = {0};
1034	zval       has_next;
1035	int        level;
1036
1037	smart_str_appendl(&str, ZSTR_VAL(object->prefix[0].s), ZSTR_LEN(object->prefix[0].s));
1038
1039	for (level = 0; level < object->level; ++level) {
1040		zend_call_method_with_0_params(&object->iterators[level].zobject, object->iterators[level].ce, NULL, "hasnext", &has_next);
1041		if (Z_TYPE(has_next) != IS_UNDEF) {
1042			if (Z_TYPE(has_next) == IS_TRUE) {
1043				smart_str_appendl(&str, ZSTR_VAL(object->prefix[1].s), ZSTR_LEN(object->prefix[1].s));
1044			} else {
1045				smart_str_appendl(&str, ZSTR_VAL(object->prefix[2].s), ZSTR_LEN(object->prefix[2].s));
1046			}
1047			zval_ptr_dtor(&has_next);
1048		}
1049	}
1050	zend_call_method_with_0_params(&object->iterators[level].zobject, object->iterators[level].ce, NULL, "hasnext", &has_next);
1051	if (Z_TYPE(has_next) != IS_UNDEF) {
1052		if (Z_TYPE(has_next) == IS_TRUE) {
1053			smart_str_appendl(&str, ZSTR_VAL(object->prefix[3].s), ZSTR_LEN(object->prefix[3].s));
1054		} else {
1055			smart_str_appendl(&str, ZSTR_VAL(object->prefix[4].s), ZSTR_LEN(object->prefix[4].s));
1056		}
1057		zval_ptr_dtor(&has_next);
1058	}
1059
1060	smart_str_appendl(&str, ZSTR_VAL(object->prefix[5].s), ZSTR_LEN(object->prefix[5].s));
1061	smart_str_0(&str);
1062
1063	RETURN_NEW_STR(str.s);
1064}
1065
1066static void spl_recursive_tree_iterator_get_entry(spl_recursive_it_object *object, zval *return_value)
1067{
1068	zend_object_iterator      *iterator = object->iterators[object->level].iterator;
1069	zval                      *data;
1070	zend_error_handling        error_handling;
1071
1072	data = iterator->funcs->get_current_data(iterator);
1073
1074	/* Replace exception handling so the catchable fatal error that is thrown when a class
1075	 * without __toString is converted to string is converted into an exception. */
1076	zend_replace_error_handling(EH_THROW, spl_ce_UnexpectedValueException, &error_handling);
1077	if (data) {
1078		ZVAL_DEREF(data);
1079		if (Z_TYPE_P(data) == IS_ARRAY) {
1080			ZVAL_STRINGL(return_value, "Array", sizeof("Array")-1);
1081		} else {
1082			ZVAL_COPY(return_value, data);
1083			convert_to_string(return_value);
1084		}
1085	}
1086	zend_restore_error_handling(&error_handling);
1087}
1088
1089static void spl_recursive_tree_iterator_get_postfix(spl_recursive_it_object *object, zval *return_value)
1090{
1091	RETVAL_STR(object->postfix[0].s);
1092	Z_ADDREF_P(return_value);
1093}
1094
1095/* {{{ proto RecursiveTreeIterator::__construct(RecursiveIterator|IteratorAggregate it [, int flags = RTIT_BYPASS_KEY [, int cit_flags = CIT_CATCH_GET_CHILD [, mode = RIT_SELF_FIRST ]]]) throws InvalidArgumentException
1096   RecursiveIteratorIterator to generate ASCII graphic trees for the entries in a RecursiveIterator */
1097SPL_METHOD(RecursiveTreeIterator, __construct)
1098{
1099	spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveTreeIterator, zend_ce_iterator, RIT_RecursiveTreeIterator);
1100} /* }}} */
1101
1102/* {{{ proto void RecursiveTreeIterator::setPrefixPart(int part, string prefix) throws OutOfRangeException
1103   Sets prefix parts as used in getPrefix() */
1104SPL_METHOD(RecursiveTreeIterator, setPrefixPart)
1105{
1106	zend_long  part;
1107	char* prefix;
1108	size_t   prefix_len;
1109	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
1110
1111	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &part, &prefix, &prefix_len) == FAILURE) {
1112		return;
1113	}
1114
1115	if (0 > part || part > 5) {
1116		zend_throw_exception_ex(spl_ce_OutOfRangeException, 0, "Use RecursiveTreeIterator::PREFIX_* constant");
1117		return;
1118	}
1119
1120	smart_str_free(&object->prefix[part]);
1121	smart_str_appendl(&object->prefix[part], prefix, prefix_len);
1122} /* }}} */
1123
1124/* {{{ proto string RecursiveTreeIterator::getPrefix()
1125   Returns the string to place in front of current element */
1126SPL_METHOD(RecursiveTreeIterator, getPrefix)
1127{
1128	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
1129
1130	if (zend_parse_parameters_none() == FAILURE) {
1131		return;
1132	}
1133
1134	if(!object->iterators) {
1135		zend_throw_exception_ex(spl_ce_LogicException, 0,
1136			"The object is in an invalid state as the parent constructor was not called");
1137		return;
1138	}
1139
1140	spl_recursive_tree_iterator_get_prefix(object, return_value);
1141} /* }}} */
1142
1143/* {{{ proto void RecursiveTreeIterator::setPostfix(string prefix)
1144   Sets postfix as used in getPostfix() */
1145SPL_METHOD(RecursiveTreeIterator, setPostfix)
1146{
1147	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
1148	char* postfix;
1149	size_t   postfix_len;
1150
1151	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &postfix, &postfix_len) == FAILURE) {
1152		return;
1153	}
1154
1155	smart_str_free(&object->postfix[0]);
1156	smart_str_appendl(&object->postfix[0], postfix, postfix_len);
1157} /* }}} */
1158
1159/* {{{ proto string RecursiveTreeIterator::getEntry()
1160   Returns the string presentation built for current element */
1161SPL_METHOD(RecursiveTreeIterator, getEntry)
1162{
1163	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
1164
1165	if (zend_parse_parameters_none() == FAILURE) {
1166		return;
1167	}
1168
1169	if(!object->iterators) {
1170		zend_throw_exception_ex(spl_ce_LogicException, 0,
1171			"The object is in an invalid state as the parent constructor was not called");
1172		return;
1173	}
1174
1175	spl_recursive_tree_iterator_get_entry(object, return_value);
1176} /* }}} */
1177
1178/* {{{ proto string RecursiveTreeIterator::getPostfix()
1179   Returns the string to place after the current element */
1180SPL_METHOD(RecursiveTreeIterator, getPostfix)
1181{
1182	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
1183
1184	if (zend_parse_parameters_none() == FAILURE) {
1185		return;
1186	}
1187
1188	if(!object->iterators) {
1189		zend_throw_exception_ex(spl_ce_LogicException, 0,
1190			"The object is in an invalid state as the parent constructor was not called");
1191		return;
1192	}
1193
1194	spl_recursive_tree_iterator_get_postfix(object, return_value);
1195} /* }}} */
1196
1197/* {{{ proto mixed RecursiveTreeIterator::current()
1198   Returns the current element prefixed and postfixed */
1199SPL_METHOD(RecursiveTreeIterator, current)
1200{
1201	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
1202	zval                       prefix, entry, postfix;
1203	char                      *ptr;
1204	zend_string               *str;
1205
1206	if (zend_parse_parameters_none() == FAILURE) {
1207		return;
1208	}
1209
1210	if(!object->iterators) {
1211		zend_throw_exception_ex(spl_ce_LogicException, 0,
1212			"The object is in an invalid state as the parent constructor was not called");
1213		return;
1214	}
1215
1216	if (object->flags & RTIT_BYPASS_CURRENT) {
1217		zend_object_iterator      *iterator = object->iterators[object->level].iterator;
1218		zval                      *data;
1219
1220        SPL_FETCH_SUB_ITERATOR(iterator, object);
1221		data = iterator->funcs->get_current_data(iterator);
1222		if (data) {
1223			ZVAL_COPY_DEREF(return_value, data);
1224			return;
1225		} else {
1226			RETURN_NULL();
1227		}
1228	}
1229
1230	ZVAL_NULL(&prefix);
1231	ZVAL_NULL(&entry);
1232	spl_recursive_tree_iterator_get_prefix(object, &prefix);
1233	spl_recursive_tree_iterator_get_entry(object, &entry);
1234	if (Z_TYPE(entry) != IS_STRING) {
1235		zval_ptr_dtor(&prefix);
1236		zval_ptr_dtor(&entry);
1237		RETURN_NULL();
1238	}
1239	spl_recursive_tree_iterator_get_postfix(object, &postfix);
1240
1241	str = zend_string_alloc(Z_STRLEN(prefix) + Z_STRLEN(entry) + Z_STRLEN(postfix), 0);
1242	ptr = ZSTR_VAL(str);
1243
1244	memcpy(ptr, Z_STRVAL(prefix), Z_STRLEN(prefix));
1245	ptr += Z_STRLEN(prefix);
1246	memcpy(ptr, Z_STRVAL(entry), Z_STRLEN(entry));
1247	ptr += Z_STRLEN(entry);
1248	memcpy(ptr, Z_STRVAL(postfix), Z_STRLEN(postfix));
1249	ptr += Z_STRLEN(postfix);
1250	*ptr = 0;
1251
1252	zval_ptr_dtor(&prefix);
1253	zval_ptr_dtor(&entry);
1254	zval_ptr_dtor(&postfix);
1255
1256	RETURN_NEW_STR(str);
1257} /* }}} */
1258
1259/* {{{ proto mixed RecursiveTreeIterator::key()
1260   Returns the current key prefixed and postfixed */
1261SPL_METHOD(RecursiveTreeIterator, key)
1262{
1263	spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(getThis());
1264	zend_object_iterator      *iterator;
1265	zval                       prefix, key, postfix, key_copy;
1266	char                      *ptr;
1267	zend_string               *str;
1268
1269	if (zend_parse_parameters_none() == FAILURE) {
1270		return;
1271	}
1272
1273	SPL_FETCH_SUB_ITERATOR(iterator, object);
1274
1275	if (iterator->funcs->get_current_key) {
1276		iterator->funcs->get_current_key(iterator, &key);
1277	} else {
1278		ZVAL_NULL(&key);
1279	}
1280
1281	if (object->flags & RTIT_BYPASS_KEY) {
1282		RETVAL_ZVAL(&key, 1, 1);
1283		return;
1284	}
1285
1286	if (Z_TYPE(key) != IS_STRING) {
1287		if (zend_make_printable_zval(&key, &key_copy)) {
1288			key = key_copy;
1289		}
1290	}
1291
1292	spl_recursive_tree_iterator_get_prefix(object, &prefix);
1293	spl_recursive_tree_iterator_get_postfix(object, &postfix);
1294
1295	str = zend_string_alloc(Z_STRLEN(prefix) + Z_STRLEN(key) + Z_STRLEN(postfix), 0);
1296	ptr = ZSTR_VAL(str);
1297
1298	memcpy(ptr, Z_STRVAL(prefix), Z_STRLEN(prefix));
1299	ptr += Z_STRLEN(prefix);
1300	memcpy(ptr, Z_STRVAL(key), Z_STRLEN(key));
1301	ptr += Z_STRLEN(key);
1302	memcpy(ptr, Z_STRVAL(postfix), Z_STRLEN(postfix));
1303	ptr += Z_STRLEN(postfix);
1304	*ptr = 0;
1305
1306	zval_ptr_dtor(&prefix);
1307	zval_ptr_dtor(&key);
1308	zval_ptr_dtor(&postfix);
1309
1310	RETURN_NEW_STR(str);
1311} /* }}} */
1312
1313ZEND_BEGIN_ARG_INFO_EX(arginfo_recursive_tree_it___construct, 0, 0, 1)
1314	ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
1315	ZEND_ARG_INFO(0, flags)
1316	ZEND_ARG_INFO(0, caching_it_flags)
1317	ZEND_ARG_INFO(0, mode)
1318ZEND_END_ARG_INFO();
1319
1320ZEND_BEGIN_ARG_INFO_EX(arginfo_recursive_tree_it_setPrefixPart, 0, 0, 2)
1321	ZEND_ARG_INFO(0, part)
1322	ZEND_ARG_INFO(0, value)
1323ZEND_END_ARG_INFO();
1324
1325ZEND_BEGIN_ARG_INFO_EX(arginfo_recursive_tree_it_setPostfix, 0, 0, 1)
1326	ZEND_ARG_INFO(0, postfix)
1327ZEND_END_ARG_INFO();
1328
1329static const zend_function_entry spl_funcs_RecursiveTreeIterator[] = {
1330	SPL_ME(RecursiveTreeIterator,     __construct,       arginfo_recursive_tree_it___construct,   ZEND_ACC_PUBLIC)
1331	SPL_ME(RecursiveIteratorIterator, rewind,            arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1332	SPL_ME(RecursiveIteratorIterator, valid,             arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1333	SPL_ME(RecursiveTreeIterator,     key,               arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1334	SPL_ME(RecursiveTreeIterator,     current,           arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1335	SPL_ME(RecursiveIteratorIterator, next,              arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1336	SPL_ME(RecursiveIteratorIterator, beginIteration,    arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1337	SPL_ME(RecursiveIteratorIterator, endIteration,      arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1338	SPL_ME(RecursiveIteratorIterator, callHasChildren,   arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1339	SPL_ME(RecursiveIteratorIterator, callGetChildren,   arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1340	SPL_ME(RecursiveIteratorIterator, beginChildren,     arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1341	SPL_ME(RecursiveIteratorIterator, endChildren,       arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1342	SPL_ME(RecursiveIteratorIterator, nextElement,       arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1343	SPL_ME(RecursiveTreeIterator,     getPrefix,         arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1344	SPL_ME(RecursiveTreeIterator,     setPrefixPart,     arginfo_recursive_tree_it_setPrefixPart, ZEND_ACC_PUBLIC)
1345	SPL_ME(RecursiveTreeIterator,     getEntry,          arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1346	SPL_ME(RecursiveTreeIterator,     setPostfix,        arginfo_recursive_tree_it_setPostfix,               ZEND_ACC_PUBLIC)
1347	SPL_ME(RecursiveTreeIterator,     getPostfix,        arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1348	PHP_FE_END
1349};
1350
1351#if MBO_0
1352static int spl_dual_it_gets_implemented(zend_class_entry *interface, zend_class_entry *class_type)
1353{
1354	class_type->iterator_funcs_ptr->zf_valid = NULL;
1355	class_type->iterator_funcs_ptr->zf_current = NULL;
1356	class_type->iterator_funcs_ptr->zf_key = NULL;
1357	class_type->iterator_funcs_ptr->zf_next = NULL;
1358	class_type->iterator_funcs_ptr->zf_rewind = NULL;
1359
1360	return SUCCESS;
1361}
1362#endif
1363
1364static union _zend_function *spl_dual_it_get_method(zend_object **object, zend_string *method, const zval *key)
1365{
1366	union _zend_function *function_handler;
1367	spl_dual_it_object   *intern;
1368
1369	intern = spl_dual_it_from_obj(*object);
1370
1371	function_handler = zend_std_get_method(object, method, key);
1372	if (!function_handler && intern->inner.ce) {
1373		if ((function_handler = zend_hash_find_ptr(&intern->inner.ce->function_table, method)) == NULL) {
1374			if (Z_OBJ_HT(intern->inner.zobject)->get_method) {
1375				*object = Z_OBJ(intern->inner.zobject);
1376				function_handler = (*object)->handlers->get_method(object, method, key);
1377			}
1378		} else {
1379			*object = Z_OBJ(intern->inner.zobject);
1380		}
1381	}
1382	return function_handler;
1383}
1384
1385#if MBO_0
1386int spl_dual_it_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
1387{
1388	zval ***func_params, func;
1389	zval retval;
1390	int arg_count;
1391	int current = 0;
1392	int success;
1393	void **p;
1394	spl_dual_it_object   *intern;
1395
1396	intern = Z_SPLDUAL_IT_P(getThis());
1397
1398	ZVAL_STRING(&func, method, 0);
1399
1400	p = EG(argument_stack).top_element-2;
1401	arg_count = (zend_ulong) *p;
1402
1403	func_params = safe_emalloc(sizeof(zval **), arg_count, 0);
1404
1405	current = 0;
1406	while (arg_count-- > 0) {
1407		func_params[current] = (zval **) p - (arg_count-current);
1408		current++;
1409	}
1410	arg_count = current; /* restore */
1411
1412	if (call_user_function_ex(EG(function_table), NULL, &func, &retval, arg_count, func_params, 0, NULL) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
1413		RETURN_ZVAL(&retval, 0, 0);
1414
1415		success = SUCCESS;
1416	} else {
1417		zend_throw_error(NULL, "Unable to call %s::%s()", intern->inner.ce->name, method);
1418		success = FAILURE;
1419	}
1420
1421	efree(func_params);
1422	return success;
1423}
1424#endif
1425
1426#define SPL_CHECK_CTOR(intern, classname) \
1427	if (intern->dit_type == DIT_Unknown) { \
1428		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Classes derived from %s must call %s::__construct()", \
1429				ZSTR_VAL((spl_ce_##classname)->name), ZSTR_VAL((spl_ce_##classname)->name)); \
1430		return; \
1431	}
1432
1433#define APPENDIT_CHECK_CTOR(intern) SPL_CHECK_CTOR(intern, AppendIterator)
1434
1435static inline int spl_dual_it_fetch(spl_dual_it_object *intern, int check_more);
1436
1437static inline int spl_cit_check_flags(zend_long flags)
1438{
1439	zend_long cnt = 0;
1440
1441	cnt += (flags & CIT_CALL_TOSTRING) ? 1 : 0;
1442	cnt += (flags & CIT_TOSTRING_USE_KEY) ? 1 : 0;
1443	cnt += (flags & CIT_TOSTRING_USE_CURRENT) ? 1 : 0;
1444	cnt += (flags & CIT_TOSTRING_USE_INNER) ? 1 : 0;
1445
1446	return cnt <= 1 ? SUCCESS : FAILURE;
1447}
1448
1449static spl_dual_it_object* spl_dual_it_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_base, zend_class_entry *ce_inner, dual_it_type dit_type)
1450{
1451	zval                 *zobject, retval;
1452	spl_dual_it_object   *intern;
1453	zend_class_entry     *ce = NULL;
1454	int                   inc_refcount = 1;
1455	zend_error_handling   error_handling;
1456
1457	intern = Z_SPLDUAL_IT_P(getThis());
1458
1459	if (intern->dit_type != DIT_Unknown) {
1460		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s::getIterator() must be called exactly once per instance", ZSTR_VAL(ce_base->name));
1461		return NULL;
1462	}
1463
1464	intern->dit_type = dit_type;
1465	switch (dit_type) {
1466		case DIT_LimitIterator: {
1467			intern->u.limit.offset = 0; /* start at beginning */
1468			intern->u.limit.count = -1; /* get all */
1469			if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "O|ll", &zobject, ce_inner, &intern->u.limit.offset, &intern->u.limit.count) == FAILURE) {
1470				return NULL;
1471			}
1472			if (intern->u.limit.offset < 0) {
1473				zend_throw_exception(spl_ce_OutOfRangeException, "Parameter offset must be >= 0", 0);
1474				return NULL;
1475			}
1476			if (intern->u.limit.count < 0 && intern->u.limit.count != -1) {
1477				zend_throw_exception(spl_ce_OutOfRangeException, "Parameter count must either be -1 or a value greater than or equal 0", 0);
1478				return NULL;
1479			}
1480			break;
1481		}
1482		case DIT_CachingIterator:
1483		case DIT_RecursiveCachingIterator: {
1484			zend_long flags = CIT_CALL_TOSTRING;
1485			if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "O|l", &zobject, ce_inner, &flags) == FAILURE) {
1486				return NULL;
1487			}
1488			if (spl_cit_check_flags(flags) != SUCCESS) {
1489				zend_throw_exception(spl_ce_InvalidArgumentException, "Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER", 0);
1490				return NULL;
1491			}
1492			intern->u.caching.flags |= flags & CIT_PUBLIC;
1493			array_init(&intern->u.caching.zcache);
1494			break;
1495		}
1496		case DIT_IteratorIterator: {
1497			zend_class_entry *ce_cast;
1498			zend_string *class_name;
1499
1500			if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "O|S", &zobject, ce_inner, &class_name) == FAILURE) {
1501				return NULL;
1502			}
1503			ce = Z_OBJCE_P(zobject);
1504			if (!instanceof_function(ce, zend_ce_iterator)) {
1505				if (ZEND_NUM_ARGS() > 1) {
1506					if (!(ce_cast = zend_lookup_class(class_name))
1507					|| !instanceof_function(ce, ce_cast)
1508					|| !ce_cast->get_iterator
1509					) {
1510						zend_throw_exception(spl_ce_LogicException, "Class to downcast to not found or not base class or does not implement Traversable", 0);
1511						return NULL;
1512					}
1513					ce = ce_cast;
1514				}
1515				if (instanceof_function(ce, zend_ce_aggregate)) {
1516					zend_call_method_with_0_params(zobject, ce, &ce->iterator_funcs_ptr->zf_new_iterator, "getiterator", &retval);
1517					if (EG(exception)) {
1518						zval_ptr_dtor(&retval);
1519						return NULL;
1520					}
1521					if (Z_TYPE(retval) != IS_OBJECT || !instanceof_function(Z_OBJCE(retval), zend_ce_traversable)) {
1522						zend_throw_exception_ex(spl_ce_LogicException, 0, "%s::getIterator() must return an object that implements Traversable", ZSTR_VAL(ce->name));
1523						return NULL;
1524					}
1525					zobject = &retval;
1526					ce = Z_OBJCE_P(zobject);
1527					inc_refcount = 0;
1528				}
1529			}
1530			break;
1531		}
1532		case DIT_AppendIterator:
1533			zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling);
1534			spl_instantiate(spl_ce_ArrayIterator, &intern->u.append.zarrayit);
1535			zend_call_method_with_0_params(&intern->u.append.zarrayit, spl_ce_ArrayIterator, &spl_ce_ArrayIterator->constructor, "__construct", NULL);
1536			intern->u.append.iterator = spl_ce_ArrayIterator->get_iterator(spl_ce_ArrayIterator, &intern->u.append.zarrayit, 0);
1537			zend_restore_error_handling(&error_handling);
1538			return intern;
1539#if HAVE_PCRE || HAVE_BUNDLED_PCRE
1540		case DIT_RegexIterator:
1541		case DIT_RecursiveRegexIterator: {
1542			zend_string *regex;
1543			zend_long mode = REGIT_MODE_MATCH;
1544
1545			intern->u.regex.use_flags = ZEND_NUM_ARGS() >= 5;
1546			intern->u.regex.flags = 0;
1547			intern->u.regex.preg_flags = 0;
1548			if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "OS|lll", &zobject, ce_inner, &regex, &mode, &intern->u.regex.flags, &intern->u.regex.preg_flags) == FAILURE) {
1549				return NULL;
1550			}
1551			if (mode < 0 || mode >= REGIT_MODE_MAX) {
1552				zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "Illegal mode " ZEND_LONG_FMT, mode);
1553				return NULL;
1554			}
1555			intern->u.regex.mode = mode;
1556			intern->u.regex.regex = zend_string_copy(regex);
1557
1558			zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling);
1559			intern->u.regex.pce = pcre_get_compiled_regex_cache(regex);
1560			zend_restore_error_handling(&error_handling);
1561
1562			if (intern->u.regex.pce == NULL) {
1563				/* pcre_get_compiled_regex_cache has already sent error */
1564				return NULL;
1565			}
1566			php_pcre_pce_incref(intern->u.regex.pce);
1567			break;
1568		}
1569#endif
1570		case DIT_CallbackFilterIterator:
1571		case DIT_RecursiveCallbackFilterIterator: {
1572			_spl_cbfilter_it_intern *cfi = emalloc(sizeof(*cfi));
1573			cfi->fci.object = NULL;
1574			if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "Of", &zobject, ce_inner, &cfi->fci, &cfi->fcc) == FAILURE) {
1575				efree(cfi);
1576				return NULL;
1577			}
1578			Z_TRY_ADDREF(cfi->fci.function_name);
1579			cfi->object = cfi->fcc.object;
1580			if (cfi->object) GC_ADDREF(cfi->object);
1581			intern->u.cbfilter = cfi;
1582			break;
1583		}
1584		default:
1585			if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "O", &zobject, ce_inner) == FAILURE) {
1586				return NULL;
1587			}
1588			break;
1589	}
1590
1591	if (inc_refcount) {
1592		Z_TRY_ADDREF_P(zobject);
1593	}
1594	ZVAL_COPY_VALUE(&intern->inner.zobject, zobject);
1595
1596	intern->inner.ce = dit_type == DIT_IteratorIterator ? ce : Z_OBJCE_P(zobject);
1597	intern->inner.object = Z_OBJ_P(zobject);
1598	intern->inner.iterator = intern->inner.ce->get_iterator(intern->inner.ce, zobject, 0);
1599
1600	return intern;
1601}
1602
1603/* {{{ proto FilterIterator::__construct(Iterator it)
1604   Create an Iterator from another iterator */
1605SPL_METHOD(FilterIterator, __construct)
1606{
1607	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_FilterIterator, zend_ce_iterator, DIT_FilterIterator);
1608} /* }}} */
1609
1610/* {{{ proto CallbackFilterIterator::__construct(Iterator it, callback func)
1611   Create an Iterator from another iterator */
1612SPL_METHOD(CallbackFilterIterator, __construct)
1613{
1614	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_CallbackFilterIterator, zend_ce_iterator, DIT_CallbackFilterIterator);
1615} /* }}} */
1616
1617/* {{{ proto Iterator FilterIterator::getInnerIterator()
1618       proto Iterator CachingIterator::getInnerIterator()
1619       proto Iterator LimitIterator::getInnerIterator()
1620       proto Iterator ParentIterator::getInnerIterator()
1621   Get the inner iterator */
1622SPL_METHOD(dual_it, getInnerIterator)
1623{
1624	spl_dual_it_object   *intern;
1625
1626	if (zend_parse_parameters_none() == FAILURE) {
1627		return;
1628	}
1629
1630	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1631
1632	if (!Z_ISUNDEF(intern->inner.zobject)) {
1633		zval *value = &intern->inner.zobject;
1634
1635		ZVAL_COPY_DEREF(return_value, value);
1636	} else {
1637		RETURN_NULL();
1638	}
1639} /* }}} */
1640
1641static inline void spl_dual_it_free(spl_dual_it_object *intern)
1642{
1643	if (intern->inner.iterator && intern->inner.iterator->funcs->invalidate_current) {
1644		intern->inner.iterator->funcs->invalidate_current(intern->inner.iterator);
1645	}
1646	if (Z_TYPE(intern->current.data) != IS_UNDEF) {
1647		zval_ptr_dtor(&intern->current.data);
1648		ZVAL_UNDEF(&intern->current.data);
1649	}
1650	if (Z_TYPE(intern->current.key) != IS_UNDEF) {
1651		zval_ptr_dtor(&intern->current.key);
1652		ZVAL_UNDEF(&intern->current.key);
1653	}
1654	if (intern->dit_type == DIT_CachingIterator || intern->dit_type == DIT_RecursiveCachingIterator) {
1655		if (Z_TYPE(intern->u.caching.zstr) != IS_UNDEF) {
1656			zval_ptr_dtor(&intern->u.caching.zstr);
1657			ZVAL_UNDEF(&intern->u.caching.zstr);
1658		}
1659		if (Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF) {
1660			zval_ptr_dtor(&intern->u.caching.zchildren);
1661			ZVAL_UNDEF(&intern->u.caching.zchildren);
1662		}
1663	}
1664}
1665
1666static inline void spl_dual_it_rewind(spl_dual_it_object *intern)
1667{
1668	spl_dual_it_free(intern);
1669	intern->current.pos = 0;
1670	if (intern->inner.iterator && intern->inner.iterator->funcs->rewind) {
1671		intern->inner.iterator->funcs->rewind(intern->inner.iterator);
1672	}
1673}
1674
1675static inline int spl_dual_it_valid(spl_dual_it_object *intern)
1676{
1677	if (!intern->inner.iterator) {
1678		return FAILURE;
1679	}
1680	/* FAILURE / SUCCESS */
1681	return intern->inner.iterator->funcs->valid(intern->inner.iterator);
1682}
1683
1684static inline int spl_dual_it_fetch(spl_dual_it_object *intern, int check_more)
1685{
1686	zval *data;
1687
1688	spl_dual_it_free(intern);
1689	if (!check_more || spl_dual_it_valid(intern) == SUCCESS) {
1690		data = intern->inner.iterator->funcs->get_current_data(intern->inner.iterator);
1691		if (data) {
1692			ZVAL_COPY(&intern->current.data, data);
1693		}
1694
1695		if (intern->inner.iterator->funcs->get_current_key) {
1696			intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, &intern->current.key);
1697			if (EG(exception)) {
1698				zval_ptr_dtor(&intern->current.key);
1699				ZVAL_UNDEF(&intern->current.key);
1700			}
1701		} else {
1702			ZVAL_LONG(&intern->current.key, intern->current.pos);
1703		}
1704		return EG(exception) ? FAILURE : SUCCESS;
1705	}
1706	return FAILURE;
1707}
1708
1709static inline void spl_dual_it_next(spl_dual_it_object *intern, int do_free)
1710{
1711	if (do_free) {
1712		spl_dual_it_free(intern);
1713	} else if (!intern->inner.iterator) {
1714		zend_throw_error(NULL, "The inner constructor wasn't initialized with an iterator instance");
1715		return;
1716	}
1717	intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
1718	intern->current.pos++;
1719}
1720
1721/* {{{ proto void ParentIterator::rewind()
1722       proto void IteratorIterator::rewind()
1723   Rewind the iterator
1724   */
1725SPL_METHOD(dual_it, rewind)
1726{
1727	spl_dual_it_object   *intern;
1728
1729	if (zend_parse_parameters_none() == FAILURE) {
1730		return;
1731	}
1732
1733	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1734
1735	spl_dual_it_rewind(intern);
1736	spl_dual_it_fetch(intern, 1);
1737} /* }}} */
1738
1739/* {{{ proto bool FilterIterator::valid()
1740       proto bool ParentIterator::valid()
1741       proto bool IteratorIterator::valid()
1742       proto bool NoRewindIterator::valid()
1743   Check whether the current element is valid */
1744SPL_METHOD(dual_it, valid)
1745{
1746	spl_dual_it_object   *intern;
1747
1748	if (zend_parse_parameters_none() == FAILURE) {
1749		return;
1750	}
1751
1752	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1753
1754	RETURN_BOOL(Z_TYPE(intern->current.data) != IS_UNDEF);
1755} /* }}} */
1756
1757/* {{{ proto mixed FilterIterator::key()
1758       proto mixed CachingIterator::key()
1759       proto mixed LimitIterator::key()
1760       proto mixed ParentIterator::key()
1761       proto mixed IteratorIterator::key()
1762       proto mixed NoRewindIterator::key()
1763       proto mixed AppendIterator::key()
1764   Get the current key */
1765SPL_METHOD(dual_it, key)
1766{
1767	spl_dual_it_object   *intern;
1768
1769	if (zend_parse_parameters_none() == FAILURE) {
1770		return;
1771	}
1772
1773	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1774
1775	if (Z_TYPE(intern->current.key) != IS_UNDEF) {
1776		zval *value = &intern->current.key;
1777
1778		ZVAL_COPY_DEREF(return_value, value);
1779	} else {
1780		RETURN_NULL();
1781	}
1782} /* }}} */
1783
1784/* {{{ proto mixed FilterIterator::current()
1785       proto mixed CachingIterator::current()
1786       proto mixed LimitIterator::current()
1787       proto mixed ParentIterator::current()
1788       proto mixed IteratorIterator::current()
1789       proto mixed NoRewindIterator::current()
1790   Get the current element value */
1791SPL_METHOD(dual_it, current)
1792{
1793	spl_dual_it_object   *intern;
1794
1795	if (zend_parse_parameters_none() == FAILURE) {
1796		return;
1797	}
1798
1799	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1800
1801	if (Z_TYPE(intern->current.data) != IS_UNDEF) {
1802		zval *value = &intern->current.data;
1803
1804		ZVAL_COPY_DEREF(return_value, value);
1805	} else {
1806		RETURN_NULL();
1807	}
1808} /* }}} */
1809
1810/* {{{ proto void ParentIterator::next()
1811       proto void IteratorIterator::next()
1812       proto void NoRewindIterator::next()
1813   Move the iterator forward */
1814SPL_METHOD(dual_it, next)
1815{
1816	spl_dual_it_object   *intern;
1817
1818	if (zend_parse_parameters_none() == FAILURE) {
1819		return;
1820	}
1821
1822	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1823
1824	spl_dual_it_next(intern, 1);
1825	spl_dual_it_fetch(intern, 1);
1826} /* }}} */
1827
1828static inline void spl_filter_it_fetch(zval *zthis, spl_dual_it_object *intern)
1829{
1830	zval retval;
1831
1832	while (spl_dual_it_fetch(intern, 1) == SUCCESS) {
1833		zend_call_method_with_0_params(zthis, intern->std.ce, NULL, "accept", &retval);
1834		if (Z_TYPE(retval) != IS_UNDEF) {
1835			if (zend_is_true(&retval)) {
1836				zval_ptr_dtor(&retval);
1837				return;
1838			}
1839			zval_ptr_dtor(&retval);
1840		}
1841		if (EG(exception)) {
1842			return;
1843		}
1844		intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
1845	}
1846	spl_dual_it_free(intern);
1847}
1848
1849static inline void spl_filter_it_rewind(zval *zthis, spl_dual_it_object *intern)
1850{
1851	spl_dual_it_rewind(intern);
1852	spl_filter_it_fetch(zthis, intern);
1853}
1854
1855static inline void spl_filter_it_next(zval *zthis, spl_dual_it_object *intern)
1856{
1857	spl_dual_it_next(intern, 1);
1858	spl_filter_it_fetch(zthis, intern);
1859}
1860
1861/* {{{ proto void FilterIterator::rewind()
1862   Rewind the iterator */
1863SPL_METHOD(FilterIterator, rewind)
1864{
1865	spl_dual_it_object   *intern;
1866
1867	if (zend_parse_parameters_none() == FAILURE) {
1868		return;
1869	}
1870
1871	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1872	spl_filter_it_rewind(getThis(), intern);
1873} /* }}} */
1874
1875/* {{{ proto void FilterIterator::next()
1876   Move the iterator forward */
1877SPL_METHOD(FilterIterator, next)
1878{
1879	spl_dual_it_object   *intern;
1880
1881	if (zend_parse_parameters_none() == FAILURE) {
1882		return;
1883	}
1884
1885	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1886	spl_filter_it_next(getThis(), intern);
1887} /* }}} */
1888
1889/* {{{ proto RecursiveCallbackFilterIterator::__construct(RecursiveIterator it, callback func)
1890   Create a RecursiveCallbackFilterIterator from a RecursiveIterator */
1891SPL_METHOD(RecursiveCallbackFilterIterator, __construct)
1892{
1893	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveCallbackFilterIterator, spl_ce_RecursiveIterator, DIT_RecursiveCallbackFilterIterator);
1894} /* }}} */
1895
1896
1897/* {{{ proto RecursiveFilterIterator::__construct(RecursiveIterator it)
1898   Create a RecursiveFilterIterator from a RecursiveIterator */
1899SPL_METHOD(RecursiveFilterIterator, __construct)
1900{
1901	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveFilterIterator, spl_ce_RecursiveIterator, DIT_RecursiveFilterIterator);
1902} /* }}} */
1903
1904/* {{{ proto bool RecursiveFilterIterator::hasChildren()
1905   Check whether the inner iterator's current element has children */
1906SPL_METHOD(RecursiveFilterIterator, hasChildren)
1907{
1908	spl_dual_it_object   *intern;
1909	zval                  retval;
1910
1911	if (zend_parse_parameters_none() == FAILURE) {
1912		return;
1913	}
1914
1915	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1916
1917	zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "haschildren", &retval);
1918	if (Z_TYPE(retval) != IS_UNDEF) {
1919		RETURN_ZVAL(&retval, 0, 1);
1920	} else {
1921		RETURN_FALSE;
1922	}
1923} /* }}} */
1924
1925/* {{{ proto RecursiveFilterIterator RecursiveFilterIterator::getChildren()
1926   Return the inner iterator's children contained in a RecursiveFilterIterator */
1927SPL_METHOD(RecursiveFilterIterator, getChildren)
1928{
1929	spl_dual_it_object   *intern;
1930	zval                  retval;
1931
1932	if (zend_parse_parameters_none() == FAILURE) {
1933		return;
1934	}
1935
1936	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1937
1938	zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval);
1939	if (!EG(exception) && Z_TYPE(retval) != IS_UNDEF) {
1940		spl_instantiate_arg_ex1(Z_OBJCE_P(getThis()), return_value, &retval);
1941	}
1942	zval_ptr_dtor(&retval);
1943} /* }}} */
1944
1945/* {{{ proto RecursiveCallbackFilterIterator RecursiveCallbackFilterIterator::getChildren()
1946   Return the inner iterator's children contained in a RecursiveCallbackFilterIterator */
1947SPL_METHOD(RecursiveCallbackFilterIterator, getChildren)
1948{
1949	spl_dual_it_object   *intern;
1950	zval                  retval;
1951
1952	if (zend_parse_parameters_none() == FAILURE) {
1953		return;
1954	}
1955
1956	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1957
1958	zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval);
1959	if (!EG(exception) && Z_TYPE(retval) != IS_UNDEF) {
1960		spl_instantiate_arg_ex2(Z_OBJCE_P(getThis()), return_value, &retval, &intern->u.cbfilter->fci.function_name);
1961	}
1962	zval_ptr_dtor(&retval);
1963} /* }}} */
1964/* {{{ proto ParentIterator::__construct(RecursiveIterator it)
1965   Create a ParentIterator from a RecursiveIterator */
1966SPL_METHOD(ParentIterator, __construct)
1967{
1968	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_ParentIterator, spl_ce_RecursiveIterator, DIT_ParentIterator);
1969} /* }}} */
1970
1971#if HAVE_PCRE || HAVE_BUNDLED_PCRE
1972/* {{{ proto RegexIterator::__construct(Iterator it, string regex [, int mode [, int flags [, int preg_flags]]])
1973   Create an RegexIterator from another iterator and a regular expression */
1974SPL_METHOD(RegexIterator, __construct)
1975{
1976	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RegexIterator, zend_ce_iterator, DIT_RegexIterator);
1977} /* }}} */
1978
1979/* {{{ proto bool CallbackFilterIterator::accept()
1980   Calls the callback with the current value, the current key and the inner iterator as arguments */
1981SPL_METHOD(CallbackFilterIterator, accept)
1982{
1983	spl_dual_it_object     *intern = Z_SPLDUAL_IT_P(getThis());
1984	zend_fcall_info        *fci = &intern->u.cbfilter->fci;
1985	zend_fcall_info_cache  *fcc = &intern->u.cbfilter->fcc;
1986	zval                    params[3];
1987
1988	if (zend_parse_parameters_none() == FAILURE) {
1989		return;
1990	}
1991
1992	if (Z_TYPE(intern->current.data) == IS_UNDEF || Z_TYPE(intern->current.key) == IS_UNDEF) {
1993		RETURN_FALSE;
1994	}
1995
1996	ZVAL_COPY_VALUE(&params[0], &intern->current.data);
1997	ZVAL_COPY_VALUE(&params[1], &intern->current.key);
1998	ZVAL_COPY_VALUE(&params[2], &intern->inner.zobject);
1999
2000	fci->retval = return_value;
2001	fci->param_count = 3;
2002	fci->params = params;
2003	fci->no_separation = 0;
2004
2005	if (zend_call_function(fci, fcc) != SUCCESS || Z_ISUNDEF_P(return_value)) {
2006		RETURN_FALSE;
2007	}
2008
2009	if (EG(exception)) {
2010		RETURN_NULL();
2011	}
2012
2013	/* zend_call_function may change args to IS_REF */
2014	ZVAL_COPY_VALUE(&intern->current.data, &params[0]);
2015	ZVAL_COPY_VALUE(&intern->current.key, &params[1]);
2016}
2017/* }}} */
2018
2019/* {{{ proto bool RegexIterator::accept()
2020   Match (string)current() against regular expression */
2021SPL_METHOD(RegexIterator, accept)
2022{
2023	spl_dual_it_object *intern;
2024	zend_string *result, *subject;
2025	size_t count = 0;
2026	zval zcount, *replacement, tmp_replacement, rv;
2027	pcre2_match_data *match_data;
2028	pcre2_code *re;
2029	int rc;
2030
2031	if (zend_parse_parameters_none() == FAILURE) {
2032		return;
2033	}
2034
2035	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2036
2037	if (Z_TYPE(intern->current.data) == IS_UNDEF) {
2038		RETURN_FALSE;
2039	}
2040
2041	if (intern->u.regex.flags & REGIT_USE_KEY) {
2042		subject = zval_get_string(&intern->current.key);
2043	} else {
2044		if (Z_TYPE(intern->current.data) == IS_ARRAY) {
2045			RETURN_FALSE;
2046		}
2047		subject = zval_get_string(&intern->current.data);
2048	}
2049
2050	switch (intern->u.regex.mode)
2051	{
2052		case REGIT_MODE_MAX: /* won't happen but makes compiler happy */
2053		case REGIT_MODE_MATCH:
2054			re = php_pcre_pce_re(intern->u.regex.pce);
2055			match_data = php_pcre_create_match_data(0, re);
2056			if (!match_data) {
2057				RETURN_FALSE;
2058			}
2059			rc = pcre2_match(re, (PCRE2_SPTR)ZSTR_VAL(subject), ZSTR_LEN(subject), 0, 0, match_data, php_pcre_mctx());
2060			RETVAL_BOOL(rc >= 0);
2061			php_pcre_free_match_data(match_data);
2062			break;
2063
2064		case REGIT_MODE_ALL_MATCHES:
2065		case REGIT_MODE_GET_MATCH:
2066			zval_ptr_dtor(&intern->current.data);
2067			ZVAL_UNDEF(&intern->current.data);
2068			php_pcre_match_impl(intern->u.regex.pce, ZSTR_VAL(subject), ZSTR_LEN(subject), &zcount,
2069				&intern->current.data, intern->u.regex.mode == REGIT_MODE_ALL_MATCHES, intern->u.regex.use_flags, intern->u.regex.preg_flags, 0);
2070			RETVAL_BOOL(Z_LVAL(zcount) > 0);
2071			break;
2072
2073		case REGIT_MODE_SPLIT:
2074			zval_ptr_dtor(&intern->current.data);
2075			ZVAL_UNDEF(&intern->current.data);
2076			php_pcre_split_impl(intern->u.regex.pce, subject, &intern->current.data, -1, intern->u.regex.preg_flags);
2077			count = zend_hash_num_elements(Z_ARRVAL(intern->current.data));
2078			RETVAL_BOOL(count > 1);
2079			break;
2080
2081		case REGIT_MODE_REPLACE:
2082			replacement = zend_read_property(intern->std.ce, getThis(), "replacement", sizeof("replacement")-1, 1, &rv);
2083			if (Z_TYPE_P(replacement) != IS_STRING) {
2084				ZVAL_COPY(&tmp_replacement, replacement);
2085				convert_to_string(&tmp_replacement);
2086				replacement = &tmp_replacement;
2087			}
2088			result = php_pcre_replace_impl(intern->u.regex.pce, subject, ZSTR_VAL(subject), ZSTR_LEN(subject), Z_STR_P(replacement), -1, &count);
2089
2090			if (intern->u.regex.flags & REGIT_USE_KEY) {
2091				zval_ptr_dtor(&intern->current.key);
2092				ZVAL_STR(&intern->current.key, result);
2093			} else {
2094				zval_ptr_dtor(&intern->current.data);
2095				ZVAL_STR(&intern->current.data, result);
2096			}
2097
2098			if (replacement == &tmp_replacement) {
2099				zval_ptr_dtor(replacement);
2100			}
2101			RETVAL_BOOL(count > 0);
2102	}
2103
2104	if (intern->u.regex.flags & REGIT_INVERTED) {
2105		RETVAL_BOOL(Z_TYPE_P(return_value) != IS_TRUE);
2106	}
2107	zend_string_release_ex(subject, 0);
2108} /* }}} */
2109
2110/* {{{ proto string RegexIterator::getRegex()
2111   Returns current regular expression */
2112SPL_METHOD(RegexIterator, getRegex)
2113{
2114	spl_dual_it_object *intern = Z_SPLDUAL_IT_P(getThis());
2115
2116	if (zend_parse_parameters_none() == FAILURE) {
2117		return;
2118	}
2119
2120	RETURN_STR_COPY(intern->u.regex.regex);
2121} /* }}} */
2122
2123/* {{{ proto bool RegexIterator::getMode()
2124   Returns current operation mode */
2125SPL_METHOD(RegexIterator, getMode)
2126{
2127	spl_dual_it_object *intern;
2128
2129	if (zend_parse_parameters_none() == FAILURE) {
2130		return;
2131	}
2132
2133	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2134
2135	RETURN_LONG(intern->u.regex.mode);
2136} /* }}} */
2137
2138/* {{{ proto bool RegexIterator::setMode(int new_mode)
2139   Set new operation mode */
2140SPL_METHOD(RegexIterator, setMode)
2141{
2142	spl_dual_it_object *intern;
2143	zend_long mode;
2144
2145	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &mode) == FAILURE) {
2146		return;
2147	}
2148
2149	if (mode < 0 || mode >= REGIT_MODE_MAX) {
2150		zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "Illegal mode " ZEND_LONG_FMT, mode);
2151		return;/* NULL */
2152	}
2153
2154	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2155
2156	intern->u.regex.mode = mode;
2157} /* }}} */
2158
2159/* {{{ proto bool RegexIterator::getFlags()
2160   Returns current operation flags */
2161SPL_METHOD(RegexIterator, getFlags)
2162{
2163	spl_dual_it_object *intern;
2164
2165	if (zend_parse_parameters_none() == FAILURE) {
2166		return;
2167	}
2168
2169	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2170
2171	RETURN_LONG(intern->u.regex.flags);
2172} /* }}} */
2173
2174/* {{{ proto bool RegexIterator::setFlags(int new_flags)
2175   Set operation flags */
2176SPL_METHOD(RegexIterator, setFlags)
2177{
2178	spl_dual_it_object *intern;
2179	zend_long flags;
2180
2181	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &flags) == FAILURE) {
2182		return;
2183	}
2184
2185	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2186
2187	intern->u.regex.flags = flags;
2188} /* }}} */
2189
2190/* {{{ proto bool RegexIterator::getFlags()
2191   Returns current PREG flags (if in use or NULL) */
2192SPL_METHOD(RegexIterator, getPregFlags)
2193{
2194	spl_dual_it_object *intern;
2195
2196	if (zend_parse_parameters_none() == FAILURE) {
2197		return;
2198	}
2199
2200	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2201
2202	if (intern->u.regex.use_flags) {
2203		RETURN_LONG(intern->u.regex.preg_flags);
2204	} else {
2205		RETURN_LONG(0);
2206	}
2207} /* }}} */
2208
2209/* {{{ proto bool RegexIterator::setPregFlags(int new_flags)
2210   Set PREG flags */
2211SPL_METHOD(RegexIterator, setPregFlags)
2212{
2213	spl_dual_it_object *intern;
2214	zend_long preg_flags;
2215
2216	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &preg_flags) == FAILURE) {
2217		return;
2218	}
2219
2220	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2221
2222	intern->u.regex.preg_flags = preg_flags;
2223	intern->u.regex.use_flags = 1;
2224} /* }}} */
2225
2226/* {{{ proto RecursiveRegexIterator::__construct(RecursiveIterator it, string regex [, int mode [, int flags [, int preg_flags]]])
2227   Create an RecursiveRegexIterator from another recursive iterator and a regular expression */
2228SPL_METHOD(RecursiveRegexIterator, __construct)
2229{
2230	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveRegexIterator, spl_ce_RecursiveIterator, DIT_RecursiveRegexIterator);
2231} /* }}} */
2232
2233/* {{{ proto RecursiveRegexIterator RecursiveRegexIterator::getChildren()
2234   Return the inner iterator's children contained in a RecursiveRegexIterator */
2235SPL_METHOD(RecursiveRegexIterator, getChildren)
2236{
2237	spl_dual_it_object   *intern;
2238	zval                 retval;
2239
2240	if (zend_parse_parameters_none() == FAILURE) {
2241		return;
2242	}
2243
2244	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2245
2246	zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval);
2247	if (!EG(exception)) {
2248		zval args[5];
2249
2250		ZVAL_COPY(&args[0], &retval);
2251		ZVAL_STR_COPY(&args[1], intern->u.regex.regex);
2252		ZVAL_LONG(&args[2], intern->u.regex.mode);
2253		ZVAL_LONG(&args[3], intern->u.regex.flags);
2254		ZVAL_LONG(&args[4], intern->u.regex.preg_flags);
2255
2256		spl_instantiate_arg_n(Z_OBJCE_P(getThis()), return_value, 5, args);
2257
2258		zval_ptr_dtor(&args[0]);
2259		zval_ptr_dtor(&args[1]);
2260	}
2261	zval_ptr_dtor(&retval);
2262} /* }}} */
2263
2264SPL_METHOD(RecursiveRegexIterator, accept)
2265{
2266	spl_dual_it_object *intern;
2267
2268	if (zend_parse_parameters_none() == FAILURE) {
2269		return;
2270	}
2271
2272	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2273
2274	if (Z_TYPE(intern->current.data) == IS_UNDEF) {
2275		RETURN_FALSE;
2276	} else if (Z_TYPE(intern->current.data) == IS_ARRAY) {
2277		RETURN_BOOL(zend_hash_num_elements(Z_ARRVAL(intern->current.data)) > 0);
2278	}
2279
2280	zend_call_method_with_0_params(getThis(), spl_ce_RegexIterator, NULL, "accept", return_value);
2281}
2282
2283#endif
2284
2285/* {{{ spl_dual_it_dtor */
2286static void spl_dual_it_dtor(zend_object *_object)
2287{
2288	spl_dual_it_object *object = spl_dual_it_from_obj(_object);
2289
2290	/* call standard dtor */
2291	zend_objects_destroy_object(_object);
2292
2293	spl_dual_it_free(object);
2294
2295	if (object->inner.iterator) {
2296		zend_iterator_dtor(object->inner.iterator);
2297	}
2298}
2299/* }}} */
2300
2301/* {{{ spl_dual_it_free_storage */
2302static void spl_dual_it_free_storage(zend_object *_object)
2303{
2304	spl_dual_it_object *object = spl_dual_it_from_obj(_object);
2305
2306
2307	if (!Z_ISUNDEF(object->inner.zobject)) {
2308		zval_ptr_dtor(&object->inner.zobject);
2309	}
2310
2311	if (object->dit_type == DIT_AppendIterator) {
2312		zend_iterator_dtor(object->u.append.iterator);
2313		if (Z_TYPE(object->u.append.zarrayit) != IS_UNDEF) {
2314			zval_ptr_dtor(&object->u.append.zarrayit);
2315		}
2316	}
2317
2318	if (object->dit_type == DIT_CachingIterator || object->dit_type == DIT_RecursiveCachingIterator) {
2319		zval_ptr_dtor(&object->u.caching.zcache);
2320	}
2321
2322#if HAVE_PCRE || HAVE_BUNDLED_PCRE
2323	if (object->dit_type == DIT_RegexIterator || object->dit_type == DIT_RecursiveRegexIterator) {
2324		if (object->u.regex.pce) {
2325			php_pcre_pce_decref(object->u.regex.pce);
2326		}
2327		if (object->u.regex.regex) {
2328			zend_string_release_ex(object->u.regex.regex, 0);
2329		}
2330	}
2331#endif
2332
2333	if (object->dit_type == DIT_CallbackFilterIterator || object->dit_type == DIT_RecursiveCallbackFilterIterator) {
2334		if (object->u.cbfilter) {
2335			_spl_cbfilter_it_intern *cbfilter = object->u.cbfilter;
2336			object->u.cbfilter = NULL;
2337			zval_ptr_dtor(&cbfilter->fci.function_name);
2338			if (cbfilter->fci.object) {
2339				OBJ_RELEASE(cbfilter->fci.object);
2340			}
2341			efree(cbfilter);
2342		}
2343	}
2344
2345	zend_object_std_dtor(&object->std);
2346}
2347/* }}} */
2348
2349/* {{{ spl_dual_it_new */
2350static zend_object *spl_dual_it_new(zend_class_entry *class_type)
2351{
2352	spl_dual_it_object *intern;
2353
2354	intern = zend_object_alloc(sizeof(spl_dual_it_object), class_type);
2355	intern->dit_type = DIT_Unknown;
2356
2357	zend_object_std_init(&intern->std, class_type);
2358	object_properties_init(&intern->std, class_type);
2359
2360	intern->std.handlers = &spl_handlers_dual_it;
2361	return &intern->std;
2362}
2363/* }}} */
2364
2365ZEND_BEGIN_ARG_INFO(arginfo_filter_it___construct, 0)
2366	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
2367ZEND_END_ARG_INFO();
2368
2369static const zend_function_entry spl_funcs_FilterIterator[] = {
2370	SPL_ME(FilterIterator,  __construct,      arginfo_filter_it___construct, ZEND_ACC_PUBLIC)
2371	SPL_ME(FilterIterator,  rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2372	SPL_ME(dual_it,         valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2373	SPL_ME(dual_it,         key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2374	SPL_ME(dual_it,         current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2375	SPL_ME(FilterIterator,  next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2376	SPL_ME(dual_it,         getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2377	SPL_ABSTRACT_ME(FilterIterator, accept,   arginfo_recursive_it_void)
2378	PHP_FE_END
2379};
2380
2381ZEND_BEGIN_ARG_INFO(arginfo_callback_filter_it___construct, 0)
2382	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
2383	ZEND_ARG_INFO(0, callback)
2384ZEND_END_ARG_INFO();
2385
2386static const zend_function_entry spl_funcs_CallbackFilterIterator[] = {
2387	SPL_ME(CallbackFilterIterator, __construct, arginfo_callback_filter_it___construct, ZEND_ACC_PUBLIC)
2388	SPL_ME(CallbackFilterIterator, accept,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2389	PHP_FE_END
2390};
2391
2392ZEND_BEGIN_ARG_INFO(arginfo_recursive_callback_filter_it___construct, 0)
2393	ZEND_ARG_OBJ_INFO(0, iterator, RecursiveIterator, 0)
2394	ZEND_ARG_INFO(0, callback)
2395ZEND_END_ARG_INFO();
2396
2397static const zend_function_entry spl_funcs_RecursiveCallbackFilterIterator[] = {
2398	SPL_ME(RecursiveCallbackFilterIterator, __construct, arginfo_recursive_callback_filter_it___construct, ZEND_ACC_PUBLIC)
2399	SPL_ME(RecursiveFilterIterator,  hasChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2400	SPL_ME(RecursiveCallbackFilterIterator,  getChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2401	PHP_FE_END
2402};
2403
2404ZEND_BEGIN_ARG_INFO(arginfo_parent_it___construct, 0)
2405	ZEND_ARG_OBJ_INFO(0, iterator, RecursiveIterator, 0)
2406ZEND_END_ARG_INFO();
2407
2408static const zend_function_entry spl_funcs_RecursiveFilterIterator[] = {
2409	SPL_ME(RecursiveFilterIterator,  __construct,      arginfo_parent_it___construct, ZEND_ACC_PUBLIC)
2410	SPL_ME(RecursiveFilterIterator,  hasChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2411	SPL_ME(RecursiveFilterIterator,  getChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2412	PHP_FE_END
2413};
2414
2415static const zend_function_entry spl_funcs_ParentIterator[] = {
2416	SPL_ME(ParentIterator,  __construct,      arginfo_parent_it___construct, ZEND_ACC_PUBLIC)
2417	SPL_MA(ParentIterator,  accept,           RecursiveFilterIterator, hasChildren, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2418	PHP_FE_END
2419};
2420
2421#if HAVE_PCRE || HAVE_BUNDLED_PCRE
2422ZEND_BEGIN_ARG_INFO_EX(arginfo_regex_it___construct, 0, 0, 2)
2423	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
2424	ZEND_ARG_INFO(0, regex)
2425	ZEND_ARG_INFO(0, mode)
2426	ZEND_ARG_INFO(0, flags)
2427	ZEND_ARG_INFO(0, preg_flags)
2428ZEND_END_ARG_INFO();
2429
2430ZEND_BEGIN_ARG_INFO_EX(arginfo_regex_it_set_mode, 0, 0, 1)
2431	ZEND_ARG_INFO(0, mode)
2432ZEND_END_ARG_INFO();
2433
2434ZEND_BEGIN_ARG_INFO_EX(arginfo_regex_it_set_flags, 0, 0, 1)
2435	ZEND_ARG_INFO(0, flags)
2436ZEND_END_ARG_INFO();
2437
2438ZEND_BEGIN_ARG_INFO_EX(arginfo_regex_it_set_preg_flags, 0, 0, 1)
2439	ZEND_ARG_INFO(0, preg_flags)
2440ZEND_END_ARG_INFO();
2441
2442static const zend_function_entry spl_funcs_RegexIterator[] = {
2443	SPL_ME(RegexIterator,   __construct,      arginfo_regex_it___construct,    ZEND_ACC_PUBLIC)
2444	SPL_ME(RegexIterator,   accept,           arginfo_recursive_it_void,       ZEND_ACC_PUBLIC)
2445	SPL_ME(RegexIterator,   getMode,          arginfo_recursive_it_void,       ZEND_ACC_PUBLIC)
2446	SPL_ME(RegexIterator,   setMode,          arginfo_regex_it_set_mode,       ZEND_ACC_PUBLIC)
2447	SPL_ME(RegexIterator,   getFlags,         arginfo_recursive_it_void,       ZEND_ACC_PUBLIC)
2448	SPL_ME(RegexIterator,   setFlags,         arginfo_regex_it_set_flags,      ZEND_ACC_PUBLIC)
2449	SPL_ME(RegexIterator,   getPregFlags,     arginfo_recursive_it_void,       ZEND_ACC_PUBLIC)
2450	SPL_ME(RegexIterator,   setPregFlags,     arginfo_regex_it_set_preg_flags, ZEND_ACC_PUBLIC)
2451	SPL_ME(RegexIterator,   getRegex,         arginfo_recursive_it_void,       ZEND_ACC_PUBLIC)
2452	PHP_FE_END
2453};
2454
2455ZEND_BEGIN_ARG_INFO_EX(arginfo_rec_regex_it___construct, 0, 0, 2)
2456	ZEND_ARG_OBJ_INFO(0, iterator, RecursiveIterator, 0)
2457	ZEND_ARG_INFO(0, regex)
2458	ZEND_ARG_INFO(0, mode)
2459	ZEND_ARG_INFO(0, flags)
2460	ZEND_ARG_INFO(0, preg_flags)
2461ZEND_END_ARG_INFO();
2462
2463static const zend_function_entry spl_funcs_RecursiveRegexIterator[] = {
2464	SPL_ME(RecursiveRegexIterator,  __construct,      arginfo_rec_regex_it___construct, ZEND_ACC_PUBLIC)
2465	SPL_ME(RecursiveRegexIterator,  accept,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2466	SPL_ME(RecursiveFilterIterator, hasChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2467	SPL_ME(RecursiveRegexIterator,  getChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2468	PHP_FE_END
2469};
2470#endif
2471
2472static inline int spl_limit_it_valid(spl_dual_it_object *intern)
2473{
2474	/* FAILURE / SUCCESS */
2475	if (intern->u.limit.count != -1 && intern->current.pos >= intern->u.limit.offset + intern->u.limit.count) {
2476		return FAILURE;
2477	} else {
2478		return spl_dual_it_valid(intern);
2479	}
2480}
2481
2482static inline void spl_limit_it_seek(spl_dual_it_object *intern, zend_long pos)
2483{
2484	zval  zpos;
2485
2486	spl_dual_it_free(intern);
2487	if (pos < intern->u.limit.offset) {
2488		zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is below the offset " ZEND_LONG_FMT, pos, intern->u.limit.offset);
2489		return;
2490	}
2491	if (pos >= intern->u.limit.offset + intern->u.limit.count && intern->u.limit.count != -1) {
2492		zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is behind offset " ZEND_LONG_FMT " plus count " ZEND_LONG_FMT, pos, intern->u.limit.offset, intern->u.limit.count);
2493		return;
2494	}
2495	if (pos != intern->current.pos && instanceof_function(intern->inner.ce, spl_ce_SeekableIterator)) {
2496		ZVAL_LONG(&zpos, pos);
2497		spl_dual_it_free(intern);
2498		zend_call_method_with_1_params(&intern->inner.zobject, intern->inner.ce, NULL, "seek", NULL, &zpos);
2499		if (!EG(exception)) {
2500			intern->current.pos = pos;
2501			if (spl_limit_it_valid(intern) == SUCCESS) {
2502				spl_dual_it_fetch(intern, 0);
2503			}
2504		}
2505	} else {
2506		/* emulate the forward seek, by next() calls */
2507		/* a back ward seek is done by a previous rewind() */
2508		if (pos < intern->current.pos) {
2509			spl_dual_it_rewind(intern);
2510		}
2511		while (pos > intern->current.pos && spl_dual_it_valid(intern) == SUCCESS) {
2512			spl_dual_it_next(intern, 1);
2513		}
2514		if (spl_dual_it_valid(intern) == SUCCESS) {
2515			spl_dual_it_fetch(intern, 1);
2516		}
2517	}
2518}
2519
2520/* {{{ proto LimitIterator::__construct(Iterator it [, int offset, int count])
2521   Construct a LimitIterator from an Iterator with a given starting offset and optionally a maximum count */
2522SPL_METHOD(LimitIterator, __construct)
2523{
2524	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_LimitIterator, zend_ce_iterator, DIT_LimitIterator);
2525} /* }}} */
2526
2527/* {{{ proto void LimitIterator::rewind()
2528   Rewind the iterator to the specified starting offset */
2529SPL_METHOD(LimitIterator, rewind)
2530{
2531	spl_dual_it_object   *intern;
2532
2533	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2534	spl_dual_it_rewind(intern);
2535	spl_limit_it_seek(intern, intern->u.limit.offset);
2536} /* }}} */
2537
2538/* {{{ proto bool LimitIterator::valid()
2539   Check whether the current element is valid */
2540SPL_METHOD(LimitIterator, valid)
2541{
2542	spl_dual_it_object   *intern;
2543
2544	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2545
2546/*	RETURN_BOOL(spl_limit_it_valid(intern) == SUCCESS);*/
2547	RETURN_BOOL((intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) && Z_TYPE(intern->current.data) != IS_UNDEF);
2548} /* }}} */
2549
2550/* {{{ proto void LimitIterator::next()
2551   Move the iterator forward */
2552SPL_METHOD(LimitIterator, next)
2553{
2554	spl_dual_it_object   *intern;
2555
2556	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2557
2558	spl_dual_it_next(intern, 1);
2559	if (intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) {
2560		spl_dual_it_fetch(intern, 1);
2561	}
2562} /* }}} */
2563
2564/* {{{ proto void LimitIterator::seek(int position)
2565   Seek to the given position */
2566SPL_METHOD(LimitIterator, seek)
2567{
2568	spl_dual_it_object   *intern;
2569	zend_long                 pos;
2570
2571	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &pos) == FAILURE) {
2572		return;
2573	}
2574
2575	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2576	spl_limit_it_seek(intern, pos);
2577	RETURN_LONG(intern->current.pos);
2578} /* }}} */
2579
2580/* {{{ proto int LimitIterator::getPosition()
2581   Return the current position */
2582SPL_METHOD(LimitIterator, getPosition)
2583{
2584	spl_dual_it_object   *intern;
2585	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2586	RETURN_LONG(intern->current.pos);
2587} /* }}} */
2588
2589ZEND_BEGIN_ARG_INFO(arginfo_seekable_it_seek, 0)
2590	ZEND_ARG_INFO(0, position)
2591ZEND_END_ARG_INFO();
2592
2593static const zend_function_entry spl_funcs_SeekableIterator[] = {
2594	SPL_ABSTRACT_ME(SeekableIterator, seek, arginfo_seekable_it_seek)
2595	PHP_FE_END
2596};
2597
2598ZEND_BEGIN_ARG_INFO_EX(arginfo_limit_it___construct, 0, 0, 1)
2599	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
2600	ZEND_ARG_INFO(0, offset)
2601	ZEND_ARG_INFO(0, count)
2602ZEND_END_ARG_INFO();
2603
2604ZEND_BEGIN_ARG_INFO(arginfo_limit_it_seek, 0)
2605	ZEND_ARG_INFO(0, position)
2606ZEND_END_ARG_INFO();
2607
2608static const zend_function_entry spl_funcs_LimitIterator[] = {
2609	SPL_ME(LimitIterator,   __construct,      arginfo_limit_it___construct, ZEND_ACC_PUBLIC)
2610	SPL_ME(LimitIterator,   rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2611	SPL_ME(LimitIterator,   valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2612	SPL_ME(dual_it,         key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2613	SPL_ME(dual_it,         current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2614	SPL_ME(LimitIterator,   next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2615	SPL_ME(LimitIterator,   seek,             arginfo_limit_it_seek, ZEND_ACC_PUBLIC)
2616	SPL_ME(LimitIterator,   getPosition,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2617	SPL_ME(dual_it,         getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2618	PHP_FE_END
2619};
2620
2621static inline int spl_caching_it_valid(spl_dual_it_object *intern)
2622{
2623	return intern->u.caching.flags & CIT_VALID ? SUCCESS : FAILURE;
2624}
2625
2626static inline int spl_caching_it_has_next(spl_dual_it_object *intern)
2627{
2628	return spl_dual_it_valid(intern);
2629}
2630
2631static inline void spl_caching_it_next(spl_dual_it_object *intern)
2632{
2633	if (spl_dual_it_fetch(intern, 1) == SUCCESS) {
2634		intern->u.caching.flags |= CIT_VALID;
2635		/* Full cache ? */
2636		if (intern->u.caching.flags & CIT_FULL_CACHE) {
2637			zval *key = &intern->current.key;
2638			zval *data = &intern->current.data;
2639
2640			ZVAL_DEREF(data);
2641			Z_TRY_ADDREF_P(data);
2642			array_set_zval_key(Z_ARRVAL(intern->u.caching.zcache), key, data);
2643			zval_ptr_dtor(data);
2644		}
2645		/* Recursion ? */
2646		if (intern->dit_type == DIT_RecursiveCachingIterator) {
2647			zval retval, zchildren, zflags;
2648			zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "haschildren", &retval);
2649			if (EG(exception)) {
2650				zval_ptr_dtor(&retval);
2651				if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2652					zend_clear_exception();
2653				} else {
2654					return;
2655				}
2656			} else {
2657				if (zend_is_true(&retval)) {
2658					zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &zchildren);
2659					if (EG(exception)) {
2660						zval_ptr_dtor(&zchildren);
2661						if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2662							zend_clear_exception();
2663						} else {
2664							zval_ptr_dtor(&retval);
2665							return;
2666						}
2667					} else {
2668						ZVAL_LONG(&zflags, intern->u.caching.flags & CIT_PUBLIC);
2669						spl_instantiate_arg_ex2(spl_ce_RecursiveCachingIterator, &intern->u.caching.zchildren, &zchildren, &zflags);
2670						zval_ptr_dtor(&zchildren);
2671					}
2672				}
2673				zval_ptr_dtor(&retval);
2674				if (EG(exception)) {
2675					if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2676						zend_clear_exception();
2677					} else {
2678						return;
2679					}
2680				}
2681			}
2682		}
2683		if (intern->u.caching.flags & (CIT_TOSTRING_USE_INNER|CIT_CALL_TOSTRING)) {
2684			int  use_copy;
2685			zval expr_copy;
2686			if (intern->u.caching.flags & CIT_TOSTRING_USE_INNER) {
2687				ZVAL_COPY_VALUE(&intern->u.caching.zstr, &intern->inner.zobject);
2688			} else {
2689				ZVAL_COPY_VALUE(&intern->u.caching.zstr, &intern->current.data);
2690			}
2691			use_copy = zend_make_printable_zval(&intern->u.caching.zstr, &expr_copy);
2692			if (use_copy) {
2693				ZVAL_COPY_VALUE(&intern->u.caching.zstr, &expr_copy);
2694			} else {
2695				Z_TRY_ADDREF(intern->u.caching.zstr);
2696			}
2697		}
2698		spl_dual_it_next(intern, 0);
2699	} else {
2700		intern->u.caching.flags &= ~CIT_VALID;
2701	}
2702}
2703
2704static inline void spl_caching_it_rewind(spl_dual_it_object *intern)
2705{
2706	spl_dual_it_rewind(intern);
2707	zend_hash_clean(Z_ARRVAL(intern->u.caching.zcache));
2708	spl_caching_it_next(intern);
2709}
2710
2711/* {{{ proto CachingIterator::__construct(Iterator it [, flags = CIT_CALL_TOSTRING])
2712   Construct a CachingIterator from an Iterator */
2713SPL_METHOD(CachingIterator, __construct)
2714{
2715	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_CachingIterator, zend_ce_iterator, DIT_CachingIterator);
2716} /* }}} */
2717
2718/* {{{ proto void CachingIterator::rewind()
2719   Rewind the iterator */
2720SPL_METHOD(CachingIterator, rewind)
2721{
2722	spl_dual_it_object   *intern;
2723
2724	if (zend_parse_parameters_none() == FAILURE) {
2725		return;
2726	}
2727
2728	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2729
2730	spl_caching_it_rewind(intern);
2731} /* }}} */
2732
2733/* {{{ proto bool CachingIterator::valid()
2734   Check whether the current element is valid */
2735SPL_METHOD(CachingIterator, valid)
2736{
2737	spl_dual_it_object   *intern;
2738
2739	if (zend_parse_parameters_none() == FAILURE) {
2740		return;
2741	}
2742
2743	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2744
2745	RETURN_BOOL(spl_caching_it_valid(intern) == SUCCESS);
2746} /* }}} */
2747
2748/* {{{ proto void CachingIterator::next()
2749   Move the iterator forward */
2750SPL_METHOD(CachingIterator, next)
2751{
2752	spl_dual_it_object   *intern;
2753
2754	if (zend_parse_parameters_none() == FAILURE) {
2755		return;
2756	}
2757
2758	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2759
2760	spl_caching_it_next(intern);
2761} /* }}} */
2762
2763/* {{{ proto bool CachingIterator::hasNext()
2764   Check whether the inner iterator has a valid next element */
2765SPL_METHOD(CachingIterator, hasNext)
2766{
2767	spl_dual_it_object   *intern;
2768
2769	if (zend_parse_parameters_none() == FAILURE) {
2770		return;
2771	}
2772
2773	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2774
2775	RETURN_BOOL(spl_caching_it_has_next(intern) == SUCCESS);
2776} /* }}} */
2777
2778/* {{{ proto string CachingIterator::__toString()
2779   Return the string representation of the current element */
2780SPL_METHOD(CachingIterator, __toString)
2781{
2782	spl_dual_it_object   *intern;
2783
2784	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2785
2786	if (!(intern->u.caching.flags & (CIT_CALL_TOSTRING|CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT|CIT_TOSTRING_USE_INNER)))	{
2787		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not fetch string value (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
2788		return;
2789	}
2790	if (intern->u.caching.flags & CIT_TOSTRING_USE_KEY) {
2791		ZVAL_COPY(return_value, &intern->current.key);
2792		convert_to_string(return_value);
2793		return;
2794	} else if (intern->u.caching.flags & CIT_TOSTRING_USE_CURRENT) {
2795		ZVAL_COPY(return_value, &intern->current.data);
2796		convert_to_string(return_value);
2797		return;
2798	}
2799	if (Z_TYPE(intern->u.caching.zstr) == IS_STRING) {
2800		RETURN_STR_COPY(Z_STR_P(&intern->u.caching.zstr));
2801	} else {
2802		RETURN_EMPTY_STRING();
2803	}
2804} /* }}} */
2805
2806/* {{{ proto void CachingIterator::offsetSet(mixed index, mixed newval)
2807   Set given index in cache */
2808SPL_METHOD(CachingIterator, offsetSet)
2809{
2810	spl_dual_it_object   *intern;
2811	zend_string *key;
2812	zval *value;
2813
2814	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2815
2816	if (!(intern->u.caching.flags & CIT_FULL_CACHE))	{
2817		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
2818		return;
2819	}
2820
2821	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &key, &value) == FAILURE) {
2822		return;
2823	}
2824
2825	Z_TRY_ADDREF_P(value);
2826	zend_symtable_update(Z_ARRVAL(intern->u.caching.zcache), key, value);
2827}
2828/* }}} */
2829
2830/* {{{ proto string CachingIterator::offsetGet(mixed index)
2831   Return the internal cache if used */
2832SPL_METHOD(CachingIterator, offsetGet)
2833{
2834	spl_dual_it_object   *intern;
2835	zend_string *key;
2836	zval *value;
2837
2838	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2839
2840	if (!(intern->u.caching.flags & CIT_FULL_CACHE))	{
2841		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
2842		return;
2843	}
2844
2845	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &key) == FAILURE) {
2846		return;
2847	}
2848
2849	if ((value = zend_symtable_find(Z_ARRVAL(intern->u.caching.zcache), key)) == NULL) {
2850		zend_error(E_NOTICE, "Undefined index: %s", ZSTR_VAL(key));
2851		return;
2852	}
2853
2854	ZVAL_COPY_DEREF(return_value, value);
2855}
2856/* }}} */
2857
2858/* {{{ proto void CachingIterator::offsetUnset(mixed index)
2859   Unset given index in cache */
2860SPL_METHOD(CachingIterator, offsetUnset)
2861{
2862	spl_dual_it_object   *intern;
2863	zend_string *key;
2864
2865	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2866
2867	if (!(intern->u.caching.flags & CIT_FULL_CACHE))	{
2868		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
2869		return;
2870	}
2871
2872	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &key) == FAILURE) {
2873		return;
2874	}
2875
2876	zend_symtable_del(Z_ARRVAL(intern->u.caching.zcache), key);
2877}
2878/* }}} */
2879
2880/* {{{ proto bool CachingIterator::offsetExists(mixed index)
2881   Return whether the requested index exists */
2882SPL_METHOD(CachingIterator, offsetExists)
2883{
2884	spl_dual_it_object   *intern;
2885	zend_string *key;
2886
2887	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2888
2889	if (!(intern->u.caching.flags & CIT_FULL_CACHE))	{
2890		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
2891		return;
2892	}
2893
2894	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &key) == FAILURE) {
2895		return;
2896	}
2897
2898	RETURN_BOOL(zend_symtable_exists(Z_ARRVAL(intern->u.caching.zcache), key));
2899}
2900/* }}} */
2901
2902/* {{{ proto bool CachingIterator::getCache()
2903   Return the cache */
2904SPL_METHOD(CachingIterator, getCache)
2905{
2906	spl_dual_it_object   *intern;
2907
2908	if (zend_parse_parameters_none() == FAILURE) {
2909		return;
2910	}
2911
2912	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2913
2914	if (!(intern->u.caching.flags & CIT_FULL_CACHE))	{
2915		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
2916		return;
2917	}
2918
2919	ZVAL_COPY(return_value, &intern->u.caching.zcache);
2920}
2921/* }}} */
2922
2923/* {{{ proto int CachingIterator::getFlags()
2924   Return the internal flags */
2925SPL_METHOD(CachingIterator, getFlags)
2926{
2927	spl_dual_it_object   *intern;
2928
2929	if (zend_parse_parameters_none() == FAILURE) {
2930		return;
2931	}
2932
2933	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2934
2935	RETURN_LONG(intern->u.caching.flags);
2936}
2937/* }}} */
2938
2939/* {{{ proto void CachingIterator::setFlags(int flags)
2940   Set the internal flags */
2941SPL_METHOD(CachingIterator, setFlags)
2942{
2943	spl_dual_it_object   *intern;
2944	zend_long flags;
2945
2946	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2947
2948	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &flags) == FAILURE) {
2949		return;
2950	}
2951
2952	if (spl_cit_check_flags(flags) != SUCCESS) {
2953		zend_throw_exception(spl_ce_InvalidArgumentException , "Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER", 0);
2954		return;
2955	}
2956	if ((intern->u.caching.flags & CIT_CALL_TOSTRING) != 0 && (flags & CIT_CALL_TOSTRING) == 0) {
2957		zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag CALL_TO_STRING is not possible", 0);
2958		return;
2959	}
2960	if ((intern->u.caching.flags & CIT_TOSTRING_USE_INNER) != 0 && (flags & CIT_TOSTRING_USE_INNER) == 0) {
2961		zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag TOSTRING_USE_INNER is not possible", 0);
2962		return;
2963	}
2964	if ((flags & CIT_FULL_CACHE) != 0 && (intern->u.caching.flags & CIT_FULL_CACHE) == 0) {
2965		/* clear on (re)enable */
2966		zend_hash_clean(Z_ARRVAL(intern->u.caching.zcache));
2967	}
2968	intern->u.caching.flags = (intern->u.caching.flags & ~CIT_PUBLIC) | (flags & CIT_PUBLIC);
2969}
2970/* }}} */
2971
2972/* {{{ proto void CachingIterator::count()
2973   Number of cached elements */
2974SPL_METHOD(CachingIterator, count)
2975{
2976	spl_dual_it_object   *intern;
2977
2978	if (zend_parse_parameters_none() == FAILURE) {
2979		return;
2980	}
2981
2982	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2983
2984	if (!(intern->u.caching.flags & CIT_FULL_CACHE))	{
2985		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(getThis())->name));
2986		return;
2987	}
2988
2989	RETURN_LONG(zend_hash_num_elements(Z_ARRVAL(intern->u.caching.zcache)));
2990}
2991/* }}} */
2992
2993ZEND_BEGIN_ARG_INFO_EX(arginfo_caching_it___construct, 0, 0, 1)
2994	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
2995	ZEND_ARG_INFO(0, flags)
2996ZEND_END_ARG_INFO();
2997
2998ZEND_BEGIN_ARG_INFO(arginfo_caching_it_setFlags, 0)
2999	ZEND_ARG_INFO(0, flags)
3000ZEND_END_ARG_INFO();
3001
3002ZEND_BEGIN_ARG_INFO(arginfo_caching_it_offsetGet, 0)
3003	ZEND_ARG_INFO(0, index)
3004ZEND_END_ARG_INFO();
3005
3006ZEND_BEGIN_ARG_INFO(arginfo_caching_it_offsetSet, 0)
3007	ZEND_ARG_INFO(0, index)
3008	ZEND_ARG_INFO(0, newval)
3009ZEND_END_ARG_INFO();
3010
3011static const zend_function_entry spl_funcs_CachingIterator[] = {
3012	SPL_ME(CachingIterator, __construct,      arginfo_caching_it___construct, ZEND_ACC_PUBLIC)
3013	SPL_ME(CachingIterator, rewind,           arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3014	SPL_ME(CachingIterator, valid,            arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3015	SPL_ME(dual_it,         key,              arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3016	SPL_ME(dual_it,         current,          arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3017	SPL_ME(CachingIterator, next,             arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3018	SPL_ME(CachingIterator, hasNext,          arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3019	SPL_ME(CachingIterator, __toString,       arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3020	SPL_ME(dual_it,         getInnerIterator, arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3021	SPL_ME(CachingIterator, getFlags,         arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3022	SPL_ME(CachingIterator, setFlags,         arginfo_caching_it_setFlags,    ZEND_ACC_PUBLIC)
3023	SPL_ME(CachingIterator, offsetGet,        arginfo_caching_it_offsetGet,   ZEND_ACC_PUBLIC)
3024	SPL_ME(CachingIterator, offsetSet,        arginfo_caching_it_offsetSet,   ZEND_ACC_PUBLIC)
3025	SPL_ME(CachingIterator, offsetUnset,      arginfo_caching_it_offsetGet,   ZEND_ACC_PUBLIC)
3026	SPL_ME(CachingIterator, offsetExists,     arginfo_caching_it_offsetGet,   ZEND_ACC_PUBLIC)
3027	SPL_ME(CachingIterator, getCache,         arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3028	SPL_ME(CachingIterator, count,            arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
3029	PHP_FE_END
3030};
3031
3032/* {{{ proto RecursiveCachingIterator::__construct(RecursiveIterator it [, flags = CIT_CALL_TOSTRING])
3033   Create an iterator from a RecursiveIterator */
3034SPL_METHOD(RecursiveCachingIterator, __construct)
3035{
3036	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveCachingIterator, spl_ce_RecursiveIterator, DIT_RecursiveCachingIterator);
3037} /* }}} */
3038
3039/* {{{ proto bool RecursiveCachingIterator::hasChildren()
3040   Check whether the current element of the inner iterator has children */
3041SPL_METHOD(RecursiveCachingIterator, hasChildren)
3042{
3043	spl_dual_it_object   *intern;
3044
3045	if (zend_parse_parameters_none() == FAILURE) {
3046		return;
3047	}
3048
3049	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3050
3051	RETURN_BOOL(Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF);
3052} /* }}} */
3053
3054/* {{{ proto RecursiveCachingIterator RecursiveCachingIterator::getChildren()
3055  Return the inner iterator's children as a RecursiveCachingIterator */
3056SPL_METHOD(RecursiveCachingIterator, getChildren)
3057{
3058	spl_dual_it_object   *intern;
3059
3060	if (zend_parse_parameters_none() == FAILURE) {
3061		return;
3062	}
3063
3064	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3065
3066	if (Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF) {
3067		zval *value = &intern->u.caching.zchildren;
3068
3069		ZVAL_COPY_DEREF(return_value, value);
3070	} else {
3071		RETURN_NULL();
3072	}
3073} /* }}} */
3074
3075ZEND_BEGIN_ARG_INFO_EX(arginfo_caching_rec_it___construct, 0, ZEND_RETURN_VALUE, 1)
3076	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
3077	ZEND_ARG_INFO(0, flags)
3078ZEND_END_ARG_INFO();
3079
3080static const zend_function_entry spl_funcs_RecursiveCachingIterator[] = {
3081	SPL_ME(RecursiveCachingIterator, __construct,   arginfo_caching_rec_it___construct, ZEND_ACC_PUBLIC)
3082	SPL_ME(RecursiveCachingIterator, hasChildren,   arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3083	SPL_ME(RecursiveCachingIterator, getChildren,   arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3084	PHP_FE_END
3085};
3086
3087/* {{{ proto IteratorIterator::__construct(Traversable it)
3088   Create an iterator from anything that is traversable */
3089SPL_METHOD(IteratorIterator, __construct)
3090{
3091	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_IteratorIterator, zend_ce_traversable, DIT_IteratorIterator);
3092} /* }}} */
3093
3094ZEND_BEGIN_ARG_INFO(arginfo_iterator_it___construct, 0)
3095	ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
3096ZEND_END_ARG_INFO();
3097
3098static const zend_function_entry spl_funcs_IteratorIterator[] = {
3099	SPL_ME(IteratorIterator, __construct,      arginfo_iterator_it___construct, ZEND_ACC_PUBLIC)
3100	SPL_ME(dual_it,          rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3101	SPL_ME(dual_it,          valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3102	SPL_ME(dual_it,          key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3103	SPL_ME(dual_it,          current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3104	SPL_ME(dual_it,          next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3105	SPL_ME(dual_it,          getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3106	PHP_FE_END
3107};
3108
3109/* {{{ proto NoRewindIterator::__construct(Iterator it)
3110   Create an iterator from another iterator */
3111SPL_METHOD(NoRewindIterator, __construct)
3112{
3113	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_NoRewindIterator, zend_ce_iterator, DIT_NoRewindIterator);
3114} /* }}} */
3115
3116/* {{{ proto void NoRewindIterator::rewind()
3117   Prevent a call to inner iterators rewind() */
3118SPL_METHOD(NoRewindIterator, rewind)
3119{
3120	if (zend_parse_parameters_none() == FAILURE) {
3121		return;
3122	}
3123	/* nothing to do */
3124} /* }}} */
3125
3126/* {{{ proto bool NoRewindIterator::valid()
3127   Return inner iterators valid() */
3128SPL_METHOD(NoRewindIterator, valid)
3129{
3130	spl_dual_it_object   *intern;
3131
3132	if (zend_parse_parameters_none() == FAILURE) {
3133		return;
3134	}
3135
3136	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3137	RETURN_BOOL(intern->inner.iterator->funcs->valid(intern->inner.iterator) == SUCCESS);
3138} /* }}} */
3139
3140/* {{{ proto mixed NoRewindIterator::key()
3141   Return inner iterators key() */
3142SPL_METHOD(NoRewindIterator, key)
3143{
3144	spl_dual_it_object   *intern;
3145
3146	if (zend_parse_parameters_none() == FAILURE) {
3147		return;
3148	}
3149
3150	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3151
3152	if (intern->inner.iterator->funcs->get_current_key) {
3153		intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, return_value);
3154	} else {
3155		RETURN_NULL();
3156	}
3157} /* }}} */
3158
3159/* {{{ proto mixed NoRewindIterator::current()
3160   Return inner iterators current() */
3161SPL_METHOD(NoRewindIterator, current)
3162{
3163	spl_dual_it_object   *intern;
3164	zval *data;
3165
3166	if (zend_parse_parameters_none() == FAILURE) {
3167		return;
3168	}
3169
3170	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3171	data = intern->inner.iterator->funcs->get_current_data(intern->inner.iterator);
3172	if (data) {
3173		ZVAL_COPY_DEREF(return_value, data);
3174	}
3175} /* }}} */
3176
3177/* {{{ proto void NoRewindIterator::next()
3178   Return inner iterators next() */
3179SPL_METHOD(NoRewindIterator, next)
3180{
3181	spl_dual_it_object   *intern;
3182
3183	if (zend_parse_parameters_none() == FAILURE) {
3184		return;
3185	}
3186
3187	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3188	intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
3189} /* }}} */
3190
3191ZEND_BEGIN_ARG_INFO(arginfo_norewind_it___construct, 0)
3192	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
3193ZEND_END_ARG_INFO();
3194
3195static const zend_function_entry spl_funcs_NoRewindIterator[] = {
3196	SPL_ME(NoRewindIterator, __construct,      arginfo_norewind_it___construct, ZEND_ACC_PUBLIC)
3197	SPL_ME(NoRewindIterator, rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3198	SPL_ME(NoRewindIterator, valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3199	SPL_ME(NoRewindIterator, key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3200	SPL_ME(NoRewindIterator, current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3201	SPL_ME(NoRewindIterator, next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3202	SPL_ME(dual_it,          getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3203	PHP_FE_END
3204};
3205
3206/* {{{ proto InfiniteIterator::__construct(Iterator it)
3207   Create an iterator from another iterator */
3208SPL_METHOD(InfiniteIterator, __construct)
3209{
3210	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_InfiniteIterator, zend_ce_iterator, DIT_InfiniteIterator);
3211} /* }}} */
3212
3213/* {{{ proto void InfiniteIterator::next()
3214   Prevent a call to inner iterators rewind() (internally the current data will be fetched if valid()) */
3215SPL_METHOD(InfiniteIterator, next)
3216{
3217	spl_dual_it_object   *intern;
3218
3219	if (zend_parse_parameters_none() == FAILURE) {
3220		return;
3221	}
3222
3223	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3224
3225	spl_dual_it_next(intern, 1);
3226	if (spl_dual_it_valid(intern) == SUCCESS) {
3227		spl_dual_it_fetch(intern, 0);
3228	} else {
3229		spl_dual_it_rewind(intern);
3230		if (spl_dual_it_valid(intern) == SUCCESS) {
3231			spl_dual_it_fetch(intern, 0);
3232		}
3233	}
3234} /* }}} */
3235
3236static const zend_function_entry spl_funcs_InfiniteIterator[] = {
3237	SPL_ME(InfiniteIterator, __construct,      arginfo_norewind_it___construct, ZEND_ACC_PUBLIC)
3238	SPL_ME(InfiniteIterator, next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3239	PHP_FE_END
3240};
3241
3242/* {{{ proto void EmptyIterator::rewind()
3243   Does nothing  */
3244SPL_METHOD(EmptyIterator, rewind)
3245{
3246	if (zend_parse_parameters_none() == FAILURE) {
3247		return;
3248	}
3249} /* }}} */
3250
3251/* {{{ proto false EmptyIterator::valid()
3252   Return false */
3253SPL_METHOD(EmptyIterator, valid)
3254{
3255	if (zend_parse_parameters_none() == FAILURE) {
3256		return;
3257	}
3258	RETURN_FALSE;
3259} /* }}} */
3260
3261/* {{{ proto void EmptyIterator::key()
3262   Throws exception BadMethodCallException */
3263SPL_METHOD(EmptyIterator, key)
3264{
3265	if (zend_parse_parameters_none() == FAILURE) {
3266		return;
3267	}
3268	zend_throw_exception(spl_ce_BadMethodCallException, "Accessing the key of an EmptyIterator", 0);
3269} /* }}} */
3270
3271/* {{{ proto void EmptyIterator::current()
3272   Throws exception BadMethodCallException */
3273SPL_METHOD(EmptyIterator, current)
3274{
3275	if (zend_parse_parameters_none() == FAILURE) {
3276		return;
3277	}
3278	zend_throw_exception(spl_ce_BadMethodCallException, "Accessing the value of an EmptyIterator", 0);
3279} /* }}} */
3280
3281/* {{{ proto void EmptyIterator::next()
3282   Does nothing */
3283SPL_METHOD(EmptyIterator, next)
3284{
3285	if (zend_parse_parameters_none() == FAILURE) {
3286		return;
3287	}
3288} /* }}} */
3289
3290static const zend_function_entry spl_funcs_EmptyIterator[] = {
3291	SPL_ME(EmptyIterator, rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3292	SPL_ME(EmptyIterator, valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3293	SPL_ME(EmptyIterator, key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3294	SPL_ME(EmptyIterator, current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3295	SPL_ME(EmptyIterator, next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3296	PHP_FE_END
3297};
3298
3299int spl_append_it_next_iterator(spl_dual_it_object *intern) /* {{{*/
3300{
3301	spl_dual_it_free(intern);
3302
3303	if (!Z_ISUNDEF(intern->inner.zobject)) {
3304		zval_ptr_dtor(&intern->inner.zobject);
3305		ZVAL_UNDEF(&intern->inner.zobject);
3306		intern->inner.ce = NULL;
3307		if (intern->inner.iterator) {
3308			zend_iterator_dtor(intern->inner.iterator);
3309			intern->inner.iterator = NULL;
3310		}
3311	}
3312	if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) == SUCCESS) {
3313		zval *it;
3314
3315		it  = intern->u.append.iterator->funcs->get_current_data(intern->u.append.iterator);
3316		ZVAL_COPY(&intern->inner.zobject, it);
3317		intern->inner.ce = Z_OBJCE_P(it);
3318		intern->inner.iterator = intern->inner.ce->get_iterator(intern->inner.ce, it, 0);
3319		spl_dual_it_rewind(intern);
3320		return SUCCESS;
3321	} else {
3322		return FAILURE;
3323	}
3324} /* }}} */
3325
3326void spl_append_it_fetch(spl_dual_it_object *intern) /* {{{*/
3327{
3328	while (spl_dual_it_valid(intern) != SUCCESS) {
3329		intern->u.append.iterator->funcs->move_forward(intern->u.append.iterator);
3330		if (spl_append_it_next_iterator(intern) != SUCCESS) {
3331			return;
3332		}
3333	}
3334	spl_dual_it_fetch(intern, 0);
3335} /* }}} */
3336
3337void spl_append_it_next(spl_dual_it_object *intern) /* {{{ */
3338{
3339	if (spl_dual_it_valid(intern) == SUCCESS) {
3340		spl_dual_it_next(intern, 1);
3341	}
3342	spl_append_it_fetch(intern);
3343} /* }}} */
3344
3345/* {{{ proto AppendIterator::__construct()
3346   Create an AppendIterator */
3347SPL_METHOD(AppendIterator, __construct)
3348{
3349	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_AppendIterator, zend_ce_iterator, DIT_AppendIterator);
3350} /* }}} */
3351
3352/* {{{ proto void AppendIterator::append(Iterator it)
3353   Append an iterator */
3354SPL_METHOD(AppendIterator, append)
3355{
3356	spl_dual_it_object   *intern;
3357	zval *it;
3358
3359	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3360
3361	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &it, zend_ce_iterator) == FAILURE) {
3362		return;
3363	}
3364	if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) == SUCCESS && spl_dual_it_valid(intern) != SUCCESS) {
3365		spl_array_iterator_append(&intern->u.append.zarrayit, it);
3366		intern->u.append.iterator->funcs->move_forward(intern->u.append.iterator);
3367	}else{
3368		spl_array_iterator_append(&intern->u.append.zarrayit, it);
3369	}
3370
3371	if (!intern->inner.iterator || spl_dual_it_valid(intern) != SUCCESS) {
3372		if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) != SUCCESS) {
3373			intern->u.append.iterator->funcs->rewind(intern->u.append.iterator);
3374		}
3375		do {
3376			spl_append_it_next_iterator(intern);
3377		} while (Z_OBJ(intern->inner.zobject) != Z_OBJ_P(it));
3378		spl_append_it_fetch(intern);
3379	}
3380} /* }}} */
3381
3382/* {{{ proto mixed AppendIterator::current()
3383   Get the current element value */
3384SPL_METHOD(AppendIterator, current)
3385{
3386	spl_dual_it_object   *intern;
3387
3388	if (zend_parse_parameters_none() == FAILURE) {
3389		return;
3390	}
3391
3392	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3393
3394	spl_dual_it_fetch(intern, 1);
3395	if (Z_TYPE(intern->current.data) != IS_UNDEF) {
3396		zval *value = &intern->current.data;
3397
3398		ZVAL_COPY_DEREF(return_value, value);
3399	} else {
3400		RETURN_NULL();
3401	}
3402} /* }}} */
3403
3404/* {{{ proto void AppendIterator::rewind()
3405   Rewind to the first iterator and rewind the first iterator, too */
3406SPL_METHOD(AppendIterator, rewind)
3407{
3408	spl_dual_it_object   *intern;
3409
3410	if (zend_parse_parameters_none() == FAILURE) {
3411		return;
3412	}
3413
3414	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3415
3416	intern->u.append.iterator->funcs->rewind(intern->u.append.iterator);
3417	if (spl_append_it_next_iterator(intern) == SUCCESS) {
3418		spl_append_it_fetch(intern);
3419	}
3420} /* }}} */
3421
3422/* {{{ proto bool AppendIterator::valid()
3423   Check if the current state is valid */
3424SPL_METHOD(AppendIterator, valid)
3425{
3426	spl_dual_it_object   *intern;
3427
3428	if (zend_parse_parameters_none() == FAILURE) {
3429		return;
3430	}
3431
3432	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3433
3434	RETURN_BOOL(Z_TYPE(intern->current.data) != IS_UNDEF);
3435} /* }}} */
3436
3437/* {{{ proto void AppendIterator::next()
3438   Forward to next element */
3439SPL_METHOD(AppendIterator, next)
3440{
3441	spl_dual_it_object   *intern;
3442
3443	if (zend_parse_parameters_none() == FAILURE) {
3444		return;
3445	}
3446
3447	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3448
3449	spl_append_it_next(intern);
3450} /* }}} */
3451
3452/* {{{ proto int AppendIterator::getIteratorIndex()
3453   Get index of iterator */
3454SPL_METHOD(AppendIterator, getIteratorIndex)
3455{
3456	spl_dual_it_object   *intern;
3457
3458	if (zend_parse_parameters_none() == FAILURE) {
3459		return;
3460	}
3461
3462	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3463
3464	APPENDIT_CHECK_CTOR(intern);
3465	spl_array_iterator_key(&intern->u.append.zarrayit, return_value);
3466} /* }}} */
3467
3468/* {{{ proto ArrayIterator AppendIterator::getArrayIterator()
3469   Get access to inner ArrayIterator */
3470SPL_METHOD(AppendIterator, getArrayIterator)
3471{
3472	spl_dual_it_object   *intern;
3473	zval *value;
3474
3475	if (zend_parse_parameters_none() == FAILURE) {
3476		return;
3477	}
3478
3479	SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3480
3481	value = &intern->u.append.zarrayit;
3482	ZVAL_COPY_DEREF(return_value, value);
3483} /* }}} */
3484
3485ZEND_BEGIN_ARG_INFO(arginfo_append_it_append, 0)
3486	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
3487ZEND_END_ARG_INFO();
3488
3489static const zend_function_entry spl_funcs_AppendIterator[] = {
3490	SPL_ME(AppendIterator, __construct,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3491	SPL_ME(AppendIterator, append,           arginfo_append_it_append, ZEND_ACC_PUBLIC)
3492	SPL_ME(AppendIterator, rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3493	SPL_ME(AppendIterator, valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3494	SPL_ME(dual_it,        key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3495	SPL_ME(AppendIterator, current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3496	SPL_ME(AppendIterator, next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3497	SPL_ME(dual_it,        getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3498	SPL_ME(AppendIterator, getIteratorIndex, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3499	SPL_ME(AppendIterator, getArrayIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3500	PHP_FE_END
3501};
3502
3503PHPAPI int spl_iterator_apply(zval *obj, spl_iterator_apply_func_t apply_func, void *puser)
3504{
3505	zend_object_iterator   *iter;
3506	zend_class_entry       *ce = Z_OBJCE_P(obj);
3507
3508	iter = ce->get_iterator(ce, obj, 0);
3509
3510	if (EG(exception)) {
3511		goto done;
3512	}
3513
3514	iter->index = 0;
3515	if (iter->funcs->rewind) {
3516		iter->funcs->rewind(iter);
3517		if (EG(exception)) {
3518			goto done;
3519		}
3520	}
3521
3522	while (iter->funcs->valid(iter) == SUCCESS) {
3523		if (EG(exception)) {
3524			goto done;
3525		}
3526		if (apply_func(iter, puser) == ZEND_HASH_APPLY_STOP || EG(exception)) {
3527			goto done;
3528		}
3529		iter->index++;
3530		iter->funcs->move_forward(iter);
3531		if (EG(exception)) {
3532			goto done;
3533		}
3534	}
3535
3536done:
3537	if (iter) {
3538		zend_iterator_dtor(iter);
3539	}
3540	return EG(exception) ? FAILURE : SUCCESS;
3541}
3542/* }}} */
3543
3544static int spl_iterator_to_array_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3545{
3546	zval *data, *return_value = (zval*)puser;
3547
3548	data = iter->funcs->get_current_data(iter);
3549	if (EG(exception)) {
3550		return ZEND_HASH_APPLY_STOP;
3551	}
3552	if (data == NULL) {
3553		return ZEND_HASH_APPLY_STOP;
3554	}
3555	if (iter->funcs->get_current_key) {
3556		zval key;
3557		iter->funcs->get_current_key(iter, &key);
3558		if (EG(exception)) {
3559			return ZEND_HASH_APPLY_STOP;
3560		}
3561		array_set_zval_key(Z_ARRVAL_P(return_value), &key, data);
3562		zval_ptr_dtor(&key);
3563	} else {
3564		Z_TRY_ADDREF_P(data);
3565		add_next_index_zval(return_value, data);
3566	}
3567	return ZEND_HASH_APPLY_KEEP;
3568}
3569/* }}} */
3570
3571static int spl_iterator_to_values_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3572{
3573	zval *data, *return_value = (zval*)puser;
3574
3575	data = iter->funcs->get_current_data(iter);
3576	if (EG(exception)) {
3577		return ZEND_HASH_APPLY_STOP;
3578	}
3579	if (data == NULL) {
3580		return ZEND_HASH_APPLY_STOP;
3581	}
3582	Z_TRY_ADDREF_P(data);
3583	add_next_index_zval(return_value, data);
3584	return ZEND_HASH_APPLY_KEEP;
3585}
3586/* }}} */
3587
3588/* {{{ proto array iterator_to_array(Traversable it [, bool use_keys = true])
3589   Copy the iterator into an array */
3590PHP_FUNCTION(iterator_to_array)
3591{
3592	zval  *obj;
3593	zend_bool use_keys = 1;
3594
3595	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &obj, zend_ce_traversable, &use_keys) == FAILURE) {
3596		RETURN_FALSE;
3597	}
3598
3599	array_init(return_value);
3600
3601	if (spl_iterator_apply(obj, use_keys ? spl_iterator_to_array_apply : spl_iterator_to_values_apply, (void*)return_value) != SUCCESS) {
3602		zval_ptr_dtor(return_value);
3603		RETURN_NULL();
3604	}
3605} /* }}} */
3606
3607static int spl_iterator_count_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3608{
3609	(*(zend_long*)puser)++;
3610	return ZEND_HASH_APPLY_KEEP;
3611}
3612/* }}} */
3613
3614/* {{{ proto int iterator_count(Traversable it)
3615   Count the elements in an iterator */
3616PHP_FUNCTION(iterator_count)
3617{
3618	zval  *obj;
3619	zend_long  count = 0;
3620
3621	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &obj, zend_ce_traversable) == FAILURE) {
3622		RETURN_FALSE;
3623	}
3624
3625	if (spl_iterator_apply(obj, spl_iterator_count_apply, (void*)&count) == SUCCESS) {
3626		RETURN_LONG(count);
3627	}
3628}
3629/* }}} */
3630
3631typedef struct {
3632	zval                   *obj;
3633	zval                   *args;
3634	zend_long              count;
3635	zend_fcall_info        fci;
3636	zend_fcall_info_cache  fcc;
3637} spl_iterator_apply_info;
3638
3639static int spl_iterator_func_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3640{
3641	zval retval;
3642	spl_iterator_apply_info  *apply_info = (spl_iterator_apply_info*)puser;
3643	int result;
3644
3645	apply_info->count++;
3646	zend_fcall_info_call(&apply_info->fci, &apply_info->fcc, &retval, NULL);
3647	result = zend_is_true(&retval) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_STOP;
3648	zval_ptr_dtor(&retval);
3649	return result;
3650}
3651/* }}} */
3652
3653/* {{{ proto int iterator_apply(Traversable it, mixed function [, mixed params])
3654   Calls a function for every element in an iterator */
3655PHP_FUNCTION(iterator_apply)
3656{
3657	spl_iterator_apply_info  apply_info;
3658
3659	apply_info.args = NULL;
3660	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Of|a!", &apply_info.obj, zend_ce_traversable, &apply_info.fci, &apply_info.fcc, &apply_info.args) == FAILURE) {
3661		return;
3662	}
3663
3664	apply_info.count = 0;
3665	zend_fcall_info_args(&apply_info.fci, apply_info.args);
3666	if (spl_iterator_apply(apply_info.obj, spl_iterator_func_apply, (void*)&apply_info) == SUCCESS) {
3667		RETVAL_LONG(apply_info.count);
3668	} else {
3669		RETVAL_FALSE;
3670	}
3671	zend_fcall_info_args(&apply_info.fci, NULL);
3672}
3673/* }}} */
3674
3675static const zend_function_entry spl_funcs_OuterIterator[] = {
3676	SPL_ABSTRACT_ME(OuterIterator, getInnerIterator,   arginfo_recursive_it_void)
3677	PHP_FE_END
3678};
3679
3680/* {{{ PHP_MINIT_FUNCTION(spl_iterators)
3681 */
3682PHP_MINIT_FUNCTION(spl_iterators)
3683{
3684	REGISTER_SPL_INTERFACE(RecursiveIterator);
3685	REGISTER_SPL_ITERATOR(RecursiveIterator);
3686
3687	REGISTER_SPL_STD_CLASS_EX(RecursiveIteratorIterator, spl_RecursiveIteratorIterator_new, spl_funcs_RecursiveIteratorIterator);
3688	REGISTER_SPL_ITERATOR(RecursiveIteratorIterator);
3689
3690	memcpy(&spl_handlers_rec_it_it, &std_object_handlers, sizeof(zend_object_handlers));
3691	spl_handlers_rec_it_it.offset = XtOffsetOf(spl_recursive_it_object, std);
3692	spl_handlers_rec_it_it.get_method = spl_recursive_it_get_method;
3693	spl_handlers_rec_it_it.clone_obj = NULL;
3694	spl_handlers_rec_it_it.dtor_obj = spl_RecursiveIteratorIterator_dtor;
3695	spl_handlers_rec_it_it.free_obj = spl_RecursiveIteratorIterator_free_storage;
3696
3697	memcpy(&spl_handlers_dual_it, &std_object_handlers, sizeof(zend_object_handlers));
3698	spl_handlers_dual_it.offset = XtOffsetOf(spl_dual_it_object, std);
3699	spl_handlers_dual_it.get_method = spl_dual_it_get_method;
3700	/*spl_handlers_dual_it.call_method = spl_dual_it_call_method;*/
3701	spl_handlers_dual_it.clone_obj = NULL;
3702	spl_handlers_dual_it.dtor_obj = spl_dual_it_dtor;
3703	spl_handlers_dual_it.free_obj = spl_dual_it_free_storage;
3704
3705	spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator;
3706
3707	REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "LEAVES_ONLY",     RIT_LEAVES_ONLY);
3708	REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "SELF_FIRST",      RIT_SELF_FIRST);
3709	REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "CHILD_FIRST",     RIT_CHILD_FIRST);
3710	REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "CATCH_GET_CHILD", RIT_CATCH_GET_CHILD);
3711
3712	REGISTER_SPL_INTERFACE(OuterIterator);
3713	REGISTER_SPL_ITERATOR(OuterIterator);
3714
3715	REGISTER_SPL_STD_CLASS_EX(IteratorIterator, spl_dual_it_new, spl_funcs_IteratorIterator);
3716	REGISTER_SPL_ITERATOR(IteratorIterator);
3717	REGISTER_SPL_IMPLEMENTS(IteratorIterator, OuterIterator);
3718
3719	REGISTER_SPL_SUB_CLASS_EX(FilterIterator, IteratorIterator, spl_dual_it_new, spl_funcs_FilterIterator);
3720	spl_ce_FilterIterator->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
3721
3722	REGISTER_SPL_SUB_CLASS_EX(RecursiveFilterIterator, FilterIterator, spl_dual_it_new, spl_funcs_RecursiveFilterIterator);
3723	REGISTER_SPL_IMPLEMENTS(RecursiveFilterIterator, RecursiveIterator);
3724
3725	REGISTER_SPL_SUB_CLASS_EX(CallbackFilterIterator, FilterIterator, spl_dual_it_new, spl_funcs_CallbackFilterIterator);
3726
3727	REGISTER_SPL_SUB_CLASS_EX(RecursiveCallbackFilterIterator, CallbackFilterIterator, spl_dual_it_new, spl_funcs_RecursiveCallbackFilterIterator);
3728	REGISTER_SPL_IMPLEMENTS(RecursiveCallbackFilterIterator, RecursiveIterator);
3729
3730
3731	REGISTER_SPL_SUB_CLASS_EX(ParentIterator, RecursiveFilterIterator, spl_dual_it_new, spl_funcs_ParentIterator);
3732
3733	REGISTER_SPL_INTERFACE(SeekableIterator);
3734	REGISTER_SPL_ITERATOR(SeekableIterator);
3735
3736	REGISTER_SPL_SUB_CLASS_EX(LimitIterator, IteratorIterator, spl_dual_it_new, spl_funcs_LimitIterator);
3737
3738	REGISTER_SPL_SUB_CLASS_EX(CachingIterator, IteratorIterator, spl_dual_it_new, spl_funcs_CachingIterator);
3739	REGISTER_SPL_IMPLEMENTS(CachingIterator, ArrayAccess);
3740	REGISTER_SPL_IMPLEMENTS(CachingIterator, Countable);
3741
3742	REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "CALL_TOSTRING",        CIT_CALL_TOSTRING);
3743	REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "CATCH_GET_CHILD",      CIT_CATCH_GET_CHILD);
3744	REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_KEY",     CIT_TOSTRING_USE_KEY);
3745	REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_CURRENT", CIT_TOSTRING_USE_CURRENT);
3746	REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_INNER",   CIT_TOSTRING_USE_INNER);
3747	REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "FULL_CACHE",           CIT_FULL_CACHE);
3748
3749	REGISTER_SPL_SUB_CLASS_EX(RecursiveCachingIterator, CachingIterator, spl_dual_it_new, spl_funcs_RecursiveCachingIterator);
3750	REGISTER_SPL_IMPLEMENTS(RecursiveCachingIterator, RecursiveIterator);
3751
3752	REGISTER_SPL_SUB_CLASS_EX(NoRewindIterator, IteratorIterator, spl_dual_it_new, spl_funcs_NoRewindIterator);
3753
3754	REGISTER_SPL_SUB_CLASS_EX(AppendIterator, IteratorIterator, spl_dual_it_new, spl_funcs_AppendIterator);
3755
3756	REGISTER_SPL_IMPLEMENTS(RecursiveIteratorIterator, OuterIterator);
3757
3758	REGISTER_SPL_SUB_CLASS_EX(InfiniteIterator, IteratorIterator, spl_dual_it_new, spl_funcs_InfiniteIterator);
3759#if HAVE_PCRE || HAVE_BUNDLED_PCRE
3760	REGISTER_SPL_SUB_CLASS_EX(RegexIterator, FilterIterator, spl_dual_it_new, spl_funcs_RegexIterator);
3761	REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "USE_KEY",     REGIT_USE_KEY);
3762	REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "INVERT_MATCH",REGIT_INVERTED);
3763	REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "MATCH",       REGIT_MODE_MATCH);
3764	REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "GET_MATCH",   REGIT_MODE_GET_MATCH);
3765	REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "ALL_MATCHES", REGIT_MODE_ALL_MATCHES);
3766	REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "SPLIT",       REGIT_MODE_SPLIT);
3767	REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "REPLACE",     REGIT_MODE_REPLACE);
3768	REGISTER_SPL_PROPERTY(RegexIterator, "replacement", 0);
3769	REGISTER_SPL_SUB_CLASS_EX(RecursiveRegexIterator, RegexIterator, spl_dual_it_new, spl_funcs_RecursiveRegexIterator);
3770	REGISTER_SPL_IMPLEMENTS(RecursiveRegexIterator, RecursiveIterator);
3771#else
3772	spl_ce_RegexIterator = NULL;
3773	spl_ce_RecursiveRegexIterator = NULL;
3774#endif
3775
3776	REGISTER_SPL_STD_CLASS_EX(EmptyIterator, NULL, spl_funcs_EmptyIterator);
3777	REGISTER_SPL_ITERATOR(EmptyIterator);
3778
3779	REGISTER_SPL_SUB_CLASS_EX(RecursiveTreeIterator, RecursiveIteratorIterator, spl_RecursiveTreeIterator_new, spl_funcs_RecursiveTreeIterator);
3780	REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "BYPASS_CURRENT",      RTIT_BYPASS_CURRENT);
3781	REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "BYPASS_KEY",          RTIT_BYPASS_KEY);
3782	REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_LEFT",         0);
3783	REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_MID_HAS_NEXT", 1);
3784	REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_MID_LAST",     2);
3785	REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_END_HAS_NEXT", 3);
3786	REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_END_LAST",     4);
3787	REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_RIGHT",        5);
3788
3789	return SUCCESS;
3790}
3791/* }}} */
3792
3793/*
3794 * Local variables:
3795 * tab-width: 4
3796 * c-basic-offset: 4
3797 * End:
3798 * vim600: fdm=marker
3799 * vim: noet sw=4 ts=4
3800 */
3801