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