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