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