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