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