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