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    }
1017    if (Z_TYPE_P(return_value) == IS_ARRAY) {
1018        zval_dtor(return_value);
1019        ZVAL_STRINGL(return_value, "Array", sizeof("Array")-1, 1);
1020    } else {
1021        convert_to_string(return_value);
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    spl_recursive_tree_iterator_get_prefix(object, &prefix TSRMLS_CC);
1123    spl_recursive_tree_iterator_get_entry(object, &entry TSRMLS_CC);
1124    spl_recursive_tree_iterator_get_postfix(object, &postfix TSRMLS_CC);
1125
1126    str_len = Z_STRLEN(prefix) + Z_STRLEN(entry) + Z_STRLEN(postfix);
1127    str = (char *) emalloc(str_len + 1U);
1128    ptr = str;
1129
1130    memcpy(ptr, Z_STRVAL(prefix), Z_STRLEN(prefix));
1131    ptr += Z_STRLEN(prefix);
1132    memcpy(ptr, Z_STRVAL(entry), Z_STRLEN(entry));
1133    ptr += Z_STRLEN(entry);
1134    memcpy(ptr, Z_STRVAL(postfix), Z_STRLEN(postfix));
1135    ptr += Z_STRLEN(postfix);
1136    *ptr = 0;
1137
1138    zval_dtor(&prefix);
1139    zval_dtor(&entry);
1140    zval_dtor(&postfix);
1141
1142    RETURN_STRINGL(str, str_len, 0);
1143} /* }}} */
1144
1145/* {{{ proto mixed RecursiveTreeIterator::key()
1146   Returns the current key prefixed and postfixed */
1147SPL_METHOD(RecursiveTreeIterator, key)
1148{
1149    spl_recursive_it_object   *object = (spl_recursive_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1150    zend_object_iterator      *iterator = object->iterators[object->level].iterator;
1151    zval                       prefix, key, postfix, key_copy;
1152    char                      *str, *ptr;
1153    size_t                     str_len;
1154
1155    if (zend_parse_parameters_none() == FAILURE) {
1156        return;
1157    }
1158
1159    if (iterator->funcs->get_current_key) {
1160        iterator->funcs->get_current_key(iterator, &key TSRMLS_CC);
1161    } else {
1162        ZVAL_NULL(&key);
1163    }
1164
1165    if (object->flags & RTIT_BYPASS_KEY) {
1166        zval *key_ptr = &key;
1167        RETVAL_ZVAL(key_ptr, 1, 0);
1168        zval_dtor(&key);
1169        return;
1170    }
1171
1172    if (Z_TYPE(key) != IS_STRING) {
1173        int use_copy;
1174        zend_make_printable_zval(&key, &key_copy, &use_copy);
1175        if (use_copy) {
1176            key = key_copy;
1177        }
1178    }
1179
1180    spl_recursive_tree_iterator_get_prefix(object, &prefix TSRMLS_CC);
1181    spl_recursive_tree_iterator_get_postfix(object, &postfix TSRMLS_CC);
1182
1183    str_len = Z_STRLEN(prefix) + Z_STRLEN(key) + Z_STRLEN(postfix);
1184    str = (char *) emalloc(str_len + 1U);
1185    ptr = str;
1186
1187    memcpy(ptr, Z_STRVAL(prefix), Z_STRLEN(prefix));
1188    ptr += Z_STRLEN(prefix);
1189    memcpy(ptr, Z_STRVAL(key), Z_STRLEN(key));
1190    ptr += Z_STRLEN(key);
1191    memcpy(ptr, Z_STRVAL(postfix), Z_STRLEN(postfix));
1192    ptr += Z_STRLEN(postfix);
1193    *ptr = 0;
1194
1195    zval_dtor(&prefix);
1196    zval_dtor(&key);
1197    zval_dtor(&postfix);
1198
1199    RETVAL_STRINGL(str, str_len, 0);
1200} /* }}} */
1201
1202ZEND_BEGIN_ARG_INFO_EX(arginfo_recursive_tree_it___construct, 0, 0, 1)
1203    ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
1204    ZEND_ARG_INFO(0, flags)
1205    ZEND_ARG_INFO(0, caching_it_flags)
1206    ZEND_ARG_INFO(0, mode)
1207ZEND_END_ARG_INFO();
1208
1209ZEND_BEGIN_ARG_INFO_EX(arginfo_recursive_tree_it_setPrefixPart, 0, 0, 2)
1210    ZEND_ARG_INFO(0, part)
1211    ZEND_ARG_INFO(0, value)
1212ZEND_END_ARG_INFO();
1213
1214static const zend_function_entry spl_funcs_RecursiveTreeIterator[] = {
1215    SPL_ME(RecursiveTreeIterator,     __construct,       arginfo_recursive_tree_it___construct,   ZEND_ACC_PUBLIC)
1216    SPL_ME(RecursiveIteratorIterator, rewind,            arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1217    SPL_ME(RecursiveIteratorIterator, valid,             arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1218    SPL_ME(RecursiveTreeIterator,     key,               arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1219    SPL_ME(RecursiveTreeIterator,     current,           arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1220    SPL_ME(RecursiveIteratorIterator, next,              arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1221    SPL_ME(RecursiveIteratorIterator, beginIteration,    arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1222    SPL_ME(RecursiveIteratorIterator, endIteration,      arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1223    SPL_ME(RecursiveIteratorIterator, callHasChildren,   arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1224    SPL_ME(RecursiveIteratorIterator, callGetChildren,   arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1225    SPL_ME(RecursiveIteratorIterator, beginChildren,     arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1226    SPL_ME(RecursiveIteratorIterator, endChildren,       arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1227    SPL_ME(RecursiveIteratorIterator, nextElement,       arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1228    SPL_ME(RecursiveTreeIterator,     getPrefix,         arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1229    SPL_ME(RecursiveTreeIterator,     setPrefixPart,     arginfo_recursive_tree_it_setPrefixPart, ZEND_ACC_PUBLIC)
1230    SPL_ME(RecursiveTreeIterator,     getEntry,          arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1231    SPL_ME(RecursiveTreeIterator,     getPostfix,        arginfo_recursive_it_void,               ZEND_ACC_PUBLIC)
1232    PHP_FE_END
1233};
1234
1235#if MBO_0
1236static int spl_dual_it_gets_implemented(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
1237{
1238    class_type->iterator_funcs.zf_valid = NULL;
1239    class_type->iterator_funcs.zf_current = NULL;
1240    class_type->iterator_funcs.zf_key = NULL;
1241    class_type->iterator_funcs.zf_next = NULL;
1242    class_type->iterator_funcs.zf_rewind = NULL;
1243    if (!class_type->iterator_funcs.funcs) {
1244        class_type->iterator_funcs.funcs = &zend_interface_iterator_funcs_iterator;
1245    }
1246
1247    return SUCCESS;
1248}
1249#endif
1250
1251static union _zend_function *spl_dual_it_get_method(zval **object_ptr, char *method, int method_len, const zend_literal *key TSRMLS_DC)
1252{
1253    union _zend_function *function_handler;
1254    spl_dual_it_object   *intern;
1255
1256    intern = (spl_dual_it_object*)zend_object_store_get_object(*object_ptr TSRMLS_CC);
1257
1258    function_handler = std_object_handlers.get_method(object_ptr, method, method_len, key TSRMLS_CC);
1259    if (!function_handler && intern->inner.ce) {
1260        if (zend_hash_find(&intern->inner.ce->function_table, method, method_len+1, (void **) &function_handler) == FAILURE) {
1261            if (Z_OBJ_HT_P(intern->inner.zobject)->get_method) {
1262                *object_ptr = intern->inner.zobject;
1263                function_handler = Z_OBJ_HT_P(*object_ptr)->get_method(object_ptr, method, method_len, key TSRMLS_CC);
1264            }
1265        } else {
1266            *object_ptr = intern->inner.zobject;
1267        }
1268    }
1269    return function_handler;
1270}
1271
1272#if MBO_0
1273int spl_dual_it_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
1274{
1275    zval ***func_params, func;
1276    zval *retval_ptr;
1277    int arg_count;
1278    int current = 0;
1279    int success;
1280    void **p;
1281    spl_dual_it_object   *intern;
1282
1283    intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1284
1285    ZVAL_STRING(&func, method, 0);
1286    if (!zend_is_callable(&func, 0, &method TSRMLS_CC)) {
1287        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Method %s::%s() does not exist", intern->inner.ce->name, method);
1288        return FAILURE;
1289    }
1290
1291    p = EG(argument_stack).top_element-2;
1292    arg_count = (ulong) *p;
1293
1294    func_params = safe_emalloc(sizeof(zval **), arg_count, 0);
1295
1296    current = 0;
1297    while (arg_count-- > 0) {
1298        func_params[current] = (zval **) p - (arg_count-current);
1299        current++;
1300    }
1301    arg_count = current; /* restore */
1302
1303    if (call_user_function_ex(EG(function_table), NULL, &func, &retval_ptr, arg_count, func_params, 0, NULL TSRMLS_CC) == SUCCESS && retval_ptr) {
1304        RETURN_ZVAL(retval_ptr, 0, 1);
1305
1306        success = SUCCESS;
1307    } else {
1308        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Unable to call %s::%s()", intern->inner.ce->name, method);
1309        success = FAILURE;
1310    }
1311
1312    efree(func_params);
1313    return success;
1314}
1315#endif
1316
1317#define SPL_CHECK_CTOR(intern, classname) \
1318    if (intern->dit_type == DIT_Unknown) { \
1319        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Classes derived from %s must call %s::__construct()", \
1320                (spl_ce_##classname)->name, (spl_ce_##classname)->name); \
1321        return; \
1322    }
1323
1324#define APPENDIT_CHECK_CTOR(intern) SPL_CHECK_CTOR(intern, AppendIterator)
1325
1326static inline int spl_dual_it_fetch(spl_dual_it_object *intern, int check_more TSRMLS_DC);
1327
1328static inline int spl_cit_check_flags(int flags)
1329{
1330    int cnt = 0;
1331
1332    cnt += (flags & CIT_CALL_TOSTRING) ? 1 : 0;
1333    cnt += (flags & CIT_TOSTRING_USE_KEY) ? 1 : 0;
1334    cnt += (flags & CIT_TOSTRING_USE_CURRENT) ? 1 : 0;
1335    cnt += (flags & CIT_TOSTRING_USE_INNER) ? 1 : 0;
1336
1337    return cnt <= 1 ? SUCCESS : FAILURE;
1338}
1339
1340static 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)
1341{
1342    zval                 *zobject, *retval;
1343    spl_dual_it_object   *intern;
1344    zend_class_entry     *ce = NULL;
1345    int                   inc_refcount = 1;
1346    zend_error_handling   error_handling;
1347
1348    intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1349
1350    if (intern->dit_type != DIT_Unknown) {
1351        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s::getIterator() must be called exactly once per instance", ce_base->name);
1352        return NULL;
1353    }
1354
1355    zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling TSRMLS_CC);
1356
1357    intern->dit_type = dit_type;
1358    switch (dit_type) {
1359        case DIT_LimitIterator: {
1360            intern->u.limit.offset = 0; /* start at beginning */
1361            intern->u.limit.count = -1; /* get all */
1362            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|ll", &zobject, ce_inner, &intern->u.limit.offset, &intern->u.limit.count) == FAILURE) {
1363                zend_restore_error_handling(&error_handling TSRMLS_CC);
1364                return NULL;
1365            }
1366            if (intern->u.limit.offset < 0) {
1367                zend_throw_exception(spl_ce_OutOfRangeException, "Parameter offset must be >= 0", 0 TSRMLS_CC);
1368                zend_restore_error_handling(&error_handling TSRMLS_CC);
1369                return NULL;
1370            }
1371            if (intern->u.limit.count < 0 && intern->u.limit.count != -1) {
1372                zend_throw_exception(spl_ce_OutOfRangeException, "Parameter count must either be -1 or a value greater than or equal 0", 0 TSRMLS_CC);
1373                zend_restore_error_handling(&error_handling TSRMLS_CC);
1374                return NULL;
1375            }
1376            break;
1377        }
1378        case DIT_CachingIterator:
1379        case DIT_RecursiveCachingIterator: {
1380            long flags = CIT_CALL_TOSTRING;
1381            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|l", &zobject, ce_inner, &flags) == FAILURE) {
1382                zend_restore_error_handling(&error_handling TSRMLS_CC);
1383                return NULL;
1384            }
1385            if (spl_cit_check_flags(flags) != SUCCESS) {
1386                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);
1387                zend_restore_error_handling(&error_handling TSRMLS_CC);
1388                return NULL;
1389            }
1390            intern->u.caching.flags |= flags & CIT_PUBLIC;
1391            MAKE_STD_ZVAL(intern->u.caching.zcache);
1392            array_init(intern->u.caching.zcache);
1393            break;
1394        }
1395        case DIT_IteratorIterator: {
1396            zend_class_entry **pce_cast;
1397            char * class_name = NULL;
1398            int class_name_len = 0;
1399
1400            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|s", &zobject, ce_inner, &class_name, &class_name_len) == FAILURE) {
1401                zend_restore_error_handling(&error_handling TSRMLS_CC);
1402                return NULL;
1403            }
1404            ce = Z_OBJCE_P(zobject);
1405            if (!instanceof_function(ce, zend_ce_iterator TSRMLS_CC)) {
1406                if (ZEND_NUM_ARGS() > 1) {
1407                    if (zend_lookup_class(class_name, class_name_len, &pce_cast TSRMLS_CC) == FAILURE
1408                    || !instanceof_function(ce, *pce_cast TSRMLS_CC)
1409                    || !(*pce_cast)->get_iterator
1410                    ) {
1411                        zend_throw_exception(spl_ce_LogicException, "Class to downcast to not found or not base class or does not implement Traversable", 0 TSRMLS_CC);
1412                        zend_restore_error_handling(&error_handling TSRMLS_CC);
1413                        return NULL;
1414                    }
1415                    ce = *pce_cast;
1416                }
1417                if (instanceof_function(ce, zend_ce_aggregate TSRMLS_CC)) {
1418                    zend_call_method_with_0_params(&zobject, ce, &ce->iterator_funcs.zf_new_iterator, "getiterator", &retval);
1419                    if (EG(exception)) {
1420                        if (retval) {
1421                            zval_ptr_dtor(&retval);
1422                        }
1423                        zend_restore_error_handling(&error_handling TSRMLS_CC);
1424                        return NULL;
1425                    }
1426                    if (!retval || Z_TYPE_P(retval) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(retval), zend_ce_traversable TSRMLS_CC)) {
1427                        zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "%s::getIterator() must return an object that implements Traversable", ce->name);
1428                        zend_restore_error_handling(&error_handling TSRMLS_CC);
1429                        return NULL;
1430                    }
1431                    zobject = retval;
1432                    ce = Z_OBJCE_P(zobject);
1433                    inc_refcount = 0;
1434                }
1435            }
1436            break;
1437        }
1438        case DIT_AppendIterator:
1439            spl_instantiate(spl_ce_ArrayIterator, &intern->u.append.zarrayit, 1 TSRMLS_CC);
1440            zend_call_method_with_0_params(&intern->u.append.zarrayit, spl_ce_ArrayIterator, &spl_ce_ArrayIterator->constructor, "__construct", NULL);
1441            intern->u.append.iterator = spl_ce_ArrayIterator->get_iterator(spl_ce_ArrayIterator, intern->u.append.zarrayit, 0 TSRMLS_CC);
1442            zend_restore_error_handling(&error_handling TSRMLS_CC);
1443            return intern;
1444#if HAVE_PCRE || HAVE_BUNDLED_PCRE
1445        case DIT_RegexIterator:
1446        case DIT_RecursiveRegexIterator: {
1447            char *regex;
1448            int regex_len;
1449            long mode = REGIT_MODE_MATCH;
1450
1451            intern->u.regex.use_flags = ZEND_NUM_ARGS() >= 5;
1452            intern->u.regex.flags = 0;
1453            intern->u.regex.preg_flags = 0;
1454            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) {
1455                zend_restore_error_handling(&error_handling TSRMLS_CC);
1456                return NULL;
1457            }
1458            if (mode < 0 || mode >= REGIT_MODE_MAX) {
1459                zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Illegal mode %ld", mode);
1460                zend_restore_error_handling(&error_handling TSRMLS_CC);
1461                return NULL;
1462            }
1463            intern->u.regex.mode = mode;
1464            intern->u.regex.regex = estrndup(regex, regex_len);
1465            intern->u.regex.regex_len = regex_len;
1466            intern->u.regex.pce = pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC);
1467            if (intern->u.regex.pce == NULL) {
1468                /* pcre_get_compiled_regex_cache has already sent error */
1469                zend_restore_error_handling(&error_handling TSRMLS_CC);
1470                return NULL;
1471            }
1472            intern->u.regex.pce->refcount++;
1473            break;
1474        }
1475#endif
1476        case DIT_CallbackFilterIterator:
1477        case DIT_RecursiveCallbackFilterIterator: {
1478            _spl_cbfilter_it_intern *cfi = emalloc(sizeof(*cfi));
1479            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Of", &zobject, ce_inner, &cfi->fci, &cfi->fcc) == FAILURE) {
1480                zend_restore_error_handling(&error_handling TSRMLS_CC);
1481                efree(cfi);
1482                return NULL;
1483            }
1484            if (cfi->fci.function_name) {
1485                Z_ADDREF_P(cfi->fci.function_name);
1486            }
1487            if (cfi->fci.object_ptr) {
1488                Z_ADDREF_P(cfi->fci.object_ptr);
1489            }
1490            intern->u.cbfilter = cfi;
1491            break;
1492        }
1493        default:
1494            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zobject, ce_inner) == FAILURE) {
1495                zend_restore_error_handling(&error_handling TSRMLS_CC);
1496                return NULL;
1497            }
1498            break;
1499    }
1500
1501    zend_restore_error_handling(&error_handling TSRMLS_CC);
1502
1503    if (inc_refcount) {
1504        Z_ADDREF_P(zobject);
1505    }
1506    intern->inner.zobject = zobject;
1507    intern->inner.ce = dit_type == DIT_IteratorIterator ? ce : Z_OBJCE_P(zobject);
1508    intern->inner.object = zend_object_store_get_object(zobject TSRMLS_CC);
1509    intern->inner.iterator = intern->inner.ce->get_iterator(intern->inner.ce, zobject, 0 TSRMLS_CC);
1510
1511    return intern;
1512}
1513
1514/* {{{ proto void FilterIterator::__construct(Iterator it)
1515   Create an Iterator from another iterator */
1516SPL_METHOD(FilterIterator, __construct)
1517{
1518    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_FilterIterator, zend_ce_iterator, DIT_FilterIterator);
1519} /* }}} */
1520
1521/* {{{ proto void CallbackFilterIterator::__construct(Iterator it, callback)
1522   Create an Iterator from another iterator */
1523SPL_METHOD(CallbackFilterIterator, __construct)
1524{
1525    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_CallbackFilterIterator, zend_ce_iterator, DIT_CallbackFilterIterator);
1526} /* }}} */
1527
1528/* {{{ proto Iterator FilterIterator::getInnerIterator()
1529       proto Iterator CachingIterator::getInnerIterator()
1530       proto Iterator LimitIterator::getInnerIterator()
1531       proto Iterator ParentIterator::getInnerIterator()
1532   Get the inner iterator */
1533SPL_METHOD(dual_it, getInnerIterator)
1534{
1535    spl_dual_it_object   *intern;
1536
1537    if (zend_parse_parameters_none() == FAILURE) {
1538        return;
1539    }
1540
1541    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1542
1543    if (intern->inner.zobject) {
1544        RETVAL_ZVAL(intern->inner.zobject, 1, 0);
1545    } else {
1546        RETURN_NULL();
1547    }
1548} /* }}} */
1549
1550static inline void spl_dual_it_require(spl_dual_it_object *intern TSRMLS_DC)
1551{
1552    if (!intern->inner.iterator) {
1553        php_error_docref(NULL TSRMLS_CC, E_ERROR, "The inner constructor wasn't initialized with an iterator instance");
1554    }
1555}
1556
1557static inline void spl_dual_it_free(spl_dual_it_object *intern TSRMLS_DC)
1558{
1559    if (intern->inner.iterator && intern->inner.iterator->funcs->invalidate_current) {
1560        intern->inner.iterator->funcs->invalidate_current(intern->inner.iterator TSRMLS_CC);
1561    }
1562    if (intern->current.data) {
1563        zval_ptr_dtor(&intern->current.data);
1564        intern->current.data = NULL;
1565    }
1566    if (intern->current.key) {
1567        zval_ptr_dtor(&intern->current.key);
1568        intern->current.key = NULL;
1569    }
1570    if (intern->dit_type == DIT_CachingIterator || intern->dit_type == DIT_RecursiveCachingIterator) {
1571        if (intern->u.caching.zstr) {
1572            zval_ptr_dtor(&intern->u.caching.zstr);
1573            intern->u.caching.zstr = NULL;
1574        }
1575        if (intern->u.caching.zchildren) {
1576            zval_ptr_dtor(&intern->u.caching.zchildren);
1577            intern->u.caching.zchildren = NULL;
1578        }
1579    }
1580}
1581
1582static inline void spl_dual_it_rewind(spl_dual_it_object *intern TSRMLS_DC)
1583{
1584    spl_dual_it_free(intern TSRMLS_CC);
1585    intern->current.pos = 0;
1586    if (intern->inner.iterator->funcs->rewind) {
1587        intern->inner.iterator->funcs->rewind(intern->inner.iterator TSRMLS_CC);
1588    }
1589}
1590
1591static inline int spl_dual_it_valid(spl_dual_it_object *intern TSRMLS_DC)
1592{
1593    if (!intern->inner.iterator) {
1594        return FAILURE;
1595    }
1596    /* FAILURE / SUCCESS */
1597    return intern->inner.iterator->funcs->valid(intern->inner.iterator TSRMLS_CC);
1598}
1599
1600static inline int spl_dual_it_fetch(spl_dual_it_object *intern, int check_more TSRMLS_DC)
1601{
1602    zval **data;
1603
1604    spl_dual_it_free(intern TSRMLS_CC);
1605    if (!check_more || spl_dual_it_valid(intern TSRMLS_CC) == SUCCESS) {
1606        intern->inner.iterator->funcs->get_current_data(intern->inner.iterator, &data TSRMLS_CC);
1607        if (data && *data) {
1608            intern->current.data = *data;
1609            Z_ADDREF_P(intern->current.data);
1610        }
1611
1612        MAKE_STD_ZVAL(intern->current.key);
1613        if (intern->inner.iterator->funcs->get_current_key) {
1614            intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, intern->current.key TSRMLS_CC);
1615            if (EG(exception)) {
1616                zval_ptr_dtor(&intern->current.key);
1617                intern->current.key = NULL;
1618            }
1619        } else {
1620            ZVAL_LONG(intern->current.key, intern->current.pos);
1621        }
1622        return EG(exception) ? FAILURE : SUCCESS;
1623    }
1624    return FAILURE;
1625}
1626
1627static inline void spl_dual_it_next(spl_dual_it_object *intern, int do_free TSRMLS_DC)
1628{
1629    if (do_free) {
1630        spl_dual_it_free(intern TSRMLS_CC);
1631    } else {
1632        spl_dual_it_require(intern TSRMLS_CC);
1633    }
1634    intern->inner.iterator->funcs->move_forward(intern->inner.iterator TSRMLS_CC);
1635    intern->current.pos++;
1636}
1637
1638/* {{{ proto void ParentIterator::rewind()
1639       proto void IteratorIterator::rewind()
1640   Rewind the iterator
1641   */
1642SPL_METHOD(dual_it, rewind)
1643{
1644    spl_dual_it_object   *intern;
1645
1646    if (zend_parse_parameters_none() == FAILURE) {
1647        return;
1648    }
1649
1650    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1651
1652    spl_dual_it_rewind(intern TSRMLS_CC);
1653    spl_dual_it_fetch(intern, 1 TSRMLS_CC);
1654} /* }}} */
1655
1656/* {{{ proto bool FilterIterator::valid()
1657       proto bool ParentIterator::valid()
1658       proto bool IteratorIterator::valid()
1659       proto bool NoRewindIterator::valid()
1660   Check whether the current element is valid */
1661SPL_METHOD(dual_it, valid)
1662{
1663    spl_dual_it_object   *intern;
1664
1665    if (zend_parse_parameters_none() == FAILURE) {
1666        return;
1667    }
1668
1669    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1670
1671    RETURN_BOOL(intern->current.data);
1672} /* }}} */
1673
1674/* {{{ proto mixed FilterIterator::key()
1675       proto mixed CachingIterator::key()
1676       proto mixed LimitIterator::key()
1677       proto mixed ParentIterator::key()
1678       proto mixed IteratorIterator::key()
1679       proto mixed NoRewindIterator::key()
1680       proto mixed AppendIterator::key()
1681   Get the current key */
1682SPL_METHOD(dual_it, key)
1683{
1684    spl_dual_it_object   *intern;
1685
1686    if (zend_parse_parameters_none() == FAILURE) {
1687        return;
1688    }
1689
1690    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1691
1692    if (intern->current.key) {
1693        RETURN_ZVAL(intern->current.key, 1, 0);
1694    }
1695    RETURN_NULL();
1696} /* }}} */
1697
1698/* {{{ proto mixed FilterIterator::current()
1699       proto mixed CachingIterator::current()
1700       proto mixed LimitIterator::current()
1701       proto mixed ParentIterator::current()
1702       proto mixed IteratorIterator::current()
1703       proto mixed NoRewindIterator::current()
1704       proto mixed AppendIterator::current()
1705   Get the current element value */
1706SPL_METHOD(dual_it, current)
1707{
1708    spl_dual_it_object   *intern;
1709
1710    if (zend_parse_parameters_none() == FAILURE) {
1711        return;
1712    }
1713
1714    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1715
1716    if (intern->current.data) {
1717        RETVAL_ZVAL(intern->current.data, 1, 0);
1718    } else {
1719        RETURN_NULL();
1720    }
1721} /* }}} */
1722
1723/* {{{ proto void ParentIterator::next()
1724       proto void IteratorIterator::next()
1725       proto void NoRewindIterator::next()
1726   Move the iterator forward */
1727SPL_METHOD(dual_it, next)
1728{
1729    spl_dual_it_object   *intern;
1730
1731    if (zend_parse_parameters_none() == FAILURE) {
1732        return;
1733    }
1734
1735    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1736
1737    spl_dual_it_next(intern, 1 TSRMLS_CC);
1738    spl_dual_it_fetch(intern, 1 TSRMLS_CC);
1739} /* }}} */
1740
1741static inline void spl_filter_it_fetch(zval *zthis, spl_dual_it_object *intern TSRMLS_DC)
1742{
1743    zval *retval;
1744
1745    while (spl_dual_it_fetch(intern, 1 TSRMLS_CC) == SUCCESS) {
1746        zend_call_method_with_0_params(&zthis, intern->std.ce, NULL, "accept", &retval);
1747        if (retval) {
1748            if (zend_is_true(retval)) {
1749                zval_ptr_dtor(&retval);
1750                return;
1751            }
1752            zval_ptr_dtor(&retval);
1753        }
1754        if (EG(exception)) {
1755            return;
1756        }
1757        intern->inner.iterator->funcs->move_forward(intern->inner.iterator TSRMLS_CC);
1758    }
1759    spl_dual_it_free(intern TSRMLS_CC);
1760}
1761
1762static inline void spl_filter_it_rewind(zval *zthis, spl_dual_it_object *intern TSRMLS_DC)
1763{
1764    spl_dual_it_rewind(intern TSRMLS_CC);
1765    spl_filter_it_fetch(zthis, intern TSRMLS_CC);
1766}
1767
1768static inline void spl_filter_it_next(zval *zthis, spl_dual_it_object *intern TSRMLS_DC)
1769{
1770    spl_dual_it_next(intern, 1 TSRMLS_CC);
1771    spl_filter_it_fetch(zthis, intern TSRMLS_CC);
1772}
1773
1774/* {{{ proto void FilterIterator::rewind()
1775   Rewind the iterator */
1776SPL_METHOD(FilterIterator, rewind)
1777{
1778    spl_dual_it_object   *intern;
1779
1780    if (zend_parse_parameters_none() == FAILURE) {
1781        return;
1782    }
1783
1784    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1785    spl_filter_it_rewind(getThis(), intern TSRMLS_CC);
1786} /* }}} */
1787
1788/* {{{ proto void FilterIterator::next()
1789   Move the iterator forward */
1790SPL_METHOD(FilterIterator, next)
1791{
1792    spl_dual_it_object   *intern;
1793
1794    if (zend_parse_parameters_none() == FAILURE) {
1795        return;
1796    }
1797
1798    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1799    spl_filter_it_next(getThis(), intern TSRMLS_CC);
1800} /* }}} */
1801
1802/* {{{ proto void RecursiveCallbackFilterIterator::__construct(RecursiveIterator it, callback)
1803   Create a RecursiveCallbackFilterIterator from a RecursiveIterator */
1804SPL_METHOD(RecursiveCallbackFilterIterator, __construct)
1805{
1806    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveCallbackFilterIterator, spl_ce_RecursiveIterator, DIT_RecursiveCallbackFilterIterator);
1807} /* }}} */
1808
1809
1810/* {{{ proto void RecursiveFilterIterator::__construct(RecursiveIterator it)
1811   Create a RecursiveFilterIterator from a RecursiveIterator */
1812SPL_METHOD(RecursiveFilterIterator, __construct)
1813{
1814    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveFilterIterator, spl_ce_RecursiveIterator, DIT_RecursiveFilterIterator);
1815} /* }}} */
1816
1817/* {{{ proto bool RecursiveFilterIterator::hasChildren()
1818   Check whether the inner iterator's current element has children */
1819SPL_METHOD(RecursiveFilterIterator, hasChildren)
1820{
1821    spl_dual_it_object   *intern;
1822    zval                 *retval;
1823
1824    if (zend_parse_parameters_none() == FAILURE) {
1825        return;
1826    }
1827
1828    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1829
1830    zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "haschildren", &retval);
1831    if (retval) {
1832        RETURN_ZVAL(retval, 0, 1);
1833    } else {
1834        RETURN_FALSE;
1835    }
1836} /* }}} */
1837
1838/* {{{ proto RecursiveFilterIterator RecursiveFilterIterator::getChildren()
1839   Return the inner iterator's children contained in a RecursiveFilterIterator */
1840SPL_METHOD(RecursiveFilterIterator, getChildren)
1841{
1842    spl_dual_it_object   *intern;
1843    zval                 *retval;
1844
1845    if (zend_parse_parameters_none() == FAILURE) {
1846        return;
1847    }
1848
1849    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1850
1851    zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval);
1852    if (!EG(exception) && retval) {
1853        spl_instantiate_arg_ex1(Z_OBJCE_P(getThis()), &return_value, 0, retval TSRMLS_CC);
1854    }
1855    if (retval) {
1856        zval_ptr_dtor(&retval);
1857    }
1858} /* }}} */
1859
1860/* {{{ proto RecursiveCallbackFilterIterator RecursiveCallbackFilterIterator::getChildren()
1861   Return the inner iterator's children contained in a RecursiveCallbackFilterIterator */
1862SPL_METHOD(RecursiveCallbackFilterIterator, getChildren)
1863{
1864    spl_dual_it_object   *intern;
1865    zval                 *retval;
1866
1867    if (zend_parse_parameters_none() == FAILURE) {
1868        return;
1869    }
1870
1871    intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1872
1873    zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval);
1874    if (!EG(exception) && retval) {
1875        spl_instantiate_arg_ex2(Z_OBJCE_P(getThis()), &return_value, 0, retval, intern->u.cbfilter->fci.function_name TSRMLS_CC);
1876    }
1877    if (retval) {
1878        zval_ptr_dtor(&retval);
1879    }
1880} /* }}} */
1881/* {{{ proto void ParentIterator::__construct(RecursiveIterator it)
1882   Create a ParentIterator from a RecursiveIterator */
1883SPL_METHOD(ParentIterator, __construct)
1884{
1885    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_ParentIterator, spl_ce_RecursiveIterator, DIT_ParentIterator);
1886} /* }}} */
1887
1888#if HAVE_PCRE || HAVE_BUNDLED_PCRE
1889/* {{{ proto void RegexIterator::__construct(Iterator it, string regex [, int mode [, int flags [, int preg_flags]]])
1890   Create an RegexIterator from another iterator and a regular expression */
1891SPL_METHOD(RegexIterator, __construct)
1892{
1893    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RegexIterator, zend_ce_iterator, DIT_RegexIterator);
1894} /* }}} */
1895
1896/* {{{ proto bool CallbackFilterIterator::accept()
1897   Calls the callback with the current value, the current key and the inner iterator as arguments */
1898SPL_METHOD(CallbackFilterIterator, accept)
1899{
1900    spl_dual_it_object     *intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1901    zend_fcall_info        *fci = &intern->u.cbfilter->fci;
1902    zend_fcall_info_cache  *fcc = &intern->u.cbfilter->fcc;
1903    zval                  **params[3];
1904    zval                   *result;
1905
1906    if (zend_parse_parameters_none() == FAILURE) {
1907        return;
1908    }
1909
1910    if (intern->current.data == NULL || intern->current.key == NULL) {
1911        RETURN_FALSE;
1912    }
1913
1914    params[0] = &intern->current.data;
1915    params[1] = &intern->current.key;
1916    params[2] = &intern->inner.zobject;
1917
1918    fci->retval_ptr_ptr = &result;
1919    fci->param_count = 3;
1920    fci->params = params;
1921    fci->no_separation = 0;
1922
1923    if (zend_call_function(fci, fcc TSRMLS_CC) != SUCCESS || !result) {
1924        RETURN_FALSE;
1925    }
1926    if (EG(exception)) {
1927        return;
1928    }
1929
1930    RETURN_ZVAL(result, 1, 1);
1931}
1932/* }}} */
1933
1934/* {{{ proto bool RegexIterator::accept()
1935   Match (string)current() against regular expression */
1936SPL_METHOD(RegexIterator, accept)
1937{
1938    spl_dual_it_object *intern;
1939    char       *subject, *result;
1940    int        subject_len, use_copy, count = 0, result_len;
1941    zval       *subject_ptr, subject_copy, zcount, *replacement, tmp_replacement;
1942
1943    if (zend_parse_parameters_none() == FAILURE) {
1944        return;
1945    }
1946
1947    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
1948
1949    if (intern->current.data == NULL) {
1950        RETURN_FALSE;
1951    }
1952
1953    if (intern->u.regex.flags & REGIT_USE_KEY) {
1954        subject_ptr = intern->current.key;
1955    } else {
1956        subject_ptr = intern->current.data;
1957    }
1958
1959    zend_make_printable_zval(subject_ptr, &subject_copy, &use_copy);
1960    if (use_copy) {
1961        subject = Z_STRVAL(subject_copy);
1962        subject_len = Z_STRLEN(subject_copy);
1963    } else {
1964        subject = Z_STRVAL_P(subject_ptr);
1965        subject_len = Z_STRLEN_P(subject_ptr);
1966    }
1967
1968    switch (intern->u.regex.mode)
1969    {
1970    case REGIT_MODE_MAX: /* won't happen but makes compiler happy */
1971    case REGIT_MODE_MATCH:
1972        count = pcre_exec(intern->u.regex.pce->re, intern->u.regex.pce->extra, subject, subject_len, 0, 0, NULL, 0);
1973        RETVAL_BOOL(count >= 0);
1974        break;
1975
1976    case REGIT_MODE_ALL_MATCHES:
1977    case REGIT_MODE_GET_MATCH:
1978        if (!use_copy) {
1979            subject = estrndup(subject, subject_len);
1980            use_copy = 1;
1981        }
1982        zval_ptr_dtor(&intern->current.data);
1983        ALLOC_INIT_ZVAL(intern->current.data);
1984        php_pcre_match_impl(intern->u.regex.pce, subject, subject_len, &zcount,
1985            intern->current.data, intern->u.regex.mode == REGIT_MODE_ALL_MATCHES, intern->u.regex.use_flags, intern->u.regex.preg_flags, 0 TSRMLS_CC);
1986        count = zend_hash_num_elements(Z_ARRVAL_P(intern->current.data));
1987        RETVAL_BOOL(count > 0);
1988        break;
1989
1990    case REGIT_MODE_SPLIT:
1991        if (!use_copy) {
1992            subject = estrndup(subject, subject_len);
1993            use_copy = 1;
1994        }
1995        zval_ptr_dtor(&intern->current.data);
1996        ALLOC_INIT_ZVAL(intern->current.data);
1997        php_pcre_split_impl(intern->u.regex.pce, subject, subject_len, intern->current.data, -1, intern->u.regex.preg_flags TSRMLS_CC);
1998        count = zend_hash_num_elements(Z_ARRVAL_P(intern->current.data));
1999        RETVAL_BOOL(count > 1);
2000        break;
2001
2002    case REGIT_MODE_REPLACE:
2003        replacement = zend_read_property(intern->std.ce, getThis(), "replacement", sizeof("replacement")-1, 1 TSRMLS_CC);
2004        if (Z_TYPE_P(replacement) != IS_STRING) {
2005            tmp_replacement = *replacement;
2006            zval_copy_ctor(&tmp_replacement);
2007            convert_to_string(&tmp_replacement);
2008            replacement = &tmp_replacement;
2009        }
2010        result = php_pcre_replace_impl(intern->u.regex.pce, subject, subject_len, replacement, 0, &result_len, -1, &count TSRMLS_CC);
2011
2012        if (intern->u.regex.flags & REGIT_USE_KEY) {
2013            zval_ptr_dtor(&intern->current.key);
2014            MAKE_STD_ZVAL(intern->current.key);
2015            ZVAL_STRINGL(intern->current.key, result, result_len, 0);
2016        } else {
2017            zval_ptr_dtor(&intern->current.data);
2018            MAKE_STD_ZVAL(intern->current.data);
2019            ZVAL_STRINGL(intern->current.data, result, result_len, 0);
2020        }
2021
2022        if (replacement == &tmp_replacement) {
2023            zval_dtor(replacement);
2024        }
2025        RETVAL_BOOL(count > 0);
2026    }
2027
2028    if (intern->u.regex.flags & REGIT_INVERTED) {
2029        RETVAL_BOOL(Z_LVAL_P(return_value));
2030    }
2031
2032    if (use_copy) {
2033        efree(subject);
2034    }
2035} /* }}} */
2036
2037/* {{{ proto string RegexIterator::getRegex()
2038   Returns current regular expression */
2039SPL_METHOD(RegexIterator, getRegex)
2040{
2041    spl_dual_it_object *intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
2042
2043    if (zend_parse_parameters_none() == FAILURE) {
2044        return;
2045    }
2046
2047    RETURN_STRINGL(intern->u.regex.regex, intern->u.regex.regex_len, 1);
2048} /* }}} */
2049
2050/* {{{ proto bool RegexIterator::getMode()
2051   Returns current operation mode */
2052SPL_METHOD(RegexIterator, getMode)
2053{
2054    spl_dual_it_object *intern;
2055
2056    if (zend_parse_parameters_none() == FAILURE) {
2057        return;
2058    }
2059
2060    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2061
2062    RETURN_LONG(intern->u.regex.mode);
2063} /* }}} */
2064
2065/* {{{ proto bool RegexIterator::setMode(int new_mode)
2066   Set new operation mode */
2067SPL_METHOD(RegexIterator, setMode)
2068{
2069    spl_dual_it_object *intern;
2070    long mode;
2071
2072    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &mode) == FAILURE) {
2073        return;
2074    }
2075
2076    if (mode < 0 || mode >= REGIT_MODE_MAX) {
2077        zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Illegal mode %ld", mode);
2078        return;/* NULL */
2079    }
2080
2081    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2082
2083    intern->u.regex.mode = mode;
2084} /* }}} */
2085
2086/* {{{ proto bool RegexIterator::getFlags()
2087   Returns current operation flags */
2088SPL_METHOD(RegexIterator, getFlags)
2089{
2090    spl_dual_it_object *intern;
2091
2092    if (zend_parse_parameters_none() == FAILURE) {
2093        return;
2094    }
2095
2096    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2097
2098    RETURN_LONG(intern->u.regex.flags);
2099} /* }}} */
2100
2101/* {{{ proto bool RegexIterator::setFlags(int new_flags)
2102   Set operation flags */
2103SPL_METHOD(RegexIterator, setFlags)
2104{
2105    spl_dual_it_object *intern;
2106    long flags;
2107
2108    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &flags) == FAILURE) {
2109        return;
2110    }
2111
2112    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2113
2114    intern->u.regex.flags = flags;
2115} /* }}} */
2116
2117/* {{{ proto bool RegexIterator::getFlags()
2118   Returns current PREG flags (if in use or NULL) */
2119SPL_METHOD(RegexIterator, getPregFlags)
2120{
2121    spl_dual_it_object *intern;
2122
2123    if (zend_parse_parameters_none() == FAILURE) {
2124        return;
2125    }
2126
2127    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2128
2129    if (intern->u.regex.use_flags) {
2130        RETURN_LONG(intern->u.regex.preg_flags);
2131    } else {
2132        return;
2133    }
2134} /* }}} */
2135
2136/* {{{ proto bool RegexIterator::setPregFlags(int new_flags)
2137   Set PREG flags */
2138SPL_METHOD(RegexIterator, setPregFlags)
2139{
2140    spl_dual_it_object *intern;
2141    long preg_flags;
2142
2143    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &preg_flags) == FAILURE) {
2144        return;
2145    }
2146
2147    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2148
2149    intern->u.regex.preg_flags = preg_flags;
2150    intern->u.regex.use_flags = 1;
2151} /* }}} */
2152
2153/* {{{ proto void RecursiveRegexIterator::__construct(RecursiveIterator it, string regex [, int mode [, int flags [, int preg_flags]]])
2154   Create an RecursiveRegexIterator from another recursive iterator and a regular expression */
2155SPL_METHOD(RecursiveRegexIterator, __construct)
2156{
2157    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveRegexIterator, spl_ce_RecursiveIterator, DIT_RecursiveRegexIterator);
2158} /* }}} */
2159
2160/* {{{ proto RecursiveRegexIterator RecursiveRegexIterator::getChildren()
2161   Return the inner iterator's children contained in a RecursiveRegexIterator */
2162SPL_METHOD(RecursiveRegexIterator, getChildren)
2163{
2164    spl_dual_it_object   *intern;
2165    zval                 *retval, *regex;
2166
2167    if (zend_parse_parameters_none() == FAILURE) {
2168        return;
2169    }
2170
2171    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2172
2173    zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval);
2174    if (!EG(exception)) {
2175        MAKE_STD_ZVAL(regex);
2176        ZVAL_STRING(regex, intern->u.regex.regex, 1);
2177        spl_instantiate_arg_ex2(Z_OBJCE_P(getThis()), &return_value, 0, retval, regex TSRMLS_CC);
2178        zval_ptr_dtor(&regex);
2179    }
2180    if (retval) {
2181        zval_ptr_dtor(&retval);
2182    }
2183} /* }}} */
2184
2185#endif
2186
2187/* {{{ spl_dual_it_dtor */
2188static void spl_dual_it_dtor(zend_object *_object, zend_object_handle handle TSRMLS_DC)
2189{
2190    spl_dual_it_object        *object = (spl_dual_it_object *)_object;
2191
2192    /* call standard dtor */
2193    zend_objects_destroy_object(_object, handle TSRMLS_CC);
2194
2195    spl_dual_it_free(object TSRMLS_CC);
2196
2197    if (object->inner.iterator) {
2198        object->inner.iterator->funcs->dtor(object->inner.iterator TSRMLS_CC);
2199    }
2200}
2201/* }}} */
2202
2203/* {{{ spl_dual_it_free_storage */
2204static void spl_dual_it_free_storage(void *_object TSRMLS_DC)
2205{
2206    spl_dual_it_object        *object = (spl_dual_it_object *)_object;
2207
2208
2209    if (object->inner.zobject) {
2210        zval_ptr_dtor(&object->inner.zobject);
2211    }
2212
2213    if (object->dit_type == DIT_AppendIterator) {
2214        object->u.append.iterator->funcs->dtor(object->u.append.iterator TSRMLS_CC);
2215        if (object->u.append.zarrayit) {
2216            zval_ptr_dtor(&object->u.append.zarrayit);
2217        }
2218    }
2219
2220    if (object->dit_type == DIT_CachingIterator || object->dit_type == DIT_RecursiveCachingIterator) {
2221        if (object->u.caching.zcache) {
2222            zval_ptr_dtor(&object->u.caching.zcache);
2223            object->u.caching.zcache = NULL;
2224        }
2225    }
2226
2227#if HAVE_PCRE || HAVE_BUNDLED_PCRE
2228    if (object->dit_type == DIT_RegexIterator || object->dit_type == DIT_RecursiveRegexIterator) {
2229        if (object->u.regex.pce) {
2230            object->u.regex.pce->refcount--;
2231        }
2232        if (object->u.regex.regex) {
2233            efree(object->u.regex.regex);
2234        }
2235    }
2236#endif
2237
2238    if (object->dit_type == DIT_CallbackFilterIterator || object->dit_type == DIT_RecursiveCallbackFilterIterator) {
2239        if (object->u.cbfilter) {
2240            if (object->u.cbfilter->fci.function_name) {
2241                zval_ptr_dtor(&object->u.cbfilter->fci.function_name);
2242            }
2243            if (object->u.cbfilter->fci.object_ptr) {
2244                zval_ptr_dtor(&object->u.cbfilter->fci.object_ptr);
2245            }
2246            efree(object->u.cbfilter);
2247        }
2248    }
2249
2250    zend_object_std_dtor(&object->std TSRMLS_CC);
2251
2252    efree(object);
2253}
2254/* }}} */
2255
2256/* {{{ spl_dual_it_new */
2257static zend_object_value spl_dual_it_new(zend_class_entry *class_type TSRMLS_DC)
2258{
2259    zend_object_value retval;
2260    spl_dual_it_object *intern;
2261
2262    intern = emalloc(sizeof(spl_dual_it_object));
2263    memset(intern, 0, sizeof(spl_dual_it_object));
2264    intern->dit_type = DIT_Unknown;
2265
2266    zend_object_std_init(&intern->std, class_type TSRMLS_CC);
2267    object_properties_init(&intern->std, class_type);
2268
2269    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);
2270    retval.handlers = &spl_handlers_dual_it;
2271    return retval;
2272}
2273/* }}} */
2274
2275ZEND_BEGIN_ARG_INFO(arginfo_filter_it___construct, 0)
2276    ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
2277ZEND_END_ARG_INFO();
2278
2279static const zend_function_entry spl_funcs_FilterIterator[] = {
2280    SPL_ME(FilterIterator,  __construct,      arginfo_filter_it___construct, ZEND_ACC_PUBLIC)
2281    SPL_ME(FilterIterator,  rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2282    SPL_ME(dual_it,         valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2283    SPL_ME(dual_it,         key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2284    SPL_ME(dual_it,         current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2285    SPL_ME(FilterIterator,  next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2286    SPL_ME(dual_it,         getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2287    SPL_ABSTRACT_ME(FilterIterator, accept,   arginfo_recursive_it_void)
2288    PHP_FE_END
2289};
2290
2291ZEND_BEGIN_ARG_INFO(arginfo_callback_filter_it___construct, 0)
2292    ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
2293    ZEND_ARG_INFO(0, callback)
2294ZEND_END_ARG_INFO();
2295
2296static const zend_function_entry spl_funcs_CallbackFilterIterator[] = {
2297    SPL_ME(CallbackFilterIterator, __construct, arginfo_callback_filter_it___construct, ZEND_ACC_PUBLIC)
2298    SPL_ME(CallbackFilterIterator, accept,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2299    PHP_FE_END
2300};
2301
2302ZEND_BEGIN_ARG_INFO(arginfo_recursive_callback_filter_it___construct, 0)
2303    ZEND_ARG_OBJ_INFO(0, iterator, RecursiveIterator, 0)
2304    ZEND_ARG_INFO(0, callback)
2305ZEND_END_ARG_INFO();
2306
2307static const zend_function_entry spl_funcs_RecursiveCallbackFilterIterator[] = {
2308    SPL_ME(RecursiveCallbackFilterIterator, __construct, arginfo_recursive_callback_filter_it___construct, ZEND_ACC_PUBLIC)
2309    SPL_ME(RecursiveFilterIterator,  hasChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2310    SPL_ME(RecursiveCallbackFilterIterator,  getChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2311    PHP_FE_END
2312};
2313
2314ZEND_BEGIN_ARG_INFO(arginfo_parent_it___construct, 0)
2315    ZEND_ARG_OBJ_INFO(0, iterator, RecursiveIterator, 0)
2316ZEND_END_ARG_INFO();
2317
2318static const zend_function_entry spl_funcs_RecursiveFilterIterator[] = {
2319    SPL_ME(RecursiveFilterIterator,  __construct,      arginfo_parent_it___construct, ZEND_ACC_PUBLIC)
2320    SPL_ME(RecursiveFilterIterator,  hasChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2321    SPL_ME(RecursiveFilterIterator,  getChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2322    PHP_FE_END
2323};
2324
2325static const zend_function_entry spl_funcs_ParentIterator[] = {
2326    SPL_ME(ParentIterator,  __construct,      arginfo_parent_it___construct, ZEND_ACC_PUBLIC)
2327    SPL_MA(ParentIterator,  accept,           RecursiveFilterIterator, hasChildren, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2328    PHP_FE_END
2329};
2330
2331#if HAVE_PCRE || HAVE_BUNDLED_PCRE
2332ZEND_BEGIN_ARG_INFO_EX(arginfo_regex_it___construct, 0, 0, 2)
2333    ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
2334    ZEND_ARG_INFO(0, regex)
2335    ZEND_ARG_INFO(0, mode)
2336    ZEND_ARG_INFO(0, flags)
2337    ZEND_ARG_INFO(0, preg_flags)
2338ZEND_END_ARG_INFO();
2339
2340ZEND_BEGIN_ARG_INFO_EX(arginfo_regex_it_set_mode, 0, 0, 1)
2341    ZEND_ARG_INFO(0, mode)
2342ZEND_END_ARG_INFO();
2343
2344ZEND_BEGIN_ARG_INFO_EX(arginfo_regex_it_set_flags, 0, 0, 1)
2345    ZEND_ARG_INFO(0, flags)
2346ZEND_END_ARG_INFO();
2347
2348ZEND_BEGIN_ARG_INFO_EX(arginfo_regex_it_set_preg_flags, 0, 0, 1)
2349    ZEND_ARG_INFO(0, preg_flags)
2350ZEND_END_ARG_INFO();
2351
2352static const zend_function_entry spl_funcs_RegexIterator[] = {
2353    SPL_ME(RegexIterator,   __construct,      arginfo_regex_it___construct,    ZEND_ACC_PUBLIC)
2354    SPL_ME(RegexIterator,   accept,           arginfo_recursive_it_void,       ZEND_ACC_PUBLIC)
2355    SPL_ME(RegexIterator,   getMode,          arginfo_recursive_it_void,       ZEND_ACC_PUBLIC)
2356    SPL_ME(RegexIterator,   setMode,          arginfo_regex_it_set_mode,       ZEND_ACC_PUBLIC)
2357    SPL_ME(RegexIterator,   getFlags,         arginfo_recursive_it_void,       ZEND_ACC_PUBLIC)
2358    SPL_ME(RegexIterator,   setFlags,         arginfo_regex_it_set_flags,      ZEND_ACC_PUBLIC)
2359    SPL_ME(RegexIterator,   getPregFlags,     arginfo_recursive_it_void,       ZEND_ACC_PUBLIC)
2360    SPL_ME(RegexIterator,   setPregFlags,     arginfo_regex_it_set_preg_flags, ZEND_ACC_PUBLIC)
2361    SPL_ME(RegexIterator,   getRegex,         arginfo_recursive_it_void,       ZEND_ACC_PUBLIC)
2362    PHP_FE_END
2363};
2364
2365ZEND_BEGIN_ARG_INFO_EX(arginfo_rec_regex_it___construct, 0, 0, 2)
2366    ZEND_ARG_OBJ_INFO(0, iterator, RecursiveIterator, 0)
2367    ZEND_ARG_INFO(0, regex)
2368    ZEND_ARG_INFO(0, mode)
2369    ZEND_ARG_INFO(0, flags)
2370    ZEND_ARG_INFO(0, preg_flags)
2371ZEND_END_ARG_INFO();
2372
2373static const zend_function_entry spl_funcs_RecursiveRegexIterator[] = {
2374    SPL_ME(RecursiveRegexIterator,  __construct,      arginfo_rec_regex_it___construct, ZEND_ACC_PUBLIC)
2375    SPL_ME(RecursiveFilterIterator, hasChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2376    SPL_ME(RecursiveRegexIterator,  getChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2377    PHP_FE_END
2378};
2379#endif
2380
2381static inline int spl_limit_it_valid(spl_dual_it_object *intern TSRMLS_DC)
2382{
2383    /* FAILURE / SUCCESS */
2384    if (intern->u.limit.count != -1 && intern->current.pos >= intern->u.limit.offset + intern->u.limit.count) {
2385        return FAILURE;
2386    } else {
2387        return spl_dual_it_valid(intern TSRMLS_CC);
2388    }
2389}
2390
2391static inline void spl_limit_it_seek(spl_dual_it_object *intern, long pos TSRMLS_DC)
2392{
2393    zval  *zpos;
2394
2395    spl_dual_it_free(intern TSRMLS_CC);
2396    if (pos < intern->u.limit.offset) {
2397        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);
2398        return;
2399    }
2400    if (pos >= intern->u.limit.offset + intern->u.limit.count && intern->u.limit.count != -1) {
2401        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);
2402        return;
2403    }
2404    if (pos != intern->current.pos && instanceof_function(intern->inner.ce, spl_ce_SeekableIterator TSRMLS_CC)) {
2405        MAKE_STD_ZVAL(zpos);
2406        ZVAL_LONG(zpos, pos);
2407        spl_dual_it_free(intern TSRMLS_CC);
2408        zend_call_method_with_1_params(&intern->inner.zobject, intern->inner.ce, NULL, "seek", NULL, zpos);
2409        zval_ptr_dtor(&zpos);
2410        if (!EG(exception)) {
2411            intern->current.pos = pos;
2412            if (spl_limit_it_valid(intern TSRMLS_CC) == SUCCESS) {
2413                spl_dual_it_fetch(intern, 0 TSRMLS_CC);
2414            }
2415        }
2416    } else {
2417        /* emulate the forward seek, by next() calls */
2418        /* a back ward seek is done by a previous rewind() */
2419        if (pos < intern->current.pos) {
2420            spl_dual_it_rewind(intern TSRMLS_CC);
2421        }
2422        while (pos > intern->current.pos && spl_dual_it_valid(intern TSRMLS_CC) == SUCCESS) {
2423            spl_dual_it_next(intern, 1 TSRMLS_CC);
2424        }
2425        if (spl_dual_it_valid(intern TSRMLS_CC) == SUCCESS) {
2426            spl_dual_it_fetch(intern, 1 TSRMLS_CC);
2427        }
2428    }
2429}
2430
2431/* {{{ proto LimitIterator::__construct(Iterator it [, int offset, int count])
2432   Construct a LimitIterator from an Iterator with a given starting offset and optionally a maximum count */
2433SPL_METHOD(LimitIterator, __construct)
2434{
2435    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_LimitIterator, zend_ce_iterator, DIT_LimitIterator);
2436} /* }}} */
2437
2438/* {{{ proto void LimitIterator::rewind()
2439   Rewind the iterator to the specified starting offset */
2440SPL_METHOD(LimitIterator, rewind)
2441{
2442    spl_dual_it_object   *intern;
2443
2444    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2445    spl_dual_it_rewind(intern TSRMLS_CC);
2446    spl_limit_it_seek(intern, intern->u.limit.offset TSRMLS_CC);
2447} /* }}} */
2448
2449/* {{{ proto bool LimitIterator::valid()
2450   Check whether the current element is valid */
2451SPL_METHOD(LimitIterator, valid)
2452{
2453    spl_dual_it_object   *intern;
2454
2455    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2456
2457/*  RETURN_BOOL(spl_limit_it_valid(intern TSRMLS_CC) == SUCCESS);*/
2458    RETURN_BOOL((intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) && intern->current.data);
2459} /* }}} */
2460
2461/* {{{ proto void LimitIterator::next()
2462   Move the iterator forward */
2463SPL_METHOD(LimitIterator, next)
2464{
2465    spl_dual_it_object   *intern;
2466
2467    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2468
2469    spl_dual_it_next(intern, 1 TSRMLS_CC);
2470    if (intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) {
2471        spl_dual_it_fetch(intern, 1 TSRMLS_CC);
2472    }
2473} /* }}} */
2474
2475/* {{{ proto void LimitIterator::seek(int position)
2476   Seek to the given position */
2477SPL_METHOD(LimitIterator, seek)
2478{
2479    spl_dual_it_object   *intern;
2480    long                 pos;
2481
2482    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &pos) == FAILURE) {
2483        return;
2484    }
2485
2486    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2487    spl_limit_it_seek(intern, pos TSRMLS_CC);
2488    RETURN_LONG(intern->current.pos);
2489} /* }}} */
2490
2491/* {{{ proto int LimitIterator::getPosition()
2492   Return the current position */
2493SPL_METHOD(LimitIterator, getPosition)
2494{
2495    spl_dual_it_object   *intern;
2496    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2497    RETURN_LONG(intern->current.pos);
2498} /* }}} */
2499
2500ZEND_BEGIN_ARG_INFO(arginfo_seekable_it_seek, 0)
2501    ZEND_ARG_INFO(0, position)
2502ZEND_END_ARG_INFO();
2503
2504static const zend_function_entry spl_funcs_SeekableIterator[] = {
2505    SPL_ABSTRACT_ME(SeekableIterator, seek, arginfo_seekable_it_seek)
2506    PHP_FE_END
2507};
2508
2509ZEND_BEGIN_ARG_INFO_EX(arginfo_limit_it___construct, 0, 0, 1)
2510    ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
2511    ZEND_ARG_INFO(0, offset)
2512    ZEND_ARG_INFO(0, count)
2513ZEND_END_ARG_INFO();
2514
2515ZEND_BEGIN_ARG_INFO(arginfo_limit_it_seek, 0)
2516    ZEND_ARG_INFO(0, position)
2517ZEND_END_ARG_INFO();
2518
2519static const zend_function_entry spl_funcs_LimitIterator[] = {
2520    SPL_ME(LimitIterator,   __construct,      arginfo_limit_it___construct, ZEND_ACC_PUBLIC)
2521    SPL_ME(LimitIterator,   rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2522    SPL_ME(LimitIterator,   valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2523    SPL_ME(dual_it,         key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2524    SPL_ME(dual_it,         current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2525    SPL_ME(LimitIterator,   next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2526    SPL_ME(LimitIterator,   seek,             arginfo_limit_it_seek, ZEND_ACC_PUBLIC)
2527    SPL_ME(LimitIterator,   getPosition,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2528    SPL_ME(dual_it,         getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
2529    PHP_FE_END
2530};
2531
2532static inline int spl_caching_it_valid(spl_dual_it_object *intern TSRMLS_DC)
2533{
2534    return intern->u.caching.flags & CIT_VALID ? SUCCESS : FAILURE;
2535}
2536
2537static inline int spl_caching_it_has_next(spl_dual_it_object *intern TSRMLS_DC)
2538{
2539    return spl_dual_it_valid(intern TSRMLS_CC);
2540}
2541
2542static inline void spl_caching_it_next(spl_dual_it_object *intern TSRMLS_DC)
2543{
2544    if (spl_dual_it_fetch(intern, 1 TSRMLS_CC) == SUCCESS) {
2545        intern->u.caching.flags |= CIT_VALID;
2546        /* Full cache ? */
2547        if (intern->u.caching.flags & CIT_FULL_CACHE) {
2548            zval *zcacheval;
2549            zval *key = intern->current.key;
2550
2551            MAKE_STD_ZVAL(zcacheval);
2552            ZVAL_ZVAL(zcacheval, intern->current.data, 1, 0);
2553
2554            array_set_zval_key(HASH_OF(intern->u.caching.zcache), key, zcacheval);
2555
2556            zval_ptr_dtor(&zcacheval);
2557        }
2558        /* Recursion ? */
2559        if (intern->dit_type == DIT_RecursiveCachingIterator) {
2560            zval *retval, *zchildren, zflags;
2561            zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "haschildren", &retval);
2562            if (EG(exception)) {
2563                if (retval) {
2564                    zval_ptr_dtor(&retval);
2565                }
2566                if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2567                    zend_clear_exception(TSRMLS_C);
2568                } else {
2569                    return;
2570                }
2571            } else {
2572                if (zend_is_true(retval)) {
2573                    zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &zchildren);
2574                    if (EG(exception)) {
2575                        if (zchildren) {
2576                            zval_ptr_dtor(&zchildren);
2577                        }
2578                        if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2579                            zend_clear_exception(TSRMLS_C);
2580                        } else {
2581                            zval_ptr_dtor(&retval);
2582                            return;
2583                        }
2584                    } else {
2585                        INIT_PZVAL(&zflags);
2586                        ZVAL_LONG(&zflags, intern->u.caching.flags & CIT_PUBLIC);
2587                        spl_instantiate_arg_ex2(spl_ce_RecursiveCachingIterator, &intern->u.caching.zchildren, 1, zchildren, &zflags TSRMLS_CC);
2588                        zval_ptr_dtor(&zchildren);
2589                    }
2590                }
2591                zval_ptr_dtor(&retval);
2592                if (EG(exception)) {
2593                    if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2594                        zend_clear_exception(TSRMLS_C);
2595                    } else {
2596                        return;
2597                    }
2598                }
2599            }
2600        }
2601        if (intern->u.caching.flags & (CIT_TOSTRING_USE_INNER|CIT_CALL_TOSTRING)) {
2602            int  use_copy;
2603            zval expr_copy;
2604            ALLOC_ZVAL(intern->u.caching.zstr);
2605            if (intern->u.caching.flags & CIT_TOSTRING_USE_INNER) {
2606                *intern->u.caching.zstr = *intern->inner.zobject;
2607            } else {
2608                *intern->u.caching.zstr = *intern->current.data;
2609            }
2610            zend_make_printable_zval(intern->u.caching.zstr, &expr_copy, &use_copy);
2611            if (use_copy) {
2612                *intern->u.caching.zstr = expr_copy;
2613                INIT_PZVAL(intern->u.caching.zstr);
2614                zval_copy_ctor(intern->u.caching.zstr);
2615                zval_dtor(&expr_copy);
2616            } else {
2617                INIT_PZVAL(intern->u.caching.zstr);
2618                zval_copy_ctor(intern->u.caching.zstr);
2619            }
2620        }
2621        spl_dual_it_next(intern, 0 TSRMLS_CC);
2622    } else {
2623        intern->u.caching.flags &= ~CIT_VALID;
2624    }
2625}
2626
2627static inline void spl_caching_it_rewind(spl_dual_it_object *intern TSRMLS_DC)
2628{
2629    spl_dual_it_rewind(intern TSRMLS_CC);
2630    zend_hash_clean(HASH_OF(intern->u.caching.zcache));
2631    spl_caching_it_next(intern TSRMLS_CC);
2632}
2633
2634/* {{{ proto void CachingIterator::__construct(Iterator it [, flags = CIT_CALL_TOSTRING])
2635   Construct a CachingIterator from an Iterator */
2636SPL_METHOD(CachingIterator, __construct)
2637{
2638    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_CachingIterator, zend_ce_iterator, DIT_CachingIterator);
2639} /* }}} */
2640
2641/* {{{ proto void CachingIterator::rewind()
2642   Rewind the iterator */
2643SPL_METHOD(CachingIterator, rewind)
2644{
2645    spl_dual_it_object   *intern;
2646
2647    if (zend_parse_parameters_none() == FAILURE) {
2648        return;
2649    }
2650
2651    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2652
2653    spl_caching_it_rewind(intern TSRMLS_CC);
2654} /* }}} */
2655
2656/* {{{ proto bool CachingIterator::valid()
2657   Check whether the current element is valid */
2658SPL_METHOD(CachingIterator, valid)
2659{
2660    spl_dual_it_object   *intern;
2661
2662    if (zend_parse_parameters_none() == FAILURE) {
2663        return;
2664    }
2665
2666    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2667
2668    RETURN_BOOL(spl_caching_it_valid(intern TSRMLS_CC) == SUCCESS);
2669} /* }}} */
2670
2671/* {{{ proto void CachingIterator::next()
2672   Move the iterator forward */
2673SPL_METHOD(CachingIterator, next)
2674{
2675    spl_dual_it_object   *intern;
2676
2677    if (zend_parse_parameters_none() == FAILURE) {
2678        return;
2679    }
2680
2681    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2682
2683    spl_caching_it_next(intern TSRMLS_CC);
2684} /* }}} */
2685
2686/* {{{ proto bool CachingIterator::hasNext()
2687   Check whether the inner iterator has a valid next element */
2688SPL_METHOD(CachingIterator, hasNext)
2689{
2690    spl_dual_it_object   *intern;
2691
2692    if (zend_parse_parameters_none() == FAILURE) {
2693        return;
2694    }
2695
2696    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2697
2698    RETURN_BOOL(spl_caching_it_has_next(intern TSRMLS_CC) == SUCCESS);
2699} /* }}} */
2700
2701/* {{{ proto string CachingIterator::__toString()
2702   Return the string representation of the current element */
2703SPL_METHOD(CachingIterator, __toString)
2704{
2705    spl_dual_it_object   *intern;
2706
2707    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2708
2709    if (!(intern->u.caching.flags & (CIT_CALL_TOSTRING|CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT|CIT_TOSTRING_USE_INNER)))  {
2710        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s does not fetch string value (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name);
2711        return;
2712    }
2713    if (intern->u.caching.flags & CIT_TOSTRING_USE_KEY) {
2714        MAKE_COPY_ZVAL(&intern->current.key, return_value);
2715        convert_to_string(return_value);
2716        return;
2717    } else if (intern->u.caching.flags & CIT_TOSTRING_USE_CURRENT) {
2718        MAKE_COPY_ZVAL(&intern->current.data, return_value);
2719        convert_to_string(return_value);
2720        return;
2721    }
2722    if (intern->u.caching.zstr) {
2723        RETURN_STRINGL(Z_STRVAL_P(intern->u.caching.zstr), Z_STRLEN_P(intern->u.caching.zstr), 1);
2724    } else {
2725        RETURN_NULL();
2726    }
2727} /* }}} */
2728
2729/* {{{ proto void CachingIterator::offsetSet(mixed index, mixed newval)
2730   Set given index in cache */
2731SPL_METHOD(CachingIterator, offsetSet)
2732{
2733    spl_dual_it_object   *intern;
2734    char *arKey;
2735    uint nKeyLength;
2736    zval *value;
2737
2738    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2739
2740    if (!(intern->u.caching.flags & CIT_FULL_CACHE))    {
2741        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);
2742        return;
2743    }
2744
2745    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &arKey, &nKeyLength, &value) == FAILURE) {
2746        return;
2747    }
2748
2749    Z_ADDREF_P(value);
2750    zend_symtable_update(HASH_OF(intern->u.caching.zcache), arKey, nKeyLength+1, &value, sizeof(value), NULL);
2751}
2752/* }}} */
2753
2754/* {{{ proto string CachingIterator::offsetGet(mixed index)
2755   Return the internal cache if used */
2756SPL_METHOD(CachingIterator, offsetGet)
2757{
2758    spl_dual_it_object   *intern;
2759    char *arKey;
2760    uint nKeyLength;
2761    zval **value;
2762
2763    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2764
2765    if (!(intern->u.caching.flags & CIT_FULL_CACHE))    {
2766        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);
2767        return;
2768    }
2769
2770    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arKey, &nKeyLength) == FAILURE) {
2771        return;
2772    }
2773
2774    if (zend_symtable_find(HASH_OF(intern->u.caching.zcache), arKey, nKeyLength+1, (void**)&value) == FAILURE) {
2775        zend_error(E_NOTICE, "Undefined index: %s", arKey);
2776        return;
2777    }
2778
2779    RETURN_ZVAL(*value, 1, 0);
2780}
2781/* }}} */
2782
2783/* {{{ proto void CachingIterator::offsetUnset(mixed index)
2784   Unset given index in cache */
2785SPL_METHOD(CachingIterator, offsetUnset)
2786{
2787    spl_dual_it_object   *intern;
2788    char *arKey;
2789    uint nKeyLength;
2790
2791    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2792
2793    if (!(intern->u.caching.flags & CIT_FULL_CACHE))    {
2794        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);
2795        return;
2796    }
2797
2798    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arKey, &nKeyLength) == FAILURE) {
2799        return;
2800    }
2801
2802    zend_symtable_del(HASH_OF(intern->u.caching.zcache), arKey, nKeyLength+1);
2803}
2804/* }}} */
2805
2806/* {{{ proto bool CachingIterator::offsetExists(mixed index)
2807   Return whether the requested index exists */
2808SPL_METHOD(CachingIterator, offsetExists)
2809{
2810    spl_dual_it_object   *intern;
2811    char *arKey;
2812    uint nKeyLength;
2813
2814    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2815
2816    if (!(intern->u.caching.flags & CIT_FULL_CACHE))    {
2817        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name);
2818        return;
2819    }
2820
2821    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arKey, &nKeyLength) == FAILURE) {
2822        return;
2823    }
2824
2825    RETURN_BOOL(zend_symtable_exists(HASH_OF(intern->u.caching.zcache), arKey, nKeyLength+1));
2826}
2827/* }}} */
2828
2829/* {{{ proto bool CachingIterator::getCache()
2830   Return the cache */
2831SPL_METHOD(CachingIterator, getCache)
2832{
2833    spl_dual_it_object   *intern;
2834
2835    if (zend_parse_parameters_none() == FAILURE) {
2836        return;
2837    }
2838
2839    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2840
2841    if (!(intern->u.caching.flags & CIT_FULL_CACHE))    {
2842        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);
2843        return;
2844    }
2845
2846    RETURN_ZVAL(intern->u.caching.zcache, 1, 0);
2847}
2848/* }}} */
2849
2850/* {{{ proto int CachingIterator::getFlags()
2851   Return the internal flags */
2852SPL_METHOD(CachingIterator, getFlags)
2853{
2854    spl_dual_it_object   *intern;
2855
2856    if (zend_parse_parameters_none() == FAILURE) {
2857        return;
2858    }
2859
2860    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2861
2862    RETURN_LONG(intern->u.caching.flags);
2863}
2864/* }}} */
2865
2866/* {{{ proto void CachingIterator::setFlags(int flags)
2867   Set the internal flags */
2868SPL_METHOD(CachingIterator, setFlags)
2869{
2870    spl_dual_it_object   *intern;
2871    long flags;
2872
2873    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2874
2875    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &flags) == FAILURE) {
2876        return;
2877    }
2878
2879    if (spl_cit_check_flags(flags) != SUCCESS) {
2880        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);
2881        return;
2882    }
2883    if ((intern->u.caching.flags & CIT_CALL_TOSTRING) != 0 && (flags & CIT_CALL_TOSTRING) == 0) {
2884        zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag CALL_TO_STRING is not possible", 0 TSRMLS_CC);
2885        return;
2886    }
2887    if ((intern->u.caching.flags & CIT_TOSTRING_USE_INNER) != 0 && (flags & CIT_TOSTRING_USE_INNER) == 0) {
2888        zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag TOSTRING_USE_INNER is not possible", 0 TSRMLS_CC);
2889        return;
2890    }
2891    if ((flags & CIT_FULL_CACHE) != 0 && (intern->u.caching.flags & CIT_FULL_CACHE) == 0) {
2892        /* clear on (re)enable */
2893        zend_hash_clean(HASH_OF(intern->u.caching.zcache));
2894    }
2895    intern->u.caching.flags = (intern->u.caching.flags & ~CIT_PUBLIC) | (flags & CIT_PUBLIC);
2896}
2897/* }}} */
2898
2899/* {{{ proto void CachingIterator::count()
2900   Number of cached elements */
2901SPL_METHOD(CachingIterator, count)
2902{
2903    spl_dual_it_object   *intern;
2904
2905    if (zend_parse_parameters_none() == FAILURE) {
2906        return;
2907    }
2908
2909    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2910
2911    if (!(intern->u.caching.flags & CIT_FULL_CACHE))    {
2912        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);
2913        return;
2914    }
2915
2916    RETURN_LONG(zend_hash_num_elements(HASH_OF(intern->u.caching.zcache)));
2917}
2918/* }}} */
2919
2920ZEND_BEGIN_ARG_INFO_EX(arginfo_caching_it___construct, 0, 0, 1)
2921    ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
2922    ZEND_ARG_INFO(0, flags)
2923ZEND_END_ARG_INFO();
2924
2925ZEND_BEGIN_ARG_INFO(arginfo_caching_it_setFlags, 0)
2926    ZEND_ARG_INFO(0, flags)
2927ZEND_END_ARG_INFO();
2928
2929ZEND_BEGIN_ARG_INFO(arginfo_caching_it_offsetGet, 0)
2930    ZEND_ARG_INFO(0, index)
2931ZEND_END_ARG_INFO();
2932
2933ZEND_BEGIN_ARG_INFO(arginfo_caching_it_offsetSet, 0)
2934    ZEND_ARG_INFO(0, index)
2935    ZEND_ARG_INFO(0, newval)
2936ZEND_END_ARG_INFO();
2937
2938static const zend_function_entry spl_funcs_CachingIterator[] = {
2939    SPL_ME(CachingIterator, __construct,      arginfo_caching_it___construct, ZEND_ACC_PUBLIC)
2940    SPL_ME(CachingIterator, rewind,           arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2941    SPL_ME(CachingIterator, valid,            arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2942    SPL_ME(dual_it,         key,              arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2943    SPL_ME(dual_it,         current,          arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2944    SPL_ME(CachingIterator, next,             arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2945    SPL_ME(CachingIterator, hasNext,          arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2946    SPL_ME(CachingIterator, __toString,       arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2947    SPL_ME(dual_it,         getInnerIterator, arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2948    SPL_ME(CachingIterator, getFlags,         arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2949    SPL_ME(CachingIterator, setFlags,         arginfo_caching_it_setFlags,    ZEND_ACC_PUBLIC)
2950    SPL_ME(CachingIterator, offsetGet,        arginfo_caching_it_offsetGet,   ZEND_ACC_PUBLIC)
2951    SPL_ME(CachingIterator, offsetSet,        arginfo_caching_it_offsetSet,   ZEND_ACC_PUBLIC)
2952    SPL_ME(CachingIterator, offsetUnset,      arginfo_caching_it_offsetGet,   ZEND_ACC_PUBLIC)
2953    SPL_ME(CachingIterator, offsetExists,     arginfo_caching_it_offsetGet,   ZEND_ACC_PUBLIC)
2954    SPL_ME(CachingIterator, getCache,         arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2955    SPL_ME(CachingIterator, count,            arginfo_recursive_it_void,      ZEND_ACC_PUBLIC)
2956    PHP_FE_END
2957};
2958
2959/* {{{ proto void RecursiveCachingIterator::__construct(RecursiveIterator it [, flags = CIT_CALL_TOSTRING])
2960   Create an iterator from a RecursiveIterator */
2961SPL_METHOD(RecursiveCachingIterator, __construct)
2962{
2963    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveCachingIterator, spl_ce_RecursiveIterator, DIT_RecursiveCachingIterator);
2964} /* }}} */
2965
2966/* {{{ proto bool RecursiveCachingIterator::hasChildren()
2967   Check whether the current element of the inner iterator has children */
2968SPL_METHOD(RecursiveCachingIterator, hasChildren)
2969{
2970    spl_dual_it_object   *intern;
2971
2972    if (zend_parse_parameters_none() == FAILURE) {
2973        return;
2974    }
2975
2976    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2977
2978    RETURN_BOOL(intern->u.caching.zchildren);
2979} /* }}} */
2980
2981/* {{{ proto RecursiveCachingIterator RecursiveCachingIterator::getChildren()
2982  Return the inner iterator's children as a RecursiveCachingIterator */
2983SPL_METHOD(RecursiveCachingIterator, getChildren)
2984{
2985    spl_dual_it_object   *intern;
2986
2987    if (zend_parse_parameters_none() == FAILURE) {
2988        return;
2989    }
2990
2991    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
2992
2993    if (intern->u.caching.zchildren) {
2994        RETURN_ZVAL(intern->u.caching.zchildren, 1, 0);
2995    } else {
2996        RETURN_NULL();
2997    }
2998} /* }}} */
2999
3000ZEND_BEGIN_ARG_INFO_EX(arginfo_caching_rec_it___construct, 0, ZEND_RETURN_VALUE, 1)
3001    ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
3002    ZEND_ARG_INFO(0, flags)
3003ZEND_END_ARG_INFO();
3004
3005static const zend_function_entry spl_funcs_RecursiveCachingIterator[] = {
3006    SPL_ME(RecursiveCachingIterator, __construct,   arginfo_caching_rec_it___construct, ZEND_ACC_PUBLIC)
3007    SPL_ME(RecursiveCachingIterator, hasChildren,   arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3008    SPL_ME(RecursiveCachingIterator, getChildren,   arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3009    PHP_FE_END
3010};
3011
3012/* {{{ proto void IteratorIterator::__construct(Traversable it)
3013   Create an iterator from anything that is traversable */
3014SPL_METHOD(IteratorIterator, __construct)
3015{
3016    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_IteratorIterator, zend_ce_traversable, DIT_IteratorIterator);
3017} /* }}} */
3018
3019ZEND_BEGIN_ARG_INFO(arginfo_iterator_it___construct, 0)
3020    ZEND_ARG_OBJ_INFO(0, iterator, Traversable, 0)
3021ZEND_END_ARG_INFO();
3022
3023static const zend_function_entry spl_funcs_IteratorIterator[] = {
3024    SPL_ME(IteratorIterator, __construct,      arginfo_iterator_it___construct, ZEND_ACC_PUBLIC)
3025    SPL_ME(dual_it,          rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3026    SPL_ME(dual_it,          valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3027    SPL_ME(dual_it,          key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3028    SPL_ME(dual_it,          current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3029    SPL_ME(dual_it,          next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3030    SPL_ME(dual_it,          getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3031    PHP_FE_END
3032};
3033
3034/* {{{ proto void NoRewindIterator::__construct(Iterator it)
3035   Create an iterator from another iterator */
3036SPL_METHOD(NoRewindIterator, __construct)
3037{
3038    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_NoRewindIterator, zend_ce_iterator, DIT_NoRewindIterator);
3039} /* }}} */
3040
3041/* {{{ proto void NoRewindIterator::rewind()
3042   Prevent a call to inner iterators rewind() */
3043SPL_METHOD(NoRewindIterator, rewind)
3044{
3045    if (zend_parse_parameters_none() == FAILURE) {
3046        return;
3047    }
3048    /* nothing to do */
3049} /* }}} */
3050
3051/* {{{ proto bool NoRewindIterator::valid()
3052   Return inner iterators valid() */
3053SPL_METHOD(NoRewindIterator, valid)
3054{
3055    spl_dual_it_object   *intern;
3056
3057    if (zend_parse_parameters_none() == FAILURE) {
3058        return;
3059    }
3060
3061    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3062    RETURN_BOOL(intern->inner.iterator->funcs->valid(intern->inner.iterator TSRMLS_CC) == SUCCESS);
3063} /* }}} */
3064
3065/* {{{ proto mixed NoRewindIterator::key()
3066   Return inner iterators key() */
3067SPL_METHOD(NoRewindIterator, key)
3068{
3069    spl_dual_it_object   *intern;
3070
3071    if (zend_parse_parameters_none() == FAILURE) {
3072        return;
3073    }
3074
3075    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3076
3077    if (intern->inner.iterator->funcs->get_current_key) {
3078        intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, return_value TSRMLS_CC);
3079    } else {
3080        RETURN_NULL();
3081    }
3082} /* }}} */
3083
3084/* {{{ proto mixed NoRewindIterator::current()
3085   Return inner iterators current() */
3086SPL_METHOD(NoRewindIterator, current)
3087{
3088    spl_dual_it_object   *intern;
3089    zval **data;
3090
3091    if (zend_parse_parameters_none() == FAILURE) {
3092        return;
3093    }
3094
3095    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3096    intern->inner.iterator->funcs->get_current_data(intern->inner.iterator, &data TSRMLS_CC);
3097    if (data && *data) {
3098        RETURN_ZVAL(*data, 1, 0);
3099    }
3100} /* }}} */
3101
3102/* {{{ proto void NoRewindIterator::next()
3103   Return inner iterators next() */
3104SPL_METHOD(NoRewindIterator, next)
3105{
3106    spl_dual_it_object   *intern;
3107
3108    if (zend_parse_parameters_none() == FAILURE) {
3109        return;
3110    }
3111
3112    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3113    intern->inner.iterator->funcs->move_forward(intern->inner.iterator TSRMLS_CC);
3114} /* }}} */
3115
3116ZEND_BEGIN_ARG_INFO(arginfo_norewind_it___construct, 0)
3117    ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
3118ZEND_END_ARG_INFO();
3119
3120static const zend_function_entry spl_funcs_NoRewindIterator[] = {
3121    SPL_ME(NoRewindIterator, __construct,      arginfo_norewind_it___construct, ZEND_ACC_PUBLIC)
3122    SPL_ME(NoRewindIterator, rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3123    SPL_ME(NoRewindIterator, valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3124    SPL_ME(NoRewindIterator, key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3125    SPL_ME(NoRewindIterator, current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3126    SPL_ME(NoRewindIterator, next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3127    SPL_ME(dual_it,          getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3128    PHP_FE_END
3129};
3130
3131/* {{{ proto void InfiniteIterator::__construct(Iterator it)
3132   Create an iterator from another iterator */
3133SPL_METHOD(InfiniteIterator, __construct)
3134{
3135    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_InfiniteIterator, zend_ce_iterator, DIT_InfiniteIterator);
3136} /* }}} */
3137
3138/* {{{ proto void InfiniteIterator::next()
3139   Prevent a call to inner iterators rewind() (internally the current data will be fetched if valid()) */
3140SPL_METHOD(InfiniteIterator, next)
3141{
3142    spl_dual_it_object   *intern;
3143
3144    if (zend_parse_parameters_none() == FAILURE) {
3145        return;
3146    }
3147
3148    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3149
3150    spl_dual_it_next(intern, 1 TSRMLS_CC);
3151    if (spl_dual_it_valid(intern TSRMLS_CC) == SUCCESS) {
3152        spl_dual_it_fetch(intern, 0 TSRMLS_CC);
3153    } else {
3154        spl_dual_it_rewind(intern TSRMLS_CC);
3155        if (spl_dual_it_valid(intern TSRMLS_CC) == SUCCESS) {
3156            spl_dual_it_fetch(intern, 0 TSRMLS_CC);
3157        }
3158    }
3159} /* }}} */
3160
3161static const zend_function_entry spl_funcs_InfiniteIterator[] = {
3162    SPL_ME(InfiniteIterator, __construct,      arginfo_norewind_it___construct, ZEND_ACC_PUBLIC)
3163    SPL_ME(InfiniteIterator, next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3164    PHP_FE_END
3165};
3166
3167/* {{{ proto void EmptyIterator::rewind()
3168   Does nothing  */
3169SPL_METHOD(EmptyIterator, rewind)
3170{
3171    if (zend_parse_parameters_none() == FAILURE) {
3172        return;
3173    }
3174} /* }}} */
3175
3176/* {{{ proto false EmptyIterator::valid()
3177   Return false */
3178SPL_METHOD(EmptyIterator, valid)
3179{
3180    if (zend_parse_parameters_none() == FAILURE) {
3181        return;
3182    }
3183    RETURN_FALSE;
3184} /* }}} */
3185
3186/* {{{ proto void EmptyIterator::key()
3187   Throws exception BadMethodCallException */
3188SPL_METHOD(EmptyIterator, key)
3189{
3190    if (zend_parse_parameters_none() == FAILURE) {
3191        return;
3192    }
3193    zend_throw_exception(spl_ce_BadMethodCallException, "Accessing the key of an EmptyIterator", 0 TSRMLS_CC);
3194} /* }}} */
3195
3196/* {{{ proto void EmptyIterator::current()
3197   Throws exception BadMethodCallException */
3198SPL_METHOD(EmptyIterator, current)
3199{
3200    if (zend_parse_parameters_none() == FAILURE) {
3201        return;
3202    }
3203    zend_throw_exception(spl_ce_BadMethodCallException, "Accessing the value of an EmptyIterator", 0 TSRMLS_CC);
3204} /* }}} */
3205
3206/* {{{ proto void EmptyIterator::next()
3207   Does nothing */
3208SPL_METHOD(EmptyIterator, next)
3209{
3210    if (zend_parse_parameters_none() == FAILURE) {
3211        return;
3212    }
3213} /* }}} */
3214
3215static const zend_function_entry spl_funcs_EmptyIterator[] = {
3216    SPL_ME(EmptyIterator, rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3217    SPL_ME(EmptyIterator, valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3218    SPL_ME(EmptyIterator, key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3219    SPL_ME(EmptyIterator, current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3220    SPL_ME(EmptyIterator, next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3221    PHP_FE_END
3222};
3223
3224int spl_append_it_next_iterator(spl_dual_it_object *intern TSRMLS_DC) /* {{{*/
3225{
3226    spl_dual_it_free(intern TSRMLS_CC);
3227
3228    if (intern->inner.zobject) {
3229        zval_ptr_dtor(&intern->inner.zobject);
3230        intern->inner.zobject = NULL;
3231        intern->inner.ce = NULL;
3232        intern->inner.object = NULL;
3233        if (intern->inner.iterator) {
3234            intern->inner.iterator->funcs->dtor(intern->inner.iterator TSRMLS_CC);
3235            intern->inner.iterator = NULL;
3236        }
3237    }
3238    if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator TSRMLS_CC) == SUCCESS) {
3239        zval **it;
3240
3241        intern->u.append.iterator->funcs->get_current_data(intern->u.append.iterator, &it TSRMLS_CC);
3242        Z_ADDREF_PP(it);
3243        intern->inner.zobject = *it;
3244        intern->inner.ce = Z_OBJCE_PP(it);
3245        intern->inner.object = zend_object_store_get_object(*it TSRMLS_CC);
3246        intern->inner.iterator = intern->inner.ce->get_iterator(intern->inner.ce, *it, 0 TSRMLS_CC);
3247        spl_dual_it_rewind(intern TSRMLS_CC);
3248        return SUCCESS;
3249    } else {
3250        return FAILURE;
3251    }
3252} /* }}} */
3253
3254void spl_append_it_fetch(spl_dual_it_object *intern TSRMLS_DC) /* {{{*/
3255{
3256    while (spl_dual_it_valid(intern TSRMLS_CC) != SUCCESS) {
3257        intern->u.append.iterator->funcs->move_forward(intern->u.append.iterator TSRMLS_CC);
3258        if (spl_append_it_next_iterator(intern TSRMLS_CC) != SUCCESS) {
3259            return;
3260        }
3261    }
3262    spl_dual_it_fetch(intern, 0 TSRMLS_CC);
3263} /* }}} */
3264
3265void spl_append_it_next(spl_dual_it_object *intern TSRMLS_DC) /* {{{ */
3266{
3267    if (spl_dual_it_valid(intern TSRMLS_CC) == SUCCESS) {
3268        spl_dual_it_next(intern, 1 TSRMLS_CC);
3269    }
3270    spl_append_it_fetch(intern TSRMLS_CC);
3271} /* }}} */
3272
3273/* {{{ proto void AppendIterator::__construct()
3274   Create an AppendIterator */
3275SPL_METHOD(AppendIterator, __construct)
3276{
3277    spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_AppendIterator, zend_ce_iterator, DIT_AppendIterator);
3278} /* }}} */
3279
3280/* {{{ proto void AppendIterator::append(Iterator it)
3281   Append an iterator */
3282SPL_METHOD(AppendIterator, append)
3283{
3284    spl_dual_it_object   *intern;
3285    zval *it;
3286
3287    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3288
3289    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "O", &it, zend_ce_iterator) == FAILURE) {
3290        return;
3291    }
3292    spl_array_iterator_append(intern->u.append.zarrayit, it TSRMLS_CC);
3293
3294    if (!intern->inner.iterator || spl_dual_it_valid(intern TSRMLS_CC) != SUCCESS) {
3295        if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator TSRMLS_CC) != SUCCESS) {
3296            intern->u.append.iterator->funcs->rewind(intern->u.append.iterator TSRMLS_CC);
3297        }
3298        do {
3299            spl_append_it_next_iterator(intern TSRMLS_CC);
3300        } while (intern->inner.zobject != it);
3301        spl_append_it_fetch(intern TSRMLS_CC);
3302    }
3303} /* }}} */
3304
3305/* {{{ proto void AppendIterator::rewind()
3306   Rewind to the first iterator and rewind the first iterator, too */
3307SPL_METHOD(AppendIterator, rewind)
3308{
3309    spl_dual_it_object   *intern;
3310
3311    if (zend_parse_parameters_none() == FAILURE) {
3312        return;
3313    }
3314
3315    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3316
3317    intern->u.append.iterator->funcs->rewind(intern->u.append.iterator TSRMLS_CC);
3318    if (spl_append_it_next_iterator(intern TSRMLS_CC) == SUCCESS) {
3319        spl_append_it_fetch(intern TSRMLS_CC);
3320    }
3321} /* }}} */
3322
3323/* {{{ proto bool AppendIterator::valid()
3324   Check if the current state is valid */
3325SPL_METHOD(AppendIterator, valid)
3326{
3327    spl_dual_it_object   *intern;
3328
3329    if (zend_parse_parameters_none() == FAILURE) {
3330        return;
3331    }
3332
3333    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3334
3335    RETURN_BOOL(intern->current.data);
3336} /* }}} */
3337
3338/* {{{ proto void AppendIterator::next()
3339   Forward to next element */
3340SPL_METHOD(AppendIterator, next)
3341{
3342    spl_dual_it_object   *intern;
3343
3344    if (zend_parse_parameters_none() == FAILURE) {
3345        return;
3346    }
3347
3348    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3349
3350    spl_append_it_next(intern TSRMLS_CC);
3351} /* }}} */
3352
3353/* {{{ proto int AppendIterator::getIteratorIndex()
3354   Get index of iterator */
3355SPL_METHOD(AppendIterator, getIteratorIndex)
3356{
3357    spl_dual_it_object   *intern;
3358
3359    if (zend_parse_parameters_none() == FAILURE) {
3360        return;
3361    }
3362
3363    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3364
3365    APPENDIT_CHECK_CTOR(intern);
3366    spl_array_iterator_key(intern->u.append.zarrayit, return_value TSRMLS_CC);
3367} /* }}} */
3368
3369/* {{{ proto ArrayIterator AppendIterator::getArrayIterator()
3370   Get access to inner ArrayIterator */
3371SPL_METHOD(AppendIterator, getArrayIterator)
3372{
3373    spl_dual_it_object   *intern;
3374
3375    if (zend_parse_parameters_none() == FAILURE) {
3376        return;
3377    }
3378
3379    SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis());
3380
3381    RETURN_ZVAL(intern->u.append.zarrayit, 1, 0);
3382} /* }}} */
3383
3384ZEND_BEGIN_ARG_INFO(arginfo_append_it_append, 0)
3385    ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
3386ZEND_END_ARG_INFO();
3387
3388static const zend_function_entry spl_funcs_AppendIterator[] = {
3389    SPL_ME(AppendIterator, __construct,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3390    SPL_ME(AppendIterator, append,           arginfo_append_it_append, ZEND_ACC_PUBLIC)
3391    SPL_ME(AppendIterator, rewind,           arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3392    SPL_ME(AppendIterator, valid,            arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3393    SPL_ME(dual_it,        key,              arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3394    SPL_ME(dual_it,        current,          arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3395    SPL_ME(AppendIterator, next,             arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3396    SPL_ME(dual_it,        getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3397    SPL_ME(AppendIterator, getIteratorIndex, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3398    SPL_ME(AppendIterator, getArrayIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
3399    PHP_FE_END
3400};
3401
3402PHPAPI int spl_iterator_apply(zval *obj, spl_iterator_apply_func_t apply_func, void *puser TSRMLS_DC)
3403{
3404    zend_object_iterator   *iter;
3405    zend_class_entry       *ce = Z_OBJCE_P(obj);
3406
3407    iter = ce->get_iterator(ce, obj, 0 TSRMLS_CC);
3408
3409    if (EG(exception)) {
3410        goto done;
3411    }
3412
3413    iter->index = 0;
3414    if (iter->funcs->rewind) {
3415        iter->funcs->rewind(iter TSRMLS_CC);
3416        if (EG(exception)) {
3417            goto done;
3418        }
3419    }
3420
3421    while (iter->funcs->valid(iter TSRMLS_CC) == SUCCESS) {
3422        if (EG(exception)) {
3423            goto done;
3424        }
3425        if (apply_func(iter, puser TSRMLS_CC) == ZEND_HASH_APPLY_STOP || EG(exception)) {
3426            goto done;
3427        }
3428        iter->index++;
3429        iter->funcs->move_forward(iter TSRMLS_CC);
3430        if (EG(exception)) {
3431            goto done;
3432        }
3433    }
3434
3435done:
3436    if (iter) {
3437        iter->funcs->dtor(iter TSRMLS_CC);
3438    }
3439    return EG(exception) ? FAILURE : SUCCESS;
3440}
3441/* }}} */
3442
3443static int spl_iterator_to_array_apply(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ */
3444{
3445    zval **data, *return_value = (zval*)puser;
3446
3447    iter->funcs->get_current_data(iter, &data TSRMLS_CC);
3448    if (EG(exception)) {
3449        return ZEND_HASH_APPLY_STOP;
3450    }
3451    if (data == NULL || *data == NULL) {
3452        return ZEND_HASH_APPLY_STOP;
3453    }
3454    if (iter->funcs->get_current_key) {
3455        zval key;
3456        iter->funcs->get_current_key(iter, &key TSRMLS_CC);
3457        if (EG(exception)) {
3458            return ZEND_HASH_APPLY_STOP;
3459        }
3460        array_set_zval_key(Z_ARRVAL_P(return_value), &key, *data);
3461        zval_dtor(&key);
3462    } else {
3463        Z_ADDREF_PP(data);
3464        add_next_index_zval(return_value, *data);
3465    }
3466    return ZEND_HASH_APPLY_KEEP;
3467}
3468/* }}} */
3469
3470static int spl_iterator_to_values_apply(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ */
3471{
3472    zval **data, *return_value = (zval*)puser;
3473
3474    iter->funcs->get_current_data(iter, &data TSRMLS_CC);
3475    if (EG(exception)) {
3476        return ZEND_HASH_APPLY_STOP;
3477    }
3478    if (data == NULL || *data == NULL) {
3479        return ZEND_HASH_APPLY_STOP;
3480    }
3481    Z_ADDREF_PP(data);
3482    add_next_index_zval(return_value, *data);
3483    return ZEND_HASH_APPLY_KEEP;
3484}
3485/* }}} */
3486
3487/* {{{ proto array iterator_to_array(Traversable it [, bool use_keys = true])
3488   Copy the iterator into an array */
3489PHP_FUNCTION(iterator_to_array)
3490{
3491    zval  *obj;
3492    zend_bool use_keys = 1;
3493
3494    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &obj, zend_ce_traversable, &use_keys) == FAILURE) {
3495        RETURN_FALSE;
3496    }
3497
3498    array_init(return_value);
3499
3500    if (spl_iterator_apply(obj, use_keys ? spl_iterator_to_array_apply : spl_iterator_to_values_apply, (void*)return_value TSRMLS_CC) != SUCCESS) {
3501        zval_dtor(return_value);
3502        RETURN_NULL();
3503    }
3504} /* }}} */
3505
3506static int spl_iterator_count_apply(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ */
3507{
3508    (*(long*)puser)++;
3509    return ZEND_HASH_APPLY_KEEP;
3510}
3511/* }}} */
3512
3513/* {{{ proto int iterator_count(Traversable it)
3514   Count the elements in an iterator */
3515PHP_FUNCTION(iterator_count)
3516{
3517    zval  *obj;
3518    long  count = 0;
3519
3520    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &obj, zend_ce_traversable) == FAILURE) {
3521        RETURN_FALSE;
3522    }
3523
3524    if (spl_iterator_apply(obj, spl_iterator_count_apply, (void*)&count TSRMLS_CC) == SUCCESS) {
3525        RETURN_LONG(count);
3526    }
3527}
3528/* }}} */
3529
3530typedef struct {
3531    zval                   *obj;
3532    zval                   *args;
3533    long                   count;
3534    zend_fcall_info        fci;
3535    zend_fcall_info_cache  fcc;
3536} spl_iterator_apply_info;
3537
3538static int spl_iterator_func_apply(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ */
3539{
3540    zval *retval;
3541    spl_iterator_apply_info  *apply_info = (spl_iterator_apply_info*)puser;
3542    int result;
3543
3544    apply_info->count++;
3545    zend_fcall_info_call(&apply_info->fci, &apply_info->fcc, &retval, NULL TSRMLS_CC);
3546    if (retval) {
3547        result = zend_is_true(retval) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_STOP;
3548        zval_ptr_dtor(&retval);
3549    } else {
3550        result = ZEND_HASH_APPLY_STOP;
3551    }
3552    return result;
3553}
3554/* }}} */
3555
3556/* {{{ proto int iterator_apply(Traversable it, mixed function [, mixed params])
3557   Calls a function for every element in an iterator */
3558PHP_FUNCTION(iterator_apply)
3559{
3560    spl_iterator_apply_info  apply_info;
3561
3562    apply_info.args = NULL;
3563    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) {
3564        return;
3565    }
3566
3567    apply_info.count = 0;
3568    zend_fcall_info_args(&apply_info.fci, apply_info.args TSRMLS_CC);
3569    if (spl_iterator_apply(apply_info.obj, spl_iterator_func_apply, (void*)&apply_info TSRMLS_CC) == SUCCESS) {
3570        RETVAL_LONG(apply_info.count);
3571    } else {
3572        RETVAL_FALSE;
3573    }
3574    zend_fcall_info_args(&apply_info.fci, NULL TSRMLS_CC);
3575}
3576/* }}} */
3577
3578static const zend_function_entry spl_funcs_OuterIterator[] = {
3579    SPL_ABSTRACT_ME(OuterIterator, getInnerIterator,   arginfo_recursive_it_void)
3580    PHP_FE_END
3581};
3582
3583static const zend_function_entry spl_funcs_Countable[] = {
3584    SPL_ABSTRACT_ME(Countable, count,   arginfo_recursive_it_void)
3585    PHP_FE_END
3586};
3587
3588/* {{{ PHP_MINIT_FUNCTION(spl_iterators)
3589 */
3590PHP_MINIT_FUNCTION(spl_iterators)
3591{
3592    REGISTER_SPL_INTERFACE(RecursiveIterator);
3593    REGISTER_SPL_ITERATOR(RecursiveIterator);
3594
3595    REGISTER_SPL_STD_CLASS_EX(RecursiveIteratorIterator, spl_RecursiveIteratorIterator_new, spl_funcs_RecursiveIteratorIterator);
3596    REGISTER_SPL_ITERATOR(RecursiveIteratorIterator);
3597
3598    memcpy(&spl_handlers_rec_it_it, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
3599    spl_handlers_rec_it_it.get_method = spl_recursive_it_get_method;
3600    spl_handlers_rec_it_it.clone_obj = NULL;
3601
3602    memcpy(&spl_handlers_dual_it, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
3603    spl_handlers_dual_it.get_method = spl_dual_it_get_method;
3604    /*spl_handlers_dual_it.call_method = spl_dual_it_call_method;*/
3605    spl_handlers_dual_it.clone_obj = NULL;
3606
3607    spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator;
3608    spl_ce_RecursiveIteratorIterator->iterator_funcs.funcs = &spl_recursive_it_iterator_funcs;
3609
3610    REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "LEAVES_ONLY",     RIT_LEAVES_ONLY);
3611    REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "SELF_FIRST",      RIT_SELF_FIRST);
3612    REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "CHILD_FIRST",     RIT_CHILD_FIRST);
3613    REGISTER_SPL_CLASS_CONST_LONG(RecursiveIteratorIterator, "CATCH_GET_CHILD", RIT_CATCH_GET_CHILD);
3614
3615    REGISTER_SPL_INTERFACE(OuterIterator);
3616    REGISTER_SPL_ITERATOR(OuterIterator);
3617
3618    REGISTER_SPL_STD_CLASS_EX(IteratorIterator, spl_dual_it_new, spl_funcs_IteratorIterator);
3619    REGISTER_SPL_ITERATOR(IteratorIterator);
3620    REGISTER_SPL_IMPLEMENTS(IteratorIterator, OuterIterator);
3621
3622    REGISTER_SPL_SUB_CLASS_EX(FilterIterator, IteratorIterator, spl_dual_it_new, spl_funcs_FilterIterator);
3623    spl_ce_FilterIterator->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
3624
3625    REGISTER_SPL_SUB_CLASS_EX(RecursiveFilterIterator, FilterIterator, spl_dual_it_new, spl_funcs_RecursiveFilterIterator);
3626    REGISTER_SPL_IMPLEMENTS(RecursiveFilterIterator, RecursiveIterator);
3627
3628    REGISTER_SPL_SUB_CLASS_EX(CallbackFilterIterator, FilterIterator, spl_dual_it_new, spl_funcs_CallbackFilterIterator);
3629
3630    REGISTER_SPL_SUB_CLASS_EX(RecursiveCallbackFilterIterator, CallbackFilterIterator, spl_dual_it_new, spl_funcs_RecursiveCallbackFilterIterator);
3631    REGISTER_SPL_IMPLEMENTS(RecursiveCallbackFilterIterator, RecursiveIterator);
3632
3633
3634    REGISTER_SPL_SUB_CLASS_EX(ParentIterator, RecursiveFilterIterator, spl_dual_it_new, spl_funcs_ParentIterator);
3635
3636    REGISTER_SPL_INTERFACE(Countable);
3637    REGISTER_SPL_INTERFACE(SeekableIterator);
3638    REGISTER_SPL_ITERATOR(SeekableIterator);
3639
3640    REGISTER_SPL_SUB_CLASS_EX(LimitIterator, IteratorIterator, spl_dual_it_new, spl_funcs_LimitIterator);
3641
3642    REGISTER_SPL_SUB_CLASS_EX(CachingIterator, IteratorIterator, spl_dual_it_new, spl_funcs_CachingIterator);
3643    REGISTER_SPL_IMPLEMENTS(CachingIterator, ArrayAccess);
3644    REGISTER_SPL_IMPLEMENTS(CachingIterator, Countable);
3645
3646    REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "CALL_TOSTRING",        CIT_CALL_TOSTRING);
3647    REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "CATCH_GET_CHILD",      CIT_CATCH_GET_CHILD);
3648    REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_KEY",     CIT_TOSTRING_USE_KEY);
3649    REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_CURRENT", CIT_TOSTRING_USE_CURRENT);
3650    REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_INNER",   CIT_TOSTRING_USE_INNER);
3651    REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "FULL_CACHE",           CIT_FULL_CACHE);
3652
3653    REGISTER_SPL_SUB_CLASS_EX(RecursiveCachingIterator, CachingIterator, spl_dual_it_new, spl_funcs_RecursiveCachingIterator);
3654    REGISTER_SPL_IMPLEMENTS(RecursiveCachingIterator, RecursiveIterator);
3655
3656    REGISTER_SPL_SUB_CLASS_EX(NoRewindIterator, IteratorIterator, spl_dual_it_new, spl_funcs_NoRewindIterator);
3657
3658    REGISTER_SPL_SUB_CLASS_EX(AppendIterator, IteratorIterator, spl_dual_it_new, spl_funcs_AppendIterator);
3659
3660    REGISTER_SPL_IMPLEMENTS(RecursiveIteratorIterator, OuterIterator);
3661
3662    REGISTER_SPL_SUB_CLASS_EX(InfiniteIterator, IteratorIterator, spl_dual_it_new, spl_funcs_InfiniteIterator);
3663#if HAVE_PCRE || HAVE_BUNDLED_PCRE
3664    REGISTER_SPL_SUB_CLASS_EX(RegexIterator, FilterIterator, spl_dual_it_new, spl_funcs_RegexIterator);
3665    REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "USE_KEY",     REGIT_USE_KEY);
3666    REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "MATCH",       REGIT_MODE_MATCH);
3667    REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "GET_MATCH",   REGIT_MODE_GET_MATCH);
3668    REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "ALL_MATCHES", REGIT_MODE_ALL_MATCHES);
3669    REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "SPLIT",       REGIT_MODE_SPLIT);
3670    REGISTER_SPL_CLASS_CONST_LONG(RegexIterator, "REPLACE",     REGIT_MODE_REPLACE);
3671    REGISTER_SPL_PROPERTY(RegexIterator, "replacement", 0);
3672    REGISTER_SPL_SUB_CLASS_EX(RecursiveRegexIterator, RegexIterator, spl_dual_it_new, spl_funcs_RecursiveRegexIterator);
3673    REGISTER_SPL_IMPLEMENTS(RecursiveRegexIterator, RecursiveIterator);
3674#else
3675    spl_ce_RegexIterator = NULL;
3676    spl_ce_RecursiveRegexIterator = NULL;
3677#endif
3678
3679    REGISTER_SPL_STD_CLASS_EX(EmptyIterator, NULL, spl_funcs_EmptyIterator);
3680    REGISTER_SPL_ITERATOR(EmptyIterator);
3681
3682    REGISTER_SPL_SUB_CLASS_EX(RecursiveTreeIterator, RecursiveIteratorIterator, spl_RecursiveTreeIterator_new, spl_funcs_RecursiveTreeIterator);
3683    REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "BYPASS_CURRENT",      RTIT_BYPASS_CURRENT);
3684    REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "BYPASS_KEY",          RTIT_BYPASS_KEY);
3685    REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_LEFT",         0);
3686    REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_MID_HAS_NEXT", 1);
3687    REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_MID_LAST",     2);
3688    REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_END_HAS_NEXT", 3);
3689    REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_END_LAST",     4);
3690    REGISTER_SPL_CLASS_CONST_LONG(RecursiveTreeIterator, "PREFIX_RIGHT",        5);
3691
3692    return SUCCESS;
3693}
3694/* }}} */
3695
3696/*
3697 * Local variables:
3698 * tab-width: 4
3699 * c-basic-offset: 4
3700 * End:
3701 * vim600: fdm=marker
3702 * vim: noet sw=4 ts=4
3703 */
3704