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