root/ext/spl/spl_iterators.c

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

DEFINITIONS

This source file includes following definitions.
  1. spl_recursive_it_dtor
  2. spl_recursive_it_valid_ex
  3. spl_recursive_it_valid
  4. spl_recursive_it_get_current_data
  5. spl_recursive_it_get_current_key
  6. spl_recursive_it_move_forward_ex
  7. spl_recursive_it_rewind_ex
  8. spl_recursive_it_move_forward
  9. spl_recursive_it_rewind
  10. spl_recursive_it_get_iterator
  11. spl_recursive_it_it_construct
  12. SPL_METHOD
  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_recursive_it_get_method
  31. spl_RecursiveIteratorIterator_dtor
  32. spl_RecursiveIteratorIterator_free_storage
  33. spl_RecursiveIteratorIterator_new_ex
  34. spl_RecursiveIteratorIterator_new
  35. spl_RecursiveTreeIterator_new
  36. spl_recursive_tree_iterator_get_prefix
  37. spl_recursive_tree_iterator_get_entry
  38. spl_recursive_tree_iterator_get_postfix
  39. SPL_METHOD
  40. SPL_METHOD
  41. SPL_METHOD
  42. SPL_METHOD
  43. SPL_METHOD
  44. SPL_METHOD
  45. SPL_METHOD
  46. SPL_METHOD
  47. spl_dual_it_gets_implemented
  48. spl_dual_it_get_method
  49. spl_dual_it_call_method
  50. spl_cit_check_flags
  51. spl_dual_it_construct
  52. SPL_METHOD
  53. SPL_METHOD
  54. SPL_METHOD
  55. spl_dual_it_require
  56. spl_dual_it_free
  57. spl_dual_it_rewind
  58. spl_dual_it_valid
  59. spl_dual_it_fetch
  60. spl_dual_it_next
  61. SPL_METHOD
  62. SPL_METHOD
  63. SPL_METHOD
  64. SPL_METHOD
  65. SPL_METHOD
  66. spl_filter_it_fetch
  67. spl_filter_it_rewind
  68. spl_filter_it_next
  69. SPL_METHOD
  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_dual_it_dtor
  91. spl_dual_it_free_storage
  92. spl_dual_it_new
  93. spl_limit_it_valid
  94. spl_limit_it_seek
  95. SPL_METHOD
  96. SPL_METHOD
  97. SPL_METHOD
  98. SPL_METHOD
  99. SPL_METHOD
  100. SPL_METHOD
  101. spl_caching_it_valid
  102. spl_caching_it_has_next
  103. spl_caching_it_next
  104. spl_caching_it_rewind
  105. SPL_METHOD
  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_append_it_next_iterator
  137. spl_append_it_fetch
  138. spl_append_it_next
  139. SPL_METHOD
  140. SPL_METHOD
  141. SPL_METHOD
  142. SPL_METHOD
  143. SPL_METHOD
  144. SPL_METHOD
  145. SPL_METHOD
  146. spl_iterator_apply
  147. spl_iterator_to_array_apply
  148. spl_iterator_to_values_apply
  149. PHP_FUNCTION
  150. spl_iterator_count_apply
  151. PHP_FUNCTION
  152. spl_iterator_func_apply
  153. PHP_FUNCTION
  154. PHP_MINIT_FUNCTION

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

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