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