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