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