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