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