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