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