root/ext/spl/spl_iterators.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. spl_recursive_it_from_obj
  2. spl_recursive_it_dtor
  3. spl_recursive_it_valid_ex
  4. spl_recursive_it_valid
  5. spl_recursive_it_get_current_data
  6. spl_recursive_it_get_current_key
  7. spl_recursive_it_move_forward_ex
  8. spl_recursive_it_rewind_ex
  9. spl_recursive_it_move_forward
  10. spl_recursive_it_rewind
  11. spl_recursive_it_get_iterator
  12. spl_recursive_it_it_construct
  13. SPL_METHOD
  14. SPL_METHOD
  15. SPL_METHOD
  16. SPL_METHOD
  17. SPL_METHOD
  18. SPL_METHOD
  19. SPL_METHOD
  20. SPL_METHOD
  21. SPL_METHOD
  22. SPL_METHOD
  23. SPL_METHOD
  24. SPL_METHOD
  25. SPL_METHOD
  26. SPL_METHOD
  27. SPL_METHOD
  28. SPL_METHOD
  29. SPL_METHOD
  30. SPL_METHOD
  31. spl_recursive_it_get_method
  32. spl_RecursiveIteratorIterator_dtor
  33. spl_RecursiveIteratorIterator_free_storage
  34. spl_RecursiveIteratorIterator_new_ex
  35. spl_RecursiveIteratorIterator_new
  36. spl_RecursiveTreeIterator_new
  37. spl_recursive_tree_iterator_get_prefix
  38. spl_recursive_tree_iterator_get_entry
  39. spl_recursive_tree_iterator_get_postfix
  40. SPL_METHOD
  41. SPL_METHOD
  42. SPL_METHOD
  43. SPL_METHOD
  44. SPL_METHOD
  45. SPL_METHOD
  46. SPL_METHOD
  47. SPL_METHOD
  48. spl_dual_it_gets_implemented
  49. spl_dual_it_get_method
  50. spl_dual_it_call_method
  51. spl_cit_check_flags
  52. spl_dual_it_construct
  53. SPL_METHOD
  54. SPL_METHOD
  55. SPL_METHOD
  56. spl_dual_it_require
  57. spl_dual_it_free
  58. spl_dual_it_rewind
  59. spl_dual_it_valid
  60. spl_dual_it_fetch
  61. spl_dual_it_next
  62. SPL_METHOD
  63. SPL_METHOD
  64. SPL_METHOD
  65. SPL_METHOD
  66. SPL_METHOD
  67. spl_filter_it_fetch
  68. spl_filter_it_rewind
  69. spl_filter_it_next
  70. SPL_METHOD
  71. SPL_METHOD
  72. SPL_METHOD
  73. SPL_METHOD
  74. SPL_METHOD
  75. SPL_METHOD
  76. SPL_METHOD
  77. SPL_METHOD
  78. SPL_METHOD
  79. SPL_METHOD
  80. SPL_METHOD
  81. SPL_METHOD
  82. SPL_METHOD
  83. SPL_METHOD
  84. SPL_METHOD
  85. SPL_METHOD
  86. SPL_METHOD
  87. SPL_METHOD
  88. SPL_METHOD
  89. SPL_METHOD
  90. SPL_METHOD
  91. spl_dual_it_dtor
  92. spl_dual_it_free_storage
  93. spl_dual_it_new
  94. spl_limit_it_valid
  95. spl_limit_it_seek
  96. SPL_METHOD
  97. SPL_METHOD
  98. SPL_METHOD
  99. SPL_METHOD
  100. SPL_METHOD
  101. SPL_METHOD
  102. spl_caching_it_valid
  103. spl_caching_it_has_next
  104. spl_caching_it_next
  105. spl_caching_it_rewind
  106. SPL_METHOD
  107. SPL_METHOD
  108. SPL_METHOD
  109. SPL_METHOD
  110. SPL_METHOD
  111. SPL_METHOD
  112. SPL_METHOD
  113. SPL_METHOD
  114. SPL_METHOD
  115. SPL_METHOD
  116. SPL_METHOD
  117. SPL_METHOD
  118. SPL_METHOD
  119. SPL_METHOD
  120. SPL_METHOD
  121. SPL_METHOD
  122. SPL_METHOD
  123. SPL_METHOD
  124. SPL_METHOD
  125. SPL_METHOD
  126. SPL_METHOD
  127. SPL_METHOD
  128. SPL_METHOD
  129. SPL_METHOD
  130. SPL_METHOD
  131. SPL_METHOD
  132. SPL_METHOD
  133. SPL_METHOD
  134. SPL_METHOD
  135. SPL_METHOD
  136. SPL_METHOD
  137. spl_append_it_next_iterator
  138. spl_append_it_fetch
  139. spl_append_it_next
  140. SPL_METHOD
  141. SPL_METHOD
  142. SPL_METHOD
  143. SPL_METHOD
  144. SPL_METHOD
  145. SPL_METHOD
  146. SPL_METHOD
  147. spl_iterator_apply
  148. spl_iterator_to_array_apply
  149. spl_iterator_to_values_apply
  150. PHP_FUNCTION
  151. spl_iterator_count_apply
  152. PHP_FUNCTION
  153. spl_iterator_func_apply
  154. PHP_FUNCTION
  155. PHP_MINIT_FUNCTION

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

/* [<][>][^][v][top][bottom][index][help] */