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