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