Coverage Report

Created: 2026-07-25 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/spl/spl_iterators.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Marcus Boerger <helly@php.net>                              |
12
   +----------------------------------------------------------------------+
13
 */
14
15
#ifdef HAVE_CONFIG_H
16
# include "config.h"
17
#endif
18
19
#include "php.h"
20
#include "zend_exceptions.h"
21
#include "zend_interfaces.h"
22
#include "ext/pcre/php_pcre.h"
23
24
#include "spl_iterators.h"
25
#include "spl_iterators_arginfo.h"
26
#include "spl_array.h" /* For spl_ce_ArrayIterator */
27
#include "spl_exceptions.h"
28
#include "zend_smart_str.h"
29
30
#ifdef accept
31
#undef accept
32
#endif
33
34
PHPAPI zend_class_entry *spl_ce_RecursiveIterator;
35
PHPAPI zend_class_entry *spl_ce_RecursiveIteratorIterator;
36
PHPAPI zend_class_entry *spl_ce_FilterIterator;
37
PHPAPI zend_class_entry *spl_ce_CallbackFilterIterator;
38
PHPAPI zend_class_entry *spl_ce_RecursiveFilterIterator;
39
PHPAPI zend_class_entry *spl_ce_RecursiveCallbackFilterIterator;
40
PHPAPI zend_class_entry *spl_ce_ParentIterator;
41
PHPAPI zend_class_entry *spl_ce_SeekableIterator;
42
PHPAPI zend_class_entry *spl_ce_LimitIterator;
43
PHPAPI zend_class_entry *spl_ce_CachingIterator;
44
PHPAPI zend_class_entry *spl_ce_RecursiveCachingIterator;
45
PHPAPI zend_class_entry *spl_ce_OuterIterator;
46
PHPAPI zend_class_entry *spl_ce_IteratorIterator;
47
PHPAPI zend_class_entry *spl_ce_NoRewindIterator;
48
PHPAPI zend_class_entry *spl_ce_InfiniteIterator;
49
PHPAPI zend_class_entry *spl_ce_EmptyIterator;
50
PHPAPI zend_class_entry *spl_ce_AppendIterator;
51
PHPAPI zend_class_entry *spl_ce_RegexIterator;
52
PHPAPI zend_class_entry *spl_ce_RecursiveRegexIterator;
53
PHPAPI zend_class_entry *spl_ce_RecursiveTreeIterator;
54
55
typedef enum {
56
  RS_NEXT  = 0,
57
  RS_TEST  = 1,
58
  RS_SELF  = 2,
59
  RS_CHILD = 3,
60
  RS_START = 4
61
} RecursiveIteratorState;
62
63
typedef struct _spl_sub_iterator {
64
  zend_object_iterator    *iterator;
65
  zval                    zobject;
66
  zend_class_entry        *ce;
67
  RecursiveIteratorState  state;
68
  zend_function           *haschildren;
69
  zend_function           *getchildren;
70
} spl_sub_iterator;
71
72
typedef struct _spl_recursive_it_object {
73
  spl_sub_iterator         *iterators;
74
  int                      level;
75
  RecursiveIteratorMode    mode;
76
  int                      flags;
77
  int                      max_depth;
78
  bool                in_iteration;
79
  zend_function            *beginIteration;
80
  zend_function            *endIteration;
81
  zend_function            *callHasChildren;
82
  zend_function            *callGetChildren;
83
  zend_function            *beginChildren;
84
  zend_function            *endChildren;
85
  zend_function            *nextElement;
86
  zend_class_entry         *ce;
87
  zend_string              *prefix[6];
88
  zend_string              *postfix[1];
89
  zend_object              std;
90
} spl_recursive_it_object;
91
92
typedef struct _spl_recursive_it_iterator {
93
  zend_object_iterator   intern;
94
} spl_recursive_it_iterator;
95
96
typedef struct _spl_dual_it_object {
97
  struct {
98
    zval                 zobject;
99
    zend_class_entry     *ce;
100
    zend_object          *object;
101
    zend_object_iterator *iterator;
102
  } inner;
103
  struct {
104
    zval                 data;
105
    zval                 key;
106
    zend_long            pos;
107
  } current;
108
  dual_it_type             dit_type;
109
  union {
110
    struct {
111
      zend_long             offset;
112
      zend_long             count;
113
    } limit;
114
    struct {
115
      zend_long             flags; /* CIT_* */
116
      zend_string          *zstr;
117
      zval             zchildren;
118
      zval             zcache;
119
    } caching;
120
    struct {
121
      zval                  zarrayit;
122
      zend_object_iterator *iterator;
123
    } append;
124
    struct {
125
      zend_long        flags;
126
      zend_long        preg_flags;
127
      pcre_cache_entry *pce;
128
      zend_string      *regex;
129
      regex_mode       mode;
130
    } regex;
131
    zend_fcall_info_cache callback_filter;
132
  } u;
133
  zend_object              std;
134
} spl_dual_it_object;
135
136
static zend_object_handlers spl_handlers_rec_it_it;
137
static zend_object_handlers spl_handlers_dual_it;
138
139
5.34k
#define spl_recursive_it_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_recursive_it_object, std)
140
141
115
#define Z_SPLRECURSIVE_IT_P(zv)  spl_recursive_it_from_obj(Z_OBJ_P((zv)))
142
143
4.66k
#define spl_dual_it_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_dual_it_object, std)
144
145
904
#define Z_SPLDUAL_IT_P(zv)  spl_dual_it_from_obj(Z_OBJ_P((zv)))
146
147
#define SPL_FETCH_AND_CHECK_DUAL_IT(var, objzval)                         \
148
736
  do {                                             \
149
736
    spl_dual_it_object *it = Z_SPLDUAL_IT_P(objzval);                    \
150
736
    if (it->dit_type == DIT_Unknown) {                             \
151
0
      zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");   \
152
0
      RETURN_THROWS();                                       \
153
0
    }                                            \
154
736
    (var) = it;                                       \
155
736
  } while (0)
156
157
#define SPL_FETCH_SUB_ELEMENT(var, object, element) \
158
42
  do { \
159
42
    if(!(object)->iterators) { \
160
0
      zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called"); \
161
0
      return; \
162
0
    } \
163
42
    (var) = (object)->iterators[(object)->level].element; \
164
42
  } while (0)
165
166
#define SPL_FETCH_SUB_ELEMENT_ADDR(var, object, element) \
167
0
  do { \
168
0
    if(!(object)->iterators) { \
169
0
      zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called"); \
170
0
      RETURN_THROWS(); \
171
0
    } \
172
0
    (var) = &(object)->iterators[(object)->level].element; \
173
0
  } while (0)
174
175
42
#define SPL_FETCH_SUB_ITERATOR(var, object) SPL_FETCH_SUB_ELEMENT(var, object, iterator)
176
177
178
static void spl_recursive_it_dtor(zend_object_iterator *_iter)
179
11
{
180
11
  spl_recursive_it_iterator *iter   = (spl_recursive_it_iterator*)_iter;
181
11
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(&iter->intern.data);
182
11
  zend_object_iterator      *sub_iter;
183
184
11
  if (object->iterators) {
185
11
    while (object->level > 0) {
186
0
      if (!Z_ISUNDEF(object->iterators[object->level].zobject)) {
187
0
        sub_iter = object->iterators[object->level].iterator;
188
0
        zend_iterator_dtor(sub_iter);
189
0
        zval_ptr_dtor(&object->iterators[object->level].zobject);
190
0
      }
191
0
      object->level--;
192
0
    }
193
11
    object->iterators = erealloc(object->iterators, sizeof(spl_sub_iterator));
194
11
    object->level = 0;
195
11
  }
196
197
11
  zval_ptr_dtor(&iter->intern.data);
198
11
}
199
200
static zend_result spl_recursive_it_valid_ex(spl_recursive_it_object *object, zval *zthis)
201
31
{
202
31
  zend_object_iterator      *sub_iter;
203
31
  int                       level = object->level;
204
205
31
  if(!object->iterators) {
206
0
    return FAILURE;
207
0
  }
208
42
  while (level >=0) {
209
31
    sub_iter = object->iterators[level].iterator;
210
31
    if (sub_iter->funcs->valid(sub_iter) == SUCCESS) {
211
20
      return SUCCESS;
212
20
    }
213
11
    level--;
214
11
  }
215
11
  if (object->endIteration && object->in_iteration) {
216
0
    zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->endIteration, "endIteration", NULL);
217
0
  }
218
11
  object->in_iteration = false;
219
11
  return FAILURE;
220
31
}
221
222
static zend_result spl_recursive_it_valid(zend_object_iterator *iter)
223
31
{
224
31
  return spl_recursive_it_valid_ex(Z_SPLRECURSIVE_IT_P(&iter->data), &iter->data);
225
31
}
226
227
static zval *spl_recursive_it_get_current_data(zend_object_iterator *iter)
228
20
{
229
20
  spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(&iter->data);
230
20
  zend_object_iterator *sub_iter = object->iterators[object->level].iterator;
231
232
20
  return sub_iter->funcs->get_current_data(sub_iter);
233
20
}
234
235
static void spl_recursive_it_get_current_key(zend_object_iterator *iter, zval *key)
236
0
{
237
0
  spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(&iter->data);
238
0
  zend_object_iterator *sub_iter = object->iterators[object->level].iterator;
239
240
0
  if (sub_iter->funcs->get_current_key) {
241
0
    sub_iter->funcs->get_current_key(sub_iter, key);
242
0
  } else {
243
0
    ZVAL_LONG(key, iter->index);
244
0
  }
245
0
}
246
247
static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zval *zthis)
248
31
{
249
31
  zend_object_iterator      *iterator;
250
31
  zend_class_entry          *ce;
251
31
  zval                      retval, child;
252
31
  zend_object_iterator      *sub_iter;
253
31
  zend_object               *sub_object;
254
31
  uint32_t                  prev_level;
255
31
  zend_result               valid_result;
256
31
  bool                      reentered;
257
258
31
  SPL_FETCH_SUB_ITERATOR(iterator, object);
259
260
40
  while (!EG(exception)) {
261
58
next_step:
262
58
    iterator = object->iterators[object->level].iterator;
263
58
    switch (object->iterators[object->level].state) {
264
20
      case RS_NEXT:
265
20
        iterator->funcs->move_forward(iterator);
266
20
        if (EG(exception)) {
267
0
          if (!(object->flags & RIT_CATCH_GET_CHILD)) {
268
0
            return;
269
0
          } else {
270
0
            zend_clear_exception();
271
0
          }
272
0
        }
273
20
        iterator = object->iterators[object->level].iterator;
274
20
        ZEND_FALLTHROUGH;
275
40
      case RS_START:
276
40
        sub_object = Z_OBJ(object->iterators[object->level].zobject);
277
40
        prev_level = object->level;
278
40
        GC_ADDREF(sub_object);
279
40
        valid_result = iterator->funcs->valid(iterator);
280
40
        reentered = object->level != prev_level
281
40
          || object->iterators[object->level].iterator != iterator;
282
40
        OBJ_RELEASE(sub_object);
283
40
        if (reentered) {
284
0
          return;
285
0
        }
286
40
        if (valid_result == FAILURE) {
287
20
          break;
288
20
        }
289
20
        object->iterators[object->level].state = RS_TEST;
290
        /* break; */
291
        /* TODO: Check this is correct */
292
20
        ZEND_FALLTHROUGH;
293
20
      case RS_TEST:
294
20
        if (object->callHasChildren) {
295
0
          zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->callHasChildren, "callHasChildren", &retval);
296
20
        } else {
297
20
          zend_class_entry *ce = object->iterators[object->level].ce;
298
20
          zend_object *obj = Z_OBJ(object->iterators[object->level].zobject);
299
20
          zend_function **cache = &object->iterators[object->level].haschildren;
300
301
20
          zend_call_method_with_0_params(obj, ce, cache, "haschildren", &retval);
302
20
        }
303
20
        if (EG(exception)) {
304
0
          if (!(object->flags & RIT_CATCH_GET_CHILD)) {
305
0
            object->iterators[object->level].state = RS_NEXT;
306
0
            return;
307
0
          } else {
308
0
            zend_clear_exception();
309
0
          }
310
0
        }
311
20
        if (Z_TYPE(retval) != IS_UNDEF) {
312
20
          bool has_children = zend_is_true(&retval);
313
20
          zval_ptr_dtor(&retval);
314
20
          if (has_children) {
315
9
            if (object->max_depth == -1 || object->max_depth > object->level) {
316
9
              switch (object->mode) {
317
0
              case RIT_LEAVES_ONLY:
318
0
              case RIT_CHILD_FIRST:
319
0
                object->iterators[object->level].state = RS_CHILD;
320
0
                goto next_step;
321
9
              case RIT_SELF_FIRST:
322
9
                object->iterators[object->level].state = RS_SELF;
323
9
                goto next_step;
324
9
              }
325
9
            } else {
326
              /* do not recurse into */
327
0
              if (object->mode == RIT_LEAVES_ONLY) {
328
                /* this is not a leave, so skip it */
329
0
                object->iterators[object->level].state = RS_NEXT;
330
0
                goto next_step;
331
0
              }
332
0
            }
333
9
          }
334
20
        }
335
11
        if (object->nextElement) {
336
0
          zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->nextElement, "nextelement", NULL);
337
0
        }
338
11
        object->iterators[object->level].state = RS_NEXT;
339
11
        if (EG(exception)) {
340
0
          if (!(object->flags & RIT_CATCH_GET_CHILD)) {
341
0
            return;
342
0
          } else {
343
0
            zend_clear_exception();
344
0
          }
345
0
        }
346
11
        return /* self */;
347
11
      case RS_SELF:
348
9
        if (object->nextElement && (object->mode == RIT_SELF_FIRST || object->mode == RIT_CHILD_FIRST)) {
349
0
          zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->nextElement, "nextelement", NULL);
350
0
        }
351
9
        if (object->mode == RIT_SELF_FIRST) {
352
9
          object->iterators[object->level].state = RS_CHILD;
353
9
        } else {
354
0
          object->iterators[object->level].state = RS_NEXT;
355
0
        }
356
9
        return /* self */;
357
9
      case RS_CHILD:
358
9
        if (object->callGetChildren) {
359
0
          zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->callGetChildren, "callGetChildren", &child);
360
9
        } else {
361
9
          zend_class_entry *ce = object->iterators[object->level].ce;
362
9
          zend_object *obj = Z_OBJ(object->iterators[object->level].zobject);
363
9
          zend_function **cache = &object->iterators[object->level].getchildren;
364
365
9
          zend_call_method_with_0_params(obj, ce, cache, "getchildren", &child);
366
9
        }
367
368
9
        if (EG(exception)) {
369
0
          if (!(object->flags & RIT_CATCH_GET_CHILD)) {
370
0
            return;
371
0
          } else {
372
0
            zend_clear_exception();
373
0
            zval_ptr_dtor(&child);
374
0
            object->iterators[object->level].state = RS_NEXT;
375
0
            goto next_step;
376
0
          }
377
0
        }
378
379
9
        if (Z_TYPE(child) == IS_UNDEF || Z_TYPE(child) != IS_OBJECT ||
380
9
            !((ce = Z_OBJCE(child)) && instanceof_function(ce, spl_ce_RecursiveIterator))) {
381
0
          zval_ptr_dtor(&child);
382
0
          zend_throw_exception(spl_ce_UnexpectedValueException, "Objects returned by RecursiveIterator::getChildren() must implement RecursiveIterator", 0);
383
0
          return;
384
0
        }
385
386
9
        if (object->mode == RIT_CHILD_FIRST) {
387
0
          object->iterators[object->level].state = RS_SELF;
388
9
        } else {
389
9
          object->iterators[object->level].state = RS_NEXT;
390
9
        }
391
9
        object->iterators = erealloc(object->iterators, sizeof(spl_sub_iterator) * (++object->level+1));
392
9
        sub_iter = ce->get_iterator(ce, &child, 0);
393
9
        ZVAL_COPY_VALUE(&object->iterators[object->level].zobject, &child);
394
9
        object->iterators[object->level].iterator = sub_iter;
395
9
        object->iterators[object->level].ce = ce;
396
9
        object->iterators[object->level].state = RS_START;
397
9
        if (object->level > 0
398
9
         && object->iterators[object->level - 1].ce == 0) {
399
0
          object->iterators[object->level].haschildren =
400
0
            object->iterators[object->level - 1].haschildren;
401
0
          object->iterators[object->level].getchildren =
402
0
            object->iterators[object->level - 1].getchildren;
403
9
        } else {
404
9
          object->iterators[object->level].haschildren = NULL;
405
9
          object->iterators[object->level].getchildren = NULL;
406
9
        }
407
9
        if (sub_iter->funcs->rewind) {
408
9
          sub_iter->funcs->rewind(sub_iter);
409
9
        }
410
9
        if (object->beginChildren) {
411
0
          zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->beginChildren, "beginchildren", NULL);
412
0
          if (EG(exception)) {
413
0
            if (!(object->flags & RIT_CATCH_GET_CHILD)) {
414
0
              return;
415
0
            } else {
416
0
              zend_clear_exception();
417
0
            }
418
0
          }
419
0
        }
420
9
        goto next_step;
421
58
    }
422
    /* no more elements */
423
20
    if (object->level > 0) {
424
9
      if (object->endChildren) {
425
0
        zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->endChildren, "endchildren", NULL);
426
0
        if (EG(exception)) {
427
0
          if (!(object->flags & RIT_CATCH_GET_CHILD)) {
428
0
            return;
429
0
          } else {
430
0
            zend_clear_exception();
431
0
          }
432
0
        }
433
0
      }
434
9
      if (object->level > 0 && object->iterators[object->level].iterator == iterator) {
435
9
        zval garbage;
436
9
        ZVAL_COPY_VALUE(&garbage, &object->iterators[object->level].zobject);
437
9
        ZVAL_UNDEF(&object->iterators[object->level].zobject);
438
9
        zval_ptr_dtor(&garbage);
439
9
        zend_iterator_dtor(iterator);
440
9
        object->level--;
441
9
      }
442
11
    } else {
443
11
      return; /* done completeley */
444
11
    }
445
20
  }
446
31
}
447
448
static void spl_recursive_it_rewind_ex(spl_recursive_it_object *object, zval *zthis)
449
11
{
450
11
  zend_object_iterator *sub_iter;
451
452
11
  SPL_FETCH_SUB_ITERATOR(sub_iter, object);
453
454
11
  while (object->level) {
455
0
    sub_iter = object->iterators[object->level].iterator;
456
0
    zend_iterator_dtor(sub_iter);
457
0
    zval_ptr_dtor(&object->iterators[object->level--].zobject);
458
0
    if (!EG(exception) && (!object->endChildren || object->endChildren->common.scope != spl_ce_RecursiveIteratorIterator)) {
459
0
      zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->endChildren, "endchildren", NULL);
460
0
    }
461
0
  }
462
11
  object->iterators = erealloc(object->iterators, sizeof(spl_sub_iterator));
463
11
  object->iterators[0].state = RS_START;
464
11
  sub_iter = object->iterators[0].iterator;
465
11
  if (sub_iter->funcs->rewind) {
466
11
    sub_iter->funcs->rewind(sub_iter);
467
11
  }
468
11
  if (!EG(exception) && object->beginIteration && !object->in_iteration) {
469
0
    zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->beginIteration, "beginIteration", NULL);
470
0
  }
471
11
  object->in_iteration = true;
472
11
  spl_recursive_it_move_forward_ex(object, zthis);
473
11
}
474
475
static void spl_recursive_it_move_forward(zend_object_iterator *iter)
476
20
{
477
20
  spl_recursive_it_move_forward_ex(Z_SPLRECURSIVE_IT_P(&iter->data), &iter->data);
478
20
}
479
480
static void spl_recursive_it_rewind(zend_object_iterator *iter)
481
11
{
482
11
  spl_recursive_it_rewind_ex(Z_SPLRECURSIVE_IT_P(&iter->data), &iter->data);
483
11
}
484
485
static const zend_object_iterator_funcs spl_recursive_it_iterator_funcs = {
486
  spl_recursive_it_dtor,
487
  spl_recursive_it_valid,
488
  spl_recursive_it_get_current_data,
489
  spl_recursive_it_get_current_key,
490
  spl_recursive_it_move_forward,
491
  spl_recursive_it_rewind,
492
  NULL,
493
  NULL, /* get_gc */
494
};
495
496
static zend_object_iterator *spl_recursive_it_get_iterator(zend_class_entry *ce, zval *zobject, int by_ref)
497
11
{
498
11
  if (by_ref) {
499
0
    zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
500
0
    return NULL;
501
0
  }
502
503
11
  spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(zobject);
504
11
  if (object->iterators == NULL) {
505
0
    zend_throw_error(NULL, "Object is not initialized");
506
0
    return NULL;
507
0
  }
508
509
11
  spl_recursive_it_iterator *iterator = emalloc(sizeof(spl_recursive_it_iterator));
510
11
  zend_iterator_init((zend_object_iterator*)iterator);
511
512
11
  ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(zobject));
513
11
  iterator->intern.funcs = &spl_recursive_it_iterator_funcs;
514
11
  return (zend_object_iterator*)iterator;
515
11
}
516
517
0
static zend_result spl_get_iterator_from_aggregate(zval *retval, zend_class_entry *ce, zend_object *obj) {
518
0
  zend_function **getiterator_cache =
519
0
    ce->iterator_funcs_ptr ? &ce->iterator_funcs_ptr->zf_new_iterator : NULL;
520
0
  zend_call_method_with_0_params(obj, ce, getiterator_cache, "getiterator", retval);
521
0
  if (EG(exception)) {
522
0
    return FAILURE;
523
0
  }
524
0
  if (Z_TYPE_P(retval) != IS_OBJECT
525
0
      || !instanceof_function(Z_OBJCE_P(retval), zend_ce_traversable)) {
526
0
    zend_throw_exception_ex(spl_ce_LogicException, 0,
527
0
      "%s::getIterator() must return an object that implements Traversable",
528
0
      ZSTR_VAL(ce->name));
529
0
    zval_ptr_dtor(retval);
530
0
    return FAILURE;
531
0
  }
532
0
  return SUCCESS;
533
0
}
534
535
static void spl_RecursiveIteratorIterator_free_iterators(spl_recursive_it_object *object)
536
1.70k
{
537
1.70k
  if (object->iterators) {
538
22
    while (object->level >= 0) {
539
11
      zend_object_iterator *sub_iter = object->iterators[object->level].iterator;
540
11
      zend_iterator_dtor(sub_iter);
541
11
      zval_ptr_dtor(&object->iterators[object->level].zobject);
542
11
      object->level--;
543
11
    }
544
11
    efree(object->iterators);
545
11
    object->iterators = NULL;
546
11
  }
547
1.70k
}
548
549
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)
550
13
{
551
13
  zval *object = ZEND_THIS;
552
13
  spl_recursive_it_object *intern;
553
13
  zval *iterator;
554
13
  zend_class_entry *ce_iterator;
555
13
  zend_long mode, flags;
556
13
  zval caching_it, aggregate_retval;
557
558
13
  switch (rit_type) {
559
0
    case RIT_RecursiveTreeIterator: {
560
0
      zend_long user_caching_it_flags = CIT_CATCH_GET_CHILD;
561
0
      mode = RIT_SELF_FIRST;
562
0
      flags = RTIT_BYPASS_KEY;
563
564
0
      if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|lll", &iterator, &flags, &user_caching_it_flags, &mode) == FAILURE) {
565
0
        RETURN_THROWS();
566
0
      }
567
568
0
      if (instanceof_function(Z_OBJCE_P(iterator), zend_ce_aggregate)) {
569
0
        if (spl_get_iterator_from_aggregate(
570
0
            &aggregate_retval, Z_OBJCE_P(iterator), Z_OBJ_P(iterator)) == FAILURE) {
571
0
          RETURN_THROWS();
572
0
        }
573
0
        iterator = &aggregate_retval;
574
0
      } else {
575
0
        Z_ADDREF_P(iterator);
576
0
      }
577
578
0
      zval params[2];
579
0
      ZVAL_COPY_VALUE(&params[0], iterator);
580
0
      ZVAL_LONG(&params[1], user_caching_it_flags);
581
0
      zend_result is_initialized = object_init_with_constructor(&caching_it, spl_ce_RecursiveCachingIterator, 2, params, NULL);
582
0
      zval_ptr_dtor(&params[0]);
583
0
      if (is_initialized == FAILURE) {
584
0
        RETURN_THROWS();
585
0
      }
586
587
0
      iterator = &caching_it;
588
0
      break;
589
0
    }
590
13
    case RIT_RecursiveIteratorIterator:
591
13
    default: {
592
13
      mode = RIT_LEAVES_ONLY;
593
13
      flags = 0;
594
13
      if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|ll", &iterator, &mode, &flags) == FAILURE) {
595
2
        RETURN_THROWS();
596
2
      }
597
598
11
      if (instanceof_function(Z_OBJCE_P(iterator), zend_ce_aggregate)) {
599
0
        if (spl_get_iterator_from_aggregate(
600
0
            &aggregate_retval, Z_OBJCE_P(iterator), Z_OBJ_P(iterator)) == FAILURE) {
601
0
          RETURN_THROWS();
602
0
        }
603
0
        iterator = &aggregate_retval;
604
11
      } else {
605
11
        Z_ADDREF_P(iterator);
606
11
      }
607
11
      break;
608
11
    }
609
13
  }
610
11
  if (!instanceof_function(Z_OBJCE_P(iterator), spl_ce_RecursiveIterator)) {
611
0
    if (iterator) {
612
0
      zval_ptr_dtor(iterator);
613
0
    }
614
0
    zend_throw_exception(spl_ce_InvalidArgumentException, "An instance of RecursiveIterator or IteratorAggregate creating it is required", 0);
615
0
    return;
616
0
  }
617
618
11
  intern = Z_SPLRECURSIVE_IT_P(object);
619
11
  spl_RecursiveIteratorIterator_free_iterators(intern);
620
11
  intern->iterators = emalloc(sizeof(spl_sub_iterator));
621
11
  intern->level = 0;
622
11
  intern->mode = mode;
623
11
  intern->flags = (int)flags;
624
11
  intern->max_depth = -1;
625
11
  intern->in_iteration = false;
626
11
  intern->ce = Z_OBJCE_P(object);
627
628
11
  intern->beginIteration = zend_hash_str_find_ptr(&intern->ce->function_table, "beginiteration", sizeof("beginiteration") - 1);
629
11
  if (intern->beginIteration->common.scope == ce_base) {
630
11
    intern->beginIteration = NULL;
631
11
  }
632
11
  intern->endIteration = zend_hash_str_find_ptr(&intern->ce->function_table, "enditeration", sizeof("enditeration") - 1);
633
11
  if (intern->endIteration->common.scope == ce_base) {
634
11
    intern->endIteration = NULL;
635
11
  }
636
11
  intern->callHasChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "callhaschildren", sizeof("callHasChildren") - 1);
637
11
  if (intern->callHasChildren->common.scope == ce_base) {
638
11
    intern->callHasChildren = NULL;
639
11
  }
640
11
  intern->callGetChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "callgetchildren", sizeof("callGetChildren") - 1);
641
11
  if (intern->callGetChildren->common.scope == ce_base) {
642
11
    intern->callGetChildren = NULL;
643
11
  }
644
11
  intern->beginChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "beginchildren", sizeof("beginchildren") - 1);
645
11
  if (intern->beginChildren->common.scope == ce_base) {
646
11
    intern->beginChildren = NULL;
647
11
  }
648
11
  intern->endChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "endchildren", sizeof("endchildren") - 1);
649
11
  if (intern->endChildren->common.scope == ce_base) {
650
11
    intern->endChildren = NULL;
651
11
  }
652
11
  intern->nextElement = zend_hash_str_find_ptr(&intern->ce->function_table, "nextelement", sizeof("nextElement") - 1);
653
11
  if (intern->nextElement->common.scope == ce_base) {
654
11
    intern->nextElement = NULL;
655
11
  }
656
657
11
  ce_iterator = Z_OBJCE_P(iterator); /* respect inheritance, don't use spl_ce_RecursiveIterator */
658
11
  intern->iterators[0].iterator = ce_iterator->get_iterator(ce_iterator, iterator, 0);
659
11
  ZVAL_OBJ(&intern->iterators[0].zobject, Z_OBJ_P(iterator));
660
11
  intern->iterators[0].ce = ce_iterator;
661
11
  intern->iterators[0].state = RS_START;
662
11
  intern->iterators[0].haschildren = NULL;
663
11
  intern->iterators[0].getchildren = NULL;
664
665
11
  if (EG(exception)) {
666
0
    spl_RecursiveIteratorIterator_free_iterators(intern);
667
0
  }
668
11
}
669
670
/* {{{ Creates a RecursiveIteratorIterator from a RecursiveIterator. */
671
PHP_METHOD(RecursiveIteratorIterator, __construct)
672
13
{
673
13
  spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveIteratorIterator, zend_ce_iterator, RIT_RecursiveIteratorIterator);
674
13
} /* }}} */
675
676
/* {{{ Rewind the iterator to the first element of the top level inner iterator. */
677
PHP_METHOD(RecursiveIteratorIterator, rewind)
678
0
{
679
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
680
681
0
  ZEND_PARSE_PARAMETERS_NONE();
682
0
  spl_recursive_it_rewind_ex(object, ZEND_THIS);
683
0
} /* }}} */
684
685
/* {{{ Check whether the current position is valid */
686
PHP_METHOD(RecursiveIteratorIterator, valid)
687
0
{
688
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
689
690
0
  ZEND_PARSE_PARAMETERS_NONE();
691
0
  RETURN_BOOL(spl_recursive_it_valid_ex(object, ZEND_THIS) == SUCCESS);
692
0
} /* }}} */
693
694
/* {{{ Access the current key */
695
PHP_METHOD(RecursiveIteratorIterator, key)
696
0
{
697
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
698
0
  zend_object_iterator      *iterator;
699
700
0
  ZEND_PARSE_PARAMETERS_NONE();
701
702
0
  SPL_FETCH_SUB_ITERATOR(iterator, object);
703
704
0
  if (iterator->funcs->get_current_key) {
705
0
    iterator->funcs->get_current_key(iterator, return_value);
706
0
  } else {
707
0
    RETURN_NULL();
708
0
  }
709
0
} /* }}} */
710
711
/* {{{ Access the current element value */
712
PHP_METHOD(RecursiveIteratorIterator, current)
713
0
{
714
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
715
0
  zend_object_iterator      *iterator;
716
0
  zval                      *data;
717
718
0
  ZEND_PARSE_PARAMETERS_NONE();
719
720
0
  SPL_FETCH_SUB_ITERATOR(iterator, object);
721
722
0
  data = iterator->funcs->get_current_data(iterator);
723
0
  if (data) {
724
0
    RETURN_COPY_DEREF(data);
725
0
  }
726
0
} /* }}} */
727
728
/* {{{ Move forward to the next element */
729
PHP_METHOD(RecursiveIteratorIterator, next)
730
0
{
731
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
732
733
0
  ZEND_PARSE_PARAMETERS_NONE();
734
0
  spl_recursive_it_move_forward_ex(object, ZEND_THIS);
735
0
} /* }}} */
736
737
/* {{{ Get the current depth of the recursive iteration */
738
PHP_METHOD(RecursiveIteratorIterator, getDepth)
739
0
{
740
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
741
742
0
  ZEND_PARSE_PARAMETERS_NONE();
743
0
  RETURN_LONG(object->level);
744
0
} /* }}} */
745
746
/* {{{ The current active sub iterator or the iterator at specified level */
747
PHP_METHOD(RecursiveIteratorIterator, getSubIterator)
748
0
{
749
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
750
0
  zend_long level;
751
0
  bool level_is_null = true;
752
0
  zval *value;
753
754
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!", &level, &level_is_null) == FAILURE) {
755
0
    RETURN_THROWS();
756
0
  }
757
758
0
  if (level_is_null) {
759
0
    level = object->level;
760
0
  } else if (level < 0 || level > object->level) {
761
0
    RETURN_NULL();
762
0
  }
763
764
0
  if(!object->iterators) {
765
0
    zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
766
0
    RETURN_THROWS();
767
0
  }
768
769
0
  value = &object->iterators[level].zobject;
770
0
  RETURN_COPY_DEREF(value);
771
0
} /* }}} */
772
773
/* {{{ The current active sub iterator */
774
PHP_METHOD(RecursiveIteratorIterator, getInnerIterator)
775
0
{
776
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
777
0
  zval      *zobject;
778
779
0
  ZEND_PARSE_PARAMETERS_NONE();
780
0
  SPL_FETCH_SUB_ELEMENT_ADDR(zobject, object, zobject);
781
0
  RETURN_COPY_DEREF(zobject);
782
0
} /* }}} */
783
784
/* {{{ Called when iteration begins (after first rewind() call) */
785
PHP_METHOD(RecursiveIteratorIterator, beginIteration)
786
0
{
787
0
  ZEND_PARSE_PARAMETERS_NONE();
788
  /* nothing to do */
789
0
} /* }}} */
790
791
/* {{{ Called when iteration ends (when valid() first returns false */
792
PHP_METHOD(RecursiveIteratorIterator, endIteration)
793
0
{
794
0
  ZEND_PARSE_PARAMETERS_NONE();
795
  /* nothing to do */
796
0
} /* }}} */
797
798
/* {{{ Called for each element to test whether it has children */
799
PHP_METHOD(RecursiveIteratorIterator, callHasChildren)
800
0
{
801
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
802
0
  zend_class_entry *ce;
803
0
  zval *zobject;
804
805
0
  ZEND_PARSE_PARAMETERS_NONE();
806
807
0
  if (!object->iterators) {
808
0
    RETURN_FALSE;
809
0
  }
810
811
0
  SPL_FETCH_SUB_ELEMENT(ce, object, ce);
812
813
0
  zobject = &object->iterators[object->level].zobject;
814
0
  if (Z_TYPE_P(zobject) == IS_UNDEF) {
815
0
    RETURN_FALSE;
816
0
  } else {
817
0
    zend_call_method_with_0_params(Z_OBJ_P(zobject), ce, &object->iterators[object->level].haschildren, "haschildren", return_value);
818
0
    if (Z_TYPE_P(return_value) == IS_UNDEF) {
819
0
      RETURN_FALSE;
820
0
    }
821
0
  }
822
0
} /* }}} */
823
824
/* {{{ Return children of current element */
825
PHP_METHOD(RecursiveIteratorIterator, callGetChildren)
826
0
{
827
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
828
0
  zend_class_entry *ce;
829
0
  zval *zobject;
830
831
0
  ZEND_PARSE_PARAMETERS_NONE();
832
833
0
  SPL_FETCH_SUB_ELEMENT(ce, object, ce);
834
835
0
  zobject = &object->iterators[object->level].zobject;
836
0
  if (Z_TYPE_P(zobject) == IS_UNDEF) {
837
0
    RETURN_NULL();
838
0
  } else {
839
0
    zend_call_method_with_0_params(Z_OBJ_P(zobject), ce, &object->iterators[object->level].getchildren, "getchildren", return_value);
840
0
    if (Z_TYPE_P(return_value) == IS_UNDEF) {
841
0
      RETURN_NULL();
842
0
    }
843
0
  }
844
0
} /* }}} */
845
846
/* {{{ Called when recursing one level down */
847
PHP_METHOD(RecursiveIteratorIterator, beginChildren)
848
0
{
849
0
  ZEND_PARSE_PARAMETERS_NONE();
850
  /* nothing to do */
851
0
} /* }}} */
852
853
/* {{{ Called when end recursing one level */
854
PHP_METHOD(RecursiveIteratorIterator, endChildren)
855
0
{
856
0
  ZEND_PARSE_PARAMETERS_NONE();
857
  /* nothing to do */
858
0
} /* }}} */
859
860
/* {{{ Called when the next element is available */
861
PHP_METHOD(RecursiveIteratorIterator, nextElement)
862
0
{
863
0
  ZEND_PARSE_PARAMETERS_NONE();
864
  /* nothing to do */
865
0
} /* }}} */
866
867
/* {{{ Set the maximum allowed depth (or any depth if pmax_depth = -1] */
868
PHP_METHOD(RecursiveIteratorIterator, setMaxDepth)
869
0
{
870
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
871
0
  zend_long  max_depth = -1;
872
873
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &max_depth) == FAILURE) {
874
0
    RETURN_THROWS();
875
0
  }
876
0
  if (max_depth < -1) {
877
0
    zend_argument_value_error(1, "must be greater than or equal to -1");
878
0
    RETURN_THROWS();
879
0
  } else if (max_depth > INT_MAX) {
880
0
    max_depth = INT_MAX;
881
0
  }
882
883
0
  object->max_depth = (int)max_depth;
884
0
} /* }}} */
885
886
/* {{{ Return the maximum accepted depth or false if any depth is allowed */
887
PHP_METHOD(RecursiveIteratorIterator, getMaxDepth)
888
0
{
889
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
890
891
0
  ZEND_PARSE_PARAMETERS_NONE();
892
893
0
  if (object->max_depth == -1) {
894
0
    RETURN_FALSE;
895
0
  } else {
896
0
    RETURN_LONG(object->max_depth);
897
0
  }
898
0
} /* }}} */
899
900
static zend_function *spl_recursive_it_get_method(zend_object **zobject, zend_string *method, const zval *key)
901
0
{
902
0
  zend_function           *function_handler;
903
0
  spl_recursive_it_object *object = spl_recursive_it_from_obj(*zobject);
904
0
  zend_long                     level = object->level;
905
0
  zval                    *zobj;
906
907
0
  if (!object->iterators) {
908
0
    zend_throw_error(NULL, "The %s instance wasn't initialized properly", ZSTR_VAL((*zobject)->ce->name));
909
0
    return NULL;
910
0
  }
911
0
  zobj = &object->iterators[level].zobject;
912
913
0
  function_handler = zend_std_get_method(zobject, method, key);
914
0
  if (!function_handler) {
915
0
    if ((function_handler = zend_hash_find_ptr(&Z_OBJCE_P(zobj)->function_table, method)) == NULL) {
916
0
      *zobject = Z_OBJ_P(zobj);
917
0
      function_handler = (*zobject)->handlers->get_method(zobject, method, key);
918
0
    } else {
919
0
      *zobject = Z_OBJ_P(zobj);
920
0
    }
921
0
  }
922
0
  return function_handler;
923
0
}
924
925
/* {{{ spl_RecursiveIteratorIterator_free_storage */
926
static void spl_RecursiveIteratorIterator_free_storage(zend_object *_object)
927
1.69k
{
928
1.69k
  spl_recursive_it_object *object = spl_recursive_it_from_obj(_object);
929
930
1.69k
  spl_RecursiveIteratorIterator_free_iterators(object);
931
932
1.69k
  zend_object_std_dtor(&object->std);
933
11.8k
  for (size_t i = 0; i < 6; i++) {
934
10.1k
    if (object->prefix[i]) {
935
2.99k
      zend_string_release(object->prefix[i]);
936
2.99k
    }
937
10.1k
  }
938
939
1.69k
  if (object->postfix[0]) {
940
499
    zend_string_release(object->postfix[0]);
941
499
  }
942
1.69k
}
943
/* }}} */
944
945
static HashTable *spl_RecursiveIteratorIterator_get_gc(zend_object *obj, zval **table, int *n)
946
3.53k
{
947
3.53k
  spl_recursive_it_object *object = spl_recursive_it_from_obj(obj);
948
3.53k
  zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
949
950
3.53k
  if (object->iterators) {
951
44
    for (int level = 0; level <= object->level; level++) {
952
22
      zend_get_gc_buffer_add_zval(gc_buffer, &object->iterators[level].zobject);
953
22
      zend_get_gc_buffer_add_obj(gc_buffer, &object->iterators[level].iterator->std);
954
22
    }
955
22
  }
956
957
3.53k
  zend_get_gc_buffer_use(gc_buffer, table, n);
958
3.53k
  return zend_std_get_properties(obj);
959
3.53k
}
960
961
/* {{{ spl_RecursiveIteratorIterator_new_ex */
962
static zend_object *spl_RecursiveIteratorIterator_new_ex(zend_class_entry *class_type, int init_prefix)
963
1.69k
{
964
1.69k
  spl_recursive_it_object *intern;
965
966
1.69k
  intern = zend_object_alloc(sizeof(spl_recursive_it_object), class_type);
967
968
1.69k
  if (init_prefix) {
969
499
    intern->prefix[0] = ZSTR_EMPTY_ALLOC();
970
499
    intern->prefix[1] = ZSTR_INIT_LITERAL("| ", 0);
971
499
    intern->prefix[2] = ZSTR_INIT_LITERAL("  ", 0);
972
499
    intern->prefix[3] = ZSTR_INIT_LITERAL("|-", 0);
973
499
    intern->prefix[4] = ZSTR_INIT_LITERAL("\\-", 0);
974
499
    intern->prefix[5] = ZSTR_EMPTY_ALLOC();
975
976
499
    intern->postfix[0] = ZSTR_EMPTY_ALLOC();
977
499
  }
978
979
1.69k
  zend_object_std_init(&intern->std, class_type);
980
1.69k
  object_properties_init(&intern->std, class_type);
981
982
1.69k
  return &intern->std;
983
1.69k
}
984
/* }}} */
985
986
/* {{{ spl_RecursiveIteratorIterator_new */
987
static zend_object *spl_RecursiveIteratorIterator_new(zend_class_entry *class_type)
988
1.19k
{
989
1.19k
  return spl_RecursiveIteratorIterator_new_ex(class_type, 0);
990
1.19k
}
991
/* }}} */
992
993
/* {{{ spl_RecursiveTreeIterator_new */
994
static zend_object *spl_RecursiveTreeIterator_new(zend_class_entry *class_type)
995
499
{
996
499
  return spl_RecursiveIteratorIterator_new_ex(class_type, 1);
997
499
}
998
/* }}} */
999
1000
static zend_string *spl_recursive_tree_iterator_get_prefix(spl_recursive_it_object *object)
1001
0
{
1002
0
  smart_str  str = {0};
1003
0
  zval       has_next;
1004
0
  int        level;
1005
1006
0
  smart_str_append(&str, object->prefix[0]);
1007
1008
0
  for (level = 0; level < object->level; ++level) {
1009
0
    zend_call_method_with_0_params(Z_OBJ(object->iterators[level].zobject), object->iterators[level].ce, NULL, "hasnext", &has_next);
1010
0
    if (Z_TYPE(has_next) != IS_UNDEF) {
1011
0
      if (Z_TYPE(has_next) == IS_TRUE) {
1012
0
        smart_str_append(&str, object->prefix[1]);
1013
0
      } else {
1014
0
        smart_str_append(&str, object->prefix[2]);
1015
0
      }
1016
0
      zval_ptr_dtor(&has_next);
1017
0
    }
1018
0
  }
1019
0
  zend_call_method_with_0_params(Z_OBJ(object->iterators[level].zobject), object->iterators[level].ce, NULL, "hasnext", &has_next);
1020
0
  if (Z_TYPE(has_next) != IS_UNDEF) {
1021
0
    if (Z_TYPE(has_next) == IS_TRUE) {
1022
0
      smart_str_append(&str, object->prefix[3]);
1023
0
    } else {
1024
0
      smart_str_append(&str, object->prefix[4]);
1025
0
    }
1026
0
    zval_ptr_dtor(&has_next);
1027
0
  }
1028
1029
0
  smart_str_append(&str, object->prefix[5]);
1030
0
  smart_str_0(&str);
1031
1032
0
  return str.s;
1033
0
}
1034
1035
static zend_string *spl_recursive_tree_iterator_get_entry(spl_recursive_it_object *object)
1036
0
{
1037
0
  zend_object_iterator *iterator = object->iterators[object->level].iterator;
1038
0
  zval *data = iterator->funcs->get_current_data(iterator);
1039
0
  if (!data) {
1040
0
    return NULL;
1041
0
  }
1042
1043
0
  ZVAL_DEREF(data);
1044
0
  if (Z_TYPE_P(data) == IS_ARRAY) {
1045
    /* TODO: Remove this special case? */
1046
0
    return ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED);
1047
0
  }
1048
0
  return zval_get_string(data);
1049
0
}
1050
1051
static zend_string *spl_recursive_tree_iterator_get_postfix(spl_recursive_it_object *object)
1052
0
{
1053
0
  return zend_string_copy(object->postfix[0]);
1054
0
}
1055
1056
/* {{{ RecursiveIteratorIterator to generate ASCII graphic trees for the entries in a RecursiveIterator */
1057
PHP_METHOD(RecursiveTreeIterator, __construct)
1058
0
{
1059
0
  spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveTreeIterator, zend_ce_iterator, RIT_RecursiveTreeIterator);
1060
0
} /* }}} */
1061
1062
/* {{{ Sets prefix parts as used in getPrefix() */
1063
PHP_METHOD(RecursiveTreeIterator, setPrefixPart)
1064
0
{
1065
0
  zend_long  part;
1066
0
  zend_string *prefix;
1067
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1068
1069
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "lS", &part, &prefix) == FAILURE) {
1070
0
    RETURN_THROWS();
1071
0
  }
1072
1073
0
  if (0 > part || part > 5) {
1074
0
    zend_argument_value_error(1, "must be a RecursiveTreeIterator::PREFIX_* constant");
1075
0
    RETURN_THROWS();
1076
0
  }
1077
1078
0
  zend_string_release(object->prefix[part]);
1079
0
  object->prefix[part] = zend_string_copy(prefix);
1080
0
} /* }}} */
1081
1082
/* {{{ Returns the string to place in front of current element */
1083
PHP_METHOD(RecursiveTreeIterator, getPrefix)
1084
0
{
1085
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1086
1087
0
  ZEND_PARSE_PARAMETERS_NONE();
1088
1089
0
  if(!object->iterators) {
1090
0
    zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1091
0
    RETURN_THROWS();
1092
0
  }
1093
1094
0
  RETURN_STR(spl_recursive_tree_iterator_get_prefix(object));
1095
0
} /* }}} */
1096
1097
/* {{{ Sets postfix as used in getPostfix() */
1098
PHP_METHOD(RecursiveTreeIterator, setPostfix)
1099
0
{
1100
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1101
0
  zend_string *postfix;
1102
1103
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &postfix) == FAILURE) {
1104
0
    RETURN_THROWS();
1105
0
  }
1106
1107
0
  zend_string_release(object->postfix[0]);
1108
0
  object->postfix[0] = zend_string_copy(postfix);
1109
0
} /* }}} */
1110
1111
/* {{{ Returns the string presentation built for current element */
1112
PHP_METHOD(RecursiveTreeIterator, getEntry)
1113
0
{
1114
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1115
1116
0
  ZEND_PARSE_PARAMETERS_NONE();
1117
1118
0
  if(!object->iterators) {
1119
0
    zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1120
0
    RETURN_THROWS();
1121
0
  }
1122
1123
0
  zend_string *entry = spl_recursive_tree_iterator_get_entry(object);
1124
0
  if (!entry) {
1125
    // TODO: Can this happen? It's not in the stubs.
1126
0
    RETURN_NULL();
1127
0
  }
1128
0
  RETURN_STR(entry);
1129
0
} /* }}} */
1130
1131
/* {{{ Returns the string to place after the current element */
1132
PHP_METHOD(RecursiveTreeIterator, getPostfix)
1133
0
{
1134
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1135
1136
0
  ZEND_PARSE_PARAMETERS_NONE();
1137
1138
0
  if(!object->iterators) {
1139
0
    zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1140
0
    RETURN_THROWS();
1141
0
  }
1142
1143
0
  RETURN_STR(spl_recursive_tree_iterator_get_postfix(object));
1144
0
} /* }}} */
1145
1146
/* {{{ Returns the current element prefixed and postfixed */
1147
PHP_METHOD(RecursiveTreeIterator, current)
1148
0
{
1149
0
  spl_recursive_it_object *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1150
1151
0
  ZEND_PARSE_PARAMETERS_NONE();
1152
1153
0
  if(!object->iterators) {
1154
0
    zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1155
0
    RETURN_THROWS();
1156
0
  }
1157
1158
0
  if (object->flags & RTIT_BYPASS_CURRENT) {
1159
0
    zend_object_iterator      *iterator;
1160
0
    zval                      *data;
1161
1162
0
    SPL_FETCH_SUB_ITERATOR(iterator, object);
1163
0
    data = iterator->funcs->get_current_data(iterator);
1164
0
    if (data) {
1165
0
      RETURN_COPY_DEREF(data);
1166
0
    } else {
1167
0
      RETURN_NULL();
1168
0
    }
1169
0
  }
1170
1171
0
  zend_string *entry = spl_recursive_tree_iterator_get_entry(object);
1172
0
  if (!entry) {
1173
0
    RETURN_NULL();
1174
0
  }
1175
1176
0
  zend_string *prefix = spl_recursive_tree_iterator_get_prefix(object);
1177
0
  zend_string *postfix = spl_recursive_tree_iterator_get_postfix(object);
1178
1179
0
  zend_string *result = zend_string_concat3(
1180
0
    ZSTR_VAL(prefix), ZSTR_LEN(prefix),
1181
0
    ZSTR_VAL(entry), ZSTR_LEN(entry),
1182
0
    ZSTR_VAL(postfix), ZSTR_LEN(postfix));
1183
1184
0
  zend_string_release(entry);
1185
0
  zend_string_release(prefix);
1186
0
  zend_string_release(postfix);
1187
1188
0
  RETURN_NEW_STR(result);
1189
0
} /* }}} */
1190
1191
/* {{{ Returns the current key prefixed and postfixed */
1192
PHP_METHOD(RecursiveTreeIterator, key)
1193
0
{
1194
0
  spl_recursive_it_object   *object = Z_SPLRECURSIVE_IT_P(ZEND_THIS);
1195
0
  zend_object_iterator      *iterator;
1196
0
  zval                       key;
1197
1198
0
  ZEND_PARSE_PARAMETERS_NONE();
1199
1200
0
  SPL_FETCH_SUB_ITERATOR(iterator, object);
1201
1202
0
  if (iterator->funcs->get_current_key) {
1203
0
    iterator->funcs->get_current_key(iterator, &key);
1204
0
  } else {
1205
0
    ZVAL_NULL(&key);
1206
0
  }
1207
1208
0
  if (object->flags & RTIT_BYPASS_KEY) {
1209
0
    RETURN_COPY_VALUE(&key);
1210
0
  }
1211
1212
0
  zend_string *key_str = zval_get_string(&key);
1213
0
  zend_string *prefix = spl_recursive_tree_iterator_get_prefix(object);
1214
0
  zend_string *postfix = spl_recursive_tree_iterator_get_postfix(object);
1215
1216
0
  zend_string *result = zend_string_concat3(
1217
0
    ZSTR_VAL(prefix), ZSTR_LEN(prefix),
1218
0
    ZSTR_VAL(key_str), ZSTR_LEN(key_str),
1219
0
    ZSTR_VAL(postfix), ZSTR_LEN(postfix));
1220
1221
0
  zend_string_release(key_str);
1222
0
  zend_string_release(prefix);
1223
0
  zend_string_release(postfix);
1224
0
  zval_ptr_dtor(&key);
1225
1226
0
  RETURN_NEW_STR(result);
1227
0
} /* }}} */
1228
1229
static zend_function *spl_dual_it_get_method(zend_object **object, zend_string *method, const zval *key)
1230
168
{
1231
168
  zend_function        *function_handler;
1232
168
  spl_dual_it_object   *intern;
1233
1234
168
  intern = spl_dual_it_from_obj(*object);
1235
1236
168
  function_handler = zend_std_get_method(object, method, key);
1237
168
  if (!function_handler && intern->inner.ce) {
1238
121
    if ((function_handler = zend_hash_find_ptr(&intern->inner.ce->function_table, method)) == NULL) {
1239
121
      if (Z_OBJ_HT(intern->inner.zobject)->get_method) {
1240
121
        *object = Z_OBJ(intern->inner.zobject);
1241
121
        function_handler = (*object)->handlers->get_method(object, method, key);
1242
121
      }
1243
121
    } else {
1244
0
      *object = Z_OBJ(intern->inner.zobject);
1245
0
    }
1246
121
  }
1247
168
  return function_handler;
1248
168
}
1249
1250
#define SPL_CHECK_CTOR(intern, classname) \
1251
0
  if (intern->dit_type == DIT_Unknown) { \
1252
0
    /* TODO Normal Error? */ \
1253
0
    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Classes derived from %s must call %s::__construct()", \
1254
0
        ZSTR_VAL((spl_ce_##classname)->name), ZSTR_VAL((spl_ce_##classname)->name)); \
1255
0
    RETURN_THROWS(); \
1256
0
  }
1257
1258
0
#define APPENDIT_CHECK_CTOR(intern) SPL_CHECK_CTOR(intern, AppendIterator)
1259
1260
static inline zend_result spl_dual_it_fetch(spl_dual_it_object *intern, int check_more);
1261
1262
static inline zend_result spl_cit_check_flags(zend_long flags)
1263
0
{
1264
0
  zend_long cnt = 0;
1265
1266
0
  cnt += (flags & CIT_CALL_TOSTRING) ? 1 : 0;
1267
0
  cnt += (flags & CIT_TOSTRING_USE_KEY) ? 1 : 0;
1268
0
  cnt += (flags & CIT_TOSTRING_USE_CURRENT) ? 1 : 0;
1269
0
  cnt += (flags & CIT_TOSTRING_USE_INNER) ? 1 : 0;
1270
1271
0
  return cnt <= 1 ? SUCCESS : FAILURE;
1272
0
}
1273
1274
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)
1275
168
{
1276
168
  zval                 *zobject, retval;
1277
168
  spl_dual_it_object   *intern;
1278
168
  zend_class_entry     *ce = NULL;
1279
168
  int                   inc_refcount = 1;
1280
168
  zend_error_handling   error_handling;
1281
1282
168
  intern = Z_SPLDUAL_IT_P(ZEND_THIS);
1283
1284
168
  if (intern->dit_type != DIT_Unknown) {
1285
0
    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s::getIterator() must be called exactly once per instance", ZSTR_VAL(ce_base->name));
1286
0
    return NULL;
1287
0
  }
1288
1289
168
  switch (dit_type) {
1290
6
    case DIT_LimitIterator: {
1291
6
      intern->u.limit.offset = 0; /* start at beginning */
1292
6
      intern->u.limit.count = -1; /* get all */
1293
6
      if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|ll", &zobject, ce_inner, &intern->u.limit.offset, &intern->u.limit.count) == FAILURE) {
1294
2
        return NULL;
1295
2
      }
1296
4
      if (intern->u.limit.offset < 0) {
1297
0
        zend_argument_value_error(2, "must be greater than or equal to 0");
1298
0
        return NULL;
1299
0
      }
1300
4
      if (intern->u.limit.count < -1) {
1301
0
        zend_argument_value_error(3, "must be greater than or equal to -1");
1302
0
        return NULL;
1303
0
      }
1304
4
      break;
1305
4
    }
1306
4
    case DIT_CachingIterator:
1307
4
    case DIT_RecursiveCachingIterator: {
1308
4
      zend_long flags = CIT_CALL_TOSTRING;
1309
4
      if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &zobject, ce_inner, &flags) == FAILURE) {
1310
4
        return NULL;
1311
4
      }
1312
0
      if (spl_cit_check_flags(flags) != SUCCESS) {
1313
0
        zend_argument_value_error(2, "must contain only one of CachingIterator::CALL_TOSTRING, "
1314
0
          "CachingIterator::TOSTRING_USE_KEY, CachingIterator::TOSTRING_USE_CURRENT, "
1315
0
          "or CachingIterator::TOSTRING_USE_INNER");
1316
0
        return NULL;
1317
0
      }
1318
0
      intern->u.caching.flags |= flags & CIT_PUBLIC;
1319
0
      array_init(&intern->u.caching.zcache);
1320
0
      break;
1321
0
    }
1322
59
    case DIT_IteratorIterator: {
1323
59
      zend_class_entry *ce_cast;
1324
59
      zend_string *class_name = NULL;
1325
1326
59
      if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|S!", &zobject, ce_inner, &class_name) == FAILURE) {
1327
5
        return NULL;
1328
5
      }
1329
54
      ce = Z_OBJCE_P(zobject);
1330
54
      if (!instanceof_function(ce, zend_ce_iterator)) {
1331
0
        if (class_name) {
1332
0
          if (!(ce_cast = zend_lookup_class(class_name))
1333
0
          || !instanceof_function(ce, ce_cast)
1334
0
          || !ce_cast->get_iterator
1335
0
          ) {
1336
0
            zend_throw_exception(spl_ce_LogicException, "Class to downcast to not found or not base class or does not implement Traversable", 0);
1337
0
            return NULL;
1338
0
          }
1339
0
          ce = ce_cast;
1340
0
        }
1341
0
        if (instanceof_function(ce, zend_ce_aggregate)) {
1342
0
          if (spl_get_iterator_from_aggregate(&retval, ce, Z_OBJ_P(zobject)) == FAILURE) {
1343
0
            return NULL;
1344
0
          }
1345
0
          zobject = &retval;
1346
0
          ce = Z_OBJCE_P(zobject);
1347
0
          inc_refcount = 0;
1348
0
        }
1349
0
      }
1350
54
      break;
1351
54
    }
1352
54
    case DIT_RegexIterator:
1353
0
    case DIT_RecursiveRegexIterator: {
1354
0
      zend_string *regex;
1355
0
      zend_long mode = REGIT_MODE_MATCH;
1356
1357
0
      intern->u.regex.flags = 0;
1358
0
      intern->u.regex.preg_flags = 0;
1359
0
      if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS|lll", &zobject, ce_inner, &regex, &mode, &intern->u.regex.flags, &intern->u.regex.preg_flags) == FAILURE) {
1360
0
        return NULL;
1361
0
      }
1362
0
      if (mode < 0 || mode >= REGIT_MODE_MAX) {
1363
0
        zend_argument_value_error(3, "must be RegexIterator::MATCH, RegexIterator::GET_MATCH, "
1364
0
          "RegexIterator::ALL_MATCHES, RegexIterator::SPLIT, or RegexIterator::REPLACE");
1365
0
        return NULL;
1366
0
      }
1367
1368
      /* pcre_get_compiled_regex_cache() might emit E_WARNINGs that we want to promote to exception */
1369
0
      zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling);
1370
0
      intern->u.regex.pce = pcre_get_compiled_regex_cache(regex);
1371
0
      zend_restore_error_handling(&error_handling);
1372
1373
0
      if (intern->u.regex.pce == NULL) {
1374
        /* pcre_get_compiled_regex_cache has already sent error */
1375
0
        return NULL;
1376
0
      }
1377
0
      intern->u.regex.mode = mode;
1378
0
      intern->u.regex.regex = zend_string_copy(regex);
1379
0
      php_pcre_pce_incref(intern->u.regex.pce);
1380
0
      break;
1381
0
    }
1382
93
    case DIT_CallbackFilterIterator:
1383
95
    case DIT_RecursiveCallbackFilterIterator: {
1384
95
      zend_fcall_info fci;
1385
95
      if (zend_parse_parameters(ZEND_NUM_ARGS(), "OF", &zobject, ce_inner, &fci, &intern->u.callback_filter) == FAILURE) {
1386
9
        return NULL;
1387
9
      }
1388
86
      zend_fcc_addref(&intern->u.callback_filter);
1389
86
      break;
1390
95
    }
1391
4
    default:
1392
4
      if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zobject, ce_inner) == FAILURE) {
1393
4
        return NULL;
1394
4
      }
1395
0
      break;
1396
168
  }
1397
1398
144
  intern->dit_type = dit_type;
1399
144
  if (inc_refcount) {
1400
144
    Z_ADDREF_P(zobject);
1401
144
  }
1402
144
  ZVAL_OBJ(&intern->inner.zobject, Z_OBJ_P(zobject));
1403
1404
144
  intern->inner.ce = dit_type == DIT_IteratorIterator ? ce : Z_OBJCE_P(zobject);
1405
144
  intern->inner.object = Z_OBJ_P(zobject);
1406
144
  intern->inner.iterator = intern->inner.ce->get_iterator(intern->inner.ce, zobject, 0);
1407
1408
144
  return intern;
1409
168
}
1410
1411
/* {{{ Create an Iterator from another iterator */
1412
PHP_METHOD(FilterIterator, __construct)
1413
0
{
1414
0
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_FilterIterator, zend_ce_iterator, DIT_FilterIterator);
1415
0
} /* }}} */
1416
1417
/* {{{ Create an Iterator from another iterator */
1418
PHP_METHOD(CallbackFilterIterator, __construct)
1419
93
{
1420
93
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_CallbackFilterIterator, zend_ce_iterator, DIT_CallbackFilterIterator);
1421
93
} /* }}} */
1422
1423
/* {{{ Get the inner iterator */
1424
PHP_METHOD(IteratorIterator, getInnerIterator)
1425
12
{
1426
12
  spl_dual_it_object   *intern;
1427
1428
12
  ZEND_PARSE_PARAMETERS_NONE();
1429
1430
12
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1431
1432
12
  if (!Z_ISUNDEF(intern->inner.zobject)) {
1433
12
    zval *value = &intern->inner.zobject;
1434
12
    RETURN_COPY_DEREF(value);
1435
12
  } else {
1436
0
    RETURN_NULL();
1437
0
  }
1438
12
} /* }}} */
1439
1440
static inline void spl_dual_it_free(spl_dual_it_object *intern)
1441
1.90k
{
1442
1.90k
  if (intern->inner.iterator && intern->inner.iterator->funcs->invalidate_current) {
1443
105
    intern->inner.iterator->funcs->invalidate_current(intern->inner.iterator);
1444
105
  }
1445
1.90k
  if (Z_TYPE(intern->current.data) != IS_UNDEF) {
1446
187
    zval_ptr_dtor(&intern->current.data);
1447
187
    ZVAL_UNDEF(&intern->current.data);
1448
187
  }
1449
1.90k
  if (Z_TYPE(intern->current.key) != IS_UNDEF) {
1450
187
    zval_ptr_dtor(&intern->current.key);
1451
187
    ZVAL_UNDEF(&intern->current.key);
1452
187
  }
1453
1.90k
  if (intern->dit_type == DIT_CachingIterator || intern->dit_type == DIT_RecursiveCachingIterator) {
1454
0
    if (intern->u.caching.zstr) {
1455
0
      zend_string_release(intern->u.caching.zstr);
1456
0
      intern->u.caching.zstr = NULL;
1457
0
    }
1458
0
    if (Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF) {
1459
0
      zval_ptr_dtor(&intern->u.caching.zchildren);
1460
0
      ZVAL_UNDEF(&intern->u.caching.zchildren);
1461
0
    }
1462
0
  }
1463
1.90k
}
1464
1465
static inline void spl_dual_it_rewind(spl_dual_it_object *intern)
1466
128
{
1467
128
  spl_dual_it_free(intern);
1468
128
  intern->current.pos = 0;
1469
128
  if (intern->inner.iterator && intern->inner.iterator->funcs->rewind) {
1470
128
    intern->inner.iterator->funcs->rewind(intern->inner.iterator);
1471
128
  }
1472
128
}
1473
1474
static inline zend_result spl_dual_it_valid(spl_dual_it_object *intern)
1475
247
{
1476
247
  if (!intern->inner.iterator) {
1477
0
    return FAILURE;
1478
0
  }
1479
  /* FAILURE / SUCCESS */
1480
247
  return intern->inner.iterator->funcs->valid(intern->inner.iterator);
1481
247
}
1482
1483
static inline zend_result spl_dual_it_fetch(spl_dual_it_object *intern, int check_more)
1484
246
{
1485
246
  zval *data;
1486
1487
246
  spl_dual_it_free(intern);
1488
246
  if (!check_more || spl_dual_it_valid(intern) == SUCCESS) {
1489
187
    data = intern->inner.iterator->funcs->get_current_data(intern->inner.iterator);
1490
187
    if (data) {
1491
187
      ZVAL_COPY(&intern->current.data, data);
1492
187
    }
1493
1494
187
    if (intern->inner.iterator->funcs->get_current_key) {
1495
187
      intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, &intern->current.key);
1496
187
      if (EG(exception)) {
1497
0
        zval_ptr_dtor(&intern->current.key);
1498
0
        ZVAL_UNDEF(&intern->current.key);
1499
0
      }
1500
187
    } else {
1501
0
      ZVAL_LONG(&intern->current.key, intern->current.pos);
1502
0
    }
1503
187
    return EG(exception) ? FAILURE : SUCCESS;
1504
187
  }
1505
59
  return FAILURE;
1506
246
}
1507
1508
static inline void spl_dual_it_next(spl_dual_it_object *intern, int do_free)
1509
120
{
1510
120
  if (do_free) {
1511
120
    spl_dual_it_free(intern);
1512
120
  } else if (!intern->inner.iterator) {
1513
0
    zend_throw_error(NULL, "The inner constructor wasn't initialized with an iterator instance");
1514
0
    return;
1515
0
  }
1516
120
  intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
1517
120
  intern->current.pos++;
1518
120
}
1519
1520
/* {{{ Rewind the iterator */
1521
PHP_METHOD(IteratorIterator, rewind)
1522
42
{
1523
42
  spl_dual_it_object   *intern;
1524
1525
42
  ZEND_PARSE_PARAMETERS_NONE();
1526
1527
42
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1528
1529
42
  spl_dual_it_rewind(intern);
1530
42
  spl_dual_it_fetch(intern, 1);
1531
42
} /* }}} */
1532
1533
/* {{{ Check whether the current element is valid */
1534
PHP_METHOD(IteratorIterator, valid)
1535
192
{
1536
192
  spl_dual_it_object   *intern;
1537
1538
192
  ZEND_PARSE_PARAMETERS_NONE();
1539
1540
192
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1541
1542
192
  RETURN_BOOL(Z_TYPE(intern->current.data) != IS_UNDEF);
1543
192
} /* }}} */
1544
1545
/* {{{ Get the current key */
1546
PHP_METHOD(IteratorIterator, key)
1547
55
{
1548
55
  spl_dual_it_object   *intern;
1549
1550
55
  ZEND_PARSE_PARAMETERS_NONE();
1551
1552
55
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1553
1554
55
  if (Z_TYPE(intern->current.key) != IS_UNDEF) {
1555
55
    RETURN_COPY_DEREF(&intern->current.key);
1556
55
  } else {
1557
0
    RETURN_NULL();
1558
0
  }
1559
55
} /* }}} */
1560
1561
/* {{{ Get the current element value */
1562
PHP_METHOD(IteratorIterator, current)
1563
145
{
1564
145
  spl_dual_it_object   *intern;
1565
1566
145
  ZEND_PARSE_PARAMETERS_NONE();
1567
1568
145
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1569
1570
145
  if (Z_TYPE(intern->current.data) != IS_UNDEF) {
1571
145
    RETURN_COPY_DEREF(&intern->current.data);
1572
145
  } else {
1573
0
    RETURN_NULL();
1574
0
  }
1575
145
} /* }}} */
1576
1577
/* {{{ Move the iterator forward */
1578
PHP_METHOD(IteratorIterator, next)
1579
91
{
1580
91
  spl_dual_it_object   *intern;
1581
1582
91
  ZEND_PARSE_PARAMETERS_NONE();
1583
1584
91
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1585
1586
91
  spl_dual_it_next(intern, 1);
1587
91
  spl_dual_it_fetch(intern, 1);
1588
91
} /* }}} */
1589
1590
static inline void spl_filter_it_fetch(zval *zthis, spl_dual_it_object *intern)
1591
111
{
1592
111
  zval retval;
1593
1594
111
  while (spl_dual_it_fetch(intern, 1) == SUCCESS) {
1595
82
    zend_call_method_with_0_params(Z_OBJ_P(zthis), intern->std.ce, NULL, "accept", &retval);
1596
82
    if (Z_TYPE(retval) != IS_UNDEF) {
1597
79
      if (zend_is_true(&retval)) {
1598
79
        zval_ptr_dtor(&retval);
1599
79
        return;
1600
79
      }
1601
0
      zval_ptr_dtor(&retval);
1602
0
    }
1603
3
    if (EG(exception)) {
1604
3
      return;
1605
3
    }
1606
0
    intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
1607
0
  }
1608
29
  spl_dual_it_free(intern);
1609
29
}
1610
1611
static inline void spl_filter_it_rewind(zval *zthis, spl_dual_it_object *intern)
1612
83
{
1613
83
  spl_dual_it_rewind(intern);
1614
83
  spl_filter_it_fetch(zthis, intern);
1615
83
}
1616
1617
static inline void spl_filter_it_next(zval *zthis, spl_dual_it_object *intern)
1618
28
{
1619
28
  spl_dual_it_next(intern, 1);
1620
28
  spl_filter_it_fetch(zthis, intern);
1621
28
}
1622
1623
/* {{{ Rewind the iterator */
1624
PHP_METHOD(FilterIterator, rewind)
1625
83
{
1626
83
  spl_dual_it_object   *intern;
1627
1628
83
  ZEND_PARSE_PARAMETERS_NONE();
1629
1630
83
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1631
83
  spl_filter_it_rewind(ZEND_THIS, intern);
1632
83
} /* }}} */
1633
1634
/* {{{ Move the iterator forward */
1635
PHP_METHOD(FilterIterator, next)
1636
28
{
1637
28
  spl_dual_it_object   *intern;
1638
1639
28
  ZEND_PARSE_PARAMETERS_NONE();
1640
1641
28
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1642
28
  spl_filter_it_next(ZEND_THIS, intern);
1643
28
} /* }}} */
1644
1645
/* {{{ Create a RecursiveCallbackFilterIterator from a RecursiveIterator */
1646
PHP_METHOD(RecursiveCallbackFilterIterator, __construct)
1647
2
{
1648
2
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveCallbackFilterIterator, spl_ce_RecursiveIterator, DIT_RecursiveCallbackFilterIterator);
1649
2
} /* }}} */
1650
1651
1652
/* {{{ Create a RecursiveFilterIterator from a RecursiveIterator */
1653
PHP_METHOD(RecursiveFilterIterator, __construct)
1654
0
{
1655
0
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveFilterIterator, spl_ce_RecursiveIterator, DIT_RecursiveFilterIterator);
1656
0
} /* }}} */
1657
1658
/* {{{ Check whether the inner iterator's current element has children */
1659
PHP_METHOD(RecursiveFilterIterator, hasChildren)
1660
0
{
1661
0
  spl_dual_it_object   *intern;
1662
1663
0
  ZEND_PARSE_PARAMETERS_NONE();
1664
1665
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1666
1667
0
  zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "haschildren", return_value);
1668
0
} /* }}} */
1669
1670
/* {{{ Return the inner iterator's children contained in a RecursiveFilterIterator */
1671
PHP_METHOD(RecursiveFilterIterator, getChildren)
1672
0
{
1673
0
  spl_dual_it_object   *intern;
1674
1675
0
  ZEND_PARSE_PARAMETERS_NONE();
1676
1677
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1678
1679
0
  zval childrens;
1680
0
  zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &childrens);
1681
0
  if (Z_TYPE(childrens) == IS_UNDEF) {
1682
0
    RETURN_THROWS();
1683
0
  }
1684
1685
0
  zend_result is_initialized = object_init_with_constructor(return_value, Z_OBJCE_P(ZEND_THIS), 1, &childrens, NULL);
1686
0
  zval_ptr_dtor(&childrens);
1687
0
  if (is_initialized == FAILURE) {
1688
0
    RETURN_THROWS();
1689
0
  }
1690
0
} /* }}} */
1691
1692
/* {{{ Return the inner iterator's children contained in a RecursiveCallbackFilterIterator */
1693
PHP_METHOD(RecursiveCallbackFilterIterator, getChildren)
1694
0
{
1695
0
  spl_dual_it_object *intern;
1696
1697
0
  ZEND_PARSE_PARAMETERS_NONE();
1698
1699
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1700
1701
0
  zval params[2];
1702
0
  zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &params[0]);
1703
0
  if (Z_TYPE(params[0]) == IS_UNDEF) {
1704
0
    RETURN_THROWS();
1705
0
  }
1706
1707
  /* Get callable to pass to the constructor */
1708
0
  zend_get_callable_zval_from_fcc(&intern->u.callback_filter, &params[1]);
1709
1710
0
  zend_result is_initialized = object_init_with_constructor(return_value, Z_OBJCE_P(ZEND_THIS), 2, params, NULL);
1711
0
  zval_ptr_dtor(&params[0]);
1712
0
  zval_ptr_dtor(&params[1]);
1713
0
  if (is_initialized == FAILURE) {
1714
0
    RETURN_THROWS();
1715
0
  }
1716
0
} /* }}} */
1717
/* {{{ Create a ParentIterator from a RecursiveIterator */
1718
PHP_METHOD(ParentIterator, __construct)
1719
2
{
1720
2
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_ParentIterator, spl_ce_RecursiveIterator, DIT_ParentIterator);
1721
2
} /* }}} */
1722
1723
/* {{{ Create an RegexIterator from another iterator and a regular expression */
1724
PHP_METHOD(RegexIterator, __construct)
1725
0
{
1726
0
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RegexIterator, zend_ce_iterator, DIT_RegexIterator);
1727
0
} /* }}} */
1728
1729
/* {{{ Calls the callback with the current value, the current key and the inner iterator as arguments */
1730
PHP_METHOD(CallbackFilterIterator, accept)
1731
82
{
1732
82
  spl_dual_it_object *intern;
1733
1734
82
  ZEND_PARSE_PARAMETERS_NONE();
1735
1736
82
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1737
1738
82
  if (Z_TYPE(intern->current.data) == IS_UNDEF || Z_TYPE(intern->current.key) == IS_UNDEF) {
1739
0
    RETURN_FALSE;
1740
0
  }
1741
1742
82
  zval params[3];
1743
82
  ZVAL_COPY_VALUE(&params[0], &intern->current.data);
1744
82
  ZVAL_COPY_VALUE(&params[1], &intern->current.key);
1745
82
  ZVAL_COPY_VALUE(&params[2], &intern->inner.zobject);
1746
1747
82
  zend_fcall_info_cache *fcc = &intern->u.callback_filter;
1748
1749
82
  zend_call_known_fcc(fcc, return_value, 3, params, NULL);
1750
82
  if (Z_ISUNDEF_P(return_value)) {
1751
3
    RETURN_FALSE;
1752
79
  } else if (Z_ISREF_P(return_value)) {
1753
12
    zend_unwrap_reference(return_value);
1754
12
  }
1755
82
}
1756
/* }}} */
1757
1758
/* {{{ Match (string)current() against regular expression */
1759
PHP_METHOD(RegexIterator, accept)
1760
0
{
1761
0
  spl_dual_it_object *intern;
1762
0
  zend_string *result, *subject;
1763
0
  size_t count = 0;
1764
0
  zval zcount, rv;
1765
0
  pcre2_match_data *match_data;
1766
0
  pcre2_code *re;
1767
0
  int rc;
1768
1769
0
  ZEND_PARSE_PARAMETERS_NONE();
1770
1771
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1772
1773
0
  if (Z_TYPE(intern->current.data) == IS_UNDEF) {
1774
0
    RETURN_FALSE;
1775
0
  }
1776
1777
0
  if (intern->u.regex.flags & REGIT_USE_KEY) {
1778
0
    subject = zval_get_string(&intern->current.key);
1779
0
  } else {
1780
0
    if (Z_TYPE(intern->current.data) == IS_ARRAY) {
1781
0
      RETURN_FALSE;
1782
0
    }
1783
0
    subject = zval_get_string(&intern->current.data);
1784
0
  }
1785
1786
  /* Exception during string conversion. */
1787
0
  if (EG(exception)) {
1788
0
    RETURN_THROWS();
1789
0
  }
1790
1791
0
  switch (intern->u.regex.mode)
1792
0
  {
1793
0
    case REGIT_MODE_MAX: /* won't happen but makes compiler happy */
1794
0
    case REGIT_MODE_MATCH:
1795
0
      re = php_pcre_pce_re(intern->u.regex.pce);
1796
0
      match_data = php_pcre_create_match_data(0, re);
1797
0
      if (!match_data) {
1798
0
        RETURN_FALSE;
1799
0
      }
1800
0
      rc = pcre2_match(re, (PCRE2_SPTR)ZSTR_VAL(subject), ZSTR_LEN(subject), 0, 0, match_data, php_pcre_mctx());
1801
0
      RETVAL_BOOL(rc >= 0);
1802
0
      php_pcre_free_match_data(match_data);
1803
0
      break;
1804
1805
0
    case REGIT_MODE_ALL_MATCHES:
1806
0
    case REGIT_MODE_GET_MATCH:
1807
0
      zval_ptr_dtor(&intern->current.data);
1808
0
      ZVAL_UNDEF(&intern->current.data);
1809
0
      php_pcre_match_impl(intern->u.regex.pce, subject, &zcount,
1810
0
        &intern->current.data, intern->u.regex.mode == REGIT_MODE_ALL_MATCHES, intern->u.regex.preg_flags, 0);
1811
0
      RETVAL_BOOL(Z_LVAL(zcount) > 0);
1812
0
      break;
1813
1814
0
    case REGIT_MODE_SPLIT:
1815
0
      zval_ptr_dtor(&intern->current.data);
1816
0
      ZVAL_UNDEF(&intern->current.data);
1817
0
      php_pcre_split_impl(intern->u.regex.pce, subject, &intern->current.data, -1, intern->u.regex.preg_flags);
1818
0
      count = zend_hash_num_elements(Z_ARRVAL(intern->current.data));
1819
0
      RETVAL_BOOL(count > 1);
1820
0
      break;
1821
1822
0
    case REGIT_MODE_REPLACE: {
1823
0
      zval *replacement = zend_read_property(intern->std.ce, Z_OBJ_P(ZEND_THIS), "replacement", sizeof("replacement")-1, 1, &rv);
1824
0
      zend_string *replacement_str = zval_try_get_string(replacement);
1825
1826
      /* Property type is ?string, so this should always succeed. */
1827
0
      ZEND_ASSERT(replacement_str != NULL);
1828
1829
0
      result = php_pcre_replace_impl(intern->u.regex.pce, subject, ZSTR_VAL(subject), ZSTR_LEN(subject), replacement_str, -1, &count);
1830
1831
0
      if (UNEXPECTED(!result)) {
1832
0
        zend_string_release(replacement_str);
1833
0
        zend_string_release_ex(subject, false);
1834
0
        RETURN_FALSE;
1835
0
      }
1836
1837
0
      if (intern->u.regex.flags & REGIT_USE_KEY) {
1838
0
        zval_ptr_dtor(&intern->current.key);
1839
0
        ZVAL_STR(&intern->current.key, result);
1840
0
      } else {
1841
0
        zval_ptr_dtor(&intern->current.data);
1842
0
        ZVAL_STR(&intern->current.data, result);
1843
0
      }
1844
1845
0
      zend_string_release(replacement_str);
1846
0
      RETVAL_BOOL(count > 0);
1847
0
    }
1848
0
  }
1849
1850
0
  if (intern->u.regex.flags & REGIT_INVERTED) {
1851
0
    RETVAL_BOOL(Z_TYPE_P(return_value) != IS_TRUE);
1852
0
  }
1853
0
  zend_string_release_ex(subject, false);
1854
0
} /* }}} */
1855
1856
/* {{{ Returns current regular expression */
1857
PHP_METHOD(RegexIterator, getRegex)
1858
0
{
1859
0
  spl_dual_it_object *intern;
1860
1861
0
  ZEND_PARSE_PARAMETERS_NONE();
1862
1863
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1864
1865
0
  RETURN_STR_COPY(intern->u.regex.regex);
1866
0
} /* }}} */
1867
1868
/* {{{ Returns current operation mode */
1869
PHP_METHOD(RegexIterator, getMode)
1870
0
{
1871
0
  spl_dual_it_object *intern;
1872
1873
0
  ZEND_PARSE_PARAMETERS_NONE();
1874
1875
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1876
1877
0
  RETURN_LONG(intern->u.regex.mode);
1878
0
} /* }}} */
1879
1880
/* {{{ Set new operation mode */
1881
PHP_METHOD(RegexIterator, setMode)
1882
0
{
1883
0
  spl_dual_it_object *intern;
1884
0
  zend_long mode;
1885
1886
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &mode) == FAILURE) {
1887
0
    RETURN_THROWS();
1888
0
  }
1889
1890
0
  if (mode < 0 || mode >= REGIT_MODE_MAX) {
1891
0
    zend_argument_value_error(1, "must be RegexIterator::MATCH, RegexIterator::GET_MATCH, "
1892
0
      "RegexIterator::ALL_MATCHES, RegexIterator::SPLIT, or RegexIterator::REPLACE");
1893
0
    RETURN_THROWS();
1894
0
  }
1895
1896
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1897
1898
0
  intern->u.regex.mode = mode;
1899
0
} /* }}} */
1900
1901
/* {{{ Returns current operation flags */
1902
PHP_METHOD(RegexIterator, getFlags)
1903
0
{
1904
0
  spl_dual_it_object *intern;
1905
1906
0
  ZEND_PARSE_PARAMETERS_NONE();
1907
1908
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1909
1910
0
  RETURN_LONG(intern->u.regex.flags);
1911
0
} /* }}} */
1912
1913
/* {{{ Set operation flags */
1914
PHP_METHOD(RegexIterator, setFlags)
1915
0
{
1916
0
  spl_dual_it_object *intern;
1917
0
  zend_long flags;
1918
1919
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &flags) == FAILURE) {
1920
0
    RETURN_THROWS();
1921
0
  }
1922
1923
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1924
1925
0
  intern->u.regex.flags = flags;
1926
0
} /* }}} */
1927
1928
/* {{{ Returns current PREG flags (if in use or NULL) */
1929
PHP_METHOD(RegexIterator, getPregFlags)
1930
0
{
1931
0
  spl_dual_it_object *intern;
1932
1933
0
  ZEND_PARSE_PARAMETERS_NONE();
1934
1935
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1936
1937
0
  RETURN_LONG(intern->u.regex.preg_flags);
1938
0
} /* }}} */
1939
1940
/* {{{ Set PREG flags */
1941
PHP_METHOD(RegexIterator, setPregFlags)
1942
0
{
1943
0
  spl_dual_it_object *intern;
1944
0
  zend_long preg_flags;
1945
1946
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &preg_flags) == FAILURE) {
1947
0
    RETURN_THROWS();
1948
0
  }
1949
1950
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1951
1952
0
  intern->u.regex.preg_flags = preg_flags;
1953
0
} /* }}} */
1954
1955
/* {{{ Create an RecursiveRegexIterator from another recursive iterator and a regular expression */
1956
PHP_METHOD(RecursiveRegexIterator, __construct)
1957
0
{
1958
0
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveRegexIterator, spl_ce_RecursiveIterator, DIT_RecursiveRegexIterator);
1959
0
} /* }}} */
1960
1961
/* {{{ Return the inner iterator's children contained in a RecursiveRegexIterator */
1962
PHP_METHOD(RecursiveRegexIterator, getChildren)
1963
0
{
1964
0
  spl_dual_it_object   *intern;
1965
0
  zval                 retval;
1966
1967
0
  ZEND_PARSE_PARAMETERS_NONE();
1968
1969
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
1970
1971
0
  zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &retval);
1972
0
  if (EG(exception)) {
1973
0
    zval_ptr_dtor(&retval);
1974
0
    RETURN_THROWS();
1975
0
  }
1976
1977
0
  zval args[5];
1978
0
  ZVAL_COPY_VALUE(&args[0], &retval);
1979
0
  ZVAL_STR_COPY(&args[1], intern->u.regex.regex);
1980
0
  ZVAL_LONG(&args[2], intern->u.regex.mode);
1981
0
  ZVAL_LONG(&args[3], intern->u.regex.flags);
1982
0
  ZVAL_LONG(&args[4], intern->u.regex.preg_flags);
1983
1984
0
  zend_result is_initialized = object_init_with_constructor(return_value, Z_OBJCE_P(ZEND_THIS), 5, args, NULL);
1985
1986
0
  zval_ptr_dtor(&args[0]);
1987
0
  zval_ptr_dtor_str(&args[1]);
1988
0
  if (is_initialized == FAILURE) {
1989
0
    RETURN_THROWS();
1990
0
  }
1991
0
} /* }}} */
1992
1993
PHP_METHOD(RecursiveRegexIterator, accept)
1994
0
{
1995
0
  spl_dual_it_object *intern;
1996
1997
0
  ZEND_PARSE_PARAMETERS_NONE();
1998
1999
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2000
2001
0
  if (Z_TYPE(intern->current.data) == IS_UNDEF) {
2002
0
    RETURN_FALSE;
2003
0
  } else if (Z_TYPE(intern->current.data) == IS_ARRAY) {
2004
0
    RETURN_BOOL(zend_hash_num_elements(Z_ARRVAL(intern->current.data)) > 0);
2005
0
  }
2006
2007
0
  zend_call_method_with_0_params(Z_OBJ_P(ZEND_THIS), spl_ce_RegexIterator, NULL, "accept", return_value);
2008
0
}
2009
2010
/* {{{ spl_dual_it_free_storage */
2011
static void spl_dual_it_free_storage(zend_object *_object)
2012
1.38k
{
2013
1.38k
  spl_dual_it_object *object = spl_dual_it_from_obj(_object);
2014
2015
1.38k
  spl_dual_it_free(object);
2016
2017
1.38k
  if (object->inner.iterator) {
2018
144
    zend_iterator_dtor(object->inner.iterator);
2019
144
  }
2020
2021
1.38k
  if (!Z_ISUNDEF(object->inner.zobject)) {
2022
144
    zval_ptr_dtor(&object->inner.zobject);
2023
144
  }
2024
2025
1.38k
  if (object->dit_type == DIT_AppendIterator) {
2026
0
    zend_iterator_dtor(object->u.append.iterator);
2027
0
    if (Z_TYPE(object->u.append.zarrayit) != IS_UNDEF) {
2028
0
      zval_ptr_dtor(&object->u.append.zarrayit);
2029
0
    }
2030
0
  }
2031
2032
1.38k
  if (object->dit_type == DIT_CachingIterator || object->dit_type == DIT_RecursiveCachingIterator) {
2033
0
    zval_ptr_dtor(&object->u.caching.zcache);
2034
0
  }
2035
2036
1.38k
  if (object->dit_type == DIT_RegexIterator || object->dit_type == DIT_RecursiveRegexIterator) {
2037
0
    if (object->u.regex.pce) {
2038
0
      php_pcre_pce_decref(object->u.regex.pce);
2039
0
    }
2040
0
    if (object->u.regex.regex) {
2041
0
      zend_string_release_ex(object->u.regex.regex, 0);
2042
0
    }
2043
0
  }
2044
2045
1.38k
  if (object->dit_type == DIT_CallbackFilterIterator || object->dit_type == DIT_RecursiveCallbackFilterIterator) {
2046
86
    if (ZEND_FCC_INITIALIZED(object->u.callback_filter)) {
2047
86
      zend_fcc_dtor(&object->u.callback_filter);
2048
86
    }
2049
86
  }
2050
2051
1.38k
  zend_object_std_dtor(&object->std);
2052
1.38k
}
2053
/* }}} */
2054
2055
static HashTable *spl_dual_it_get_gc(zend_object *obj, zval **table, int *n)
2056
2.21k
{
2057
2.21k
  spl_dual_it_object *object = spl_dual_it_from_obj(obj);
2058
2.21k
  zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
2059
2060
2.21k
  if (object->inner.iterator) {
2061
406
    zend_get_gc_buffer_add_obj(gc_buffer, &object->inner.iterator->std);
2062
406
  }
2063
2064
2.21k
  zend_get_gc_buffer_add_zval(gc_buffer, &object->current.data);
2065
2.21k
  zend_get_gc_buffer_add_zval(gc_buffer, &object->current.key);
2066
2.21k
  zend_get_gc_buffer_add_zval(gc_buffer, &object->inner.zobject);
2067
2068
2.21k
  switch (object->dit_type) {
2069
1.80k
    case DIT_Unknown:
2070
1.80k
    case DIT_Default:
2071
1.89k
    case DIT_IteratorIterator:
2072
1.89k
    case DIT_NoRewindIterator:
2073
1.89k
    case DIT_InfiniteIterator:
2074
1.89k
    case DIT_LimitIterator:
2075
1.89k
    case DIT_RegexIterator:
2076
1.89k
    case DIT_RecursiveRegexIterator:
2077
      /* Nothing to do */
2078
1.89k
      break;
2079
0
    case DIT_AppendIterator:
2080
0
      zend_get_gc_buffer_add_obj(gc_buffer, &object->u.append.iterator->std);
2081
0
      if (Z_TYPE(object->u.append.zarrayit) != IS_UNDEF) {
2082
0
        zend_get_gc_buffer_add_zval(gc_buffer, &object->u.append.zarrayit);
2083
0
      }
2084
0
      break;
2085
0
    case DIT_CachingIterator:
2086
0
    case DIT_RecursiveCachingIterator:
2087
0
      zend_get_gc_buffer_add_zval(gc_buffer, &object->u.caching.zcache);
2088
0
      zend_get_gc_buffer_add_zval(gc_buffer, &object->u.caching.zchildren);
2089
0
      break;
2090
318
    case DIT_CallbackFilterIterator:
2091
318
    case DIT_RecursiveCallbackFilterIterator:
2092
318
      if (ZEND_FCC_INITIALIZED(object->u.callback_filter)) {
2093
318
        zend_get_gc_buffer_add_fcc(gc_buffer, &object->u.callback_filter);
2094
318
      }
2095
318
      break;
2096
2.21k
  }
2097
2098
2.21k
  zend_get_gc_buffer_use(gc_buffer, table, n);
2099
2.21k
  return zend_std_get_properties(obj);
2100
2.21k
}
2101
2102
/* {{{ spl_dual_it_new */
2103
static zend_object *spl_dual_it_new(zend_class_entry *class_type)
2104
1.38k
{
2105
1.38k
  spl_dual_it_object *intern;
2106
2107
1.38k
  intern = zend_object_alloc(sizeof(spl_dual_it_object), class_type);
2108
1.38k
  intern->dit_type = DIT_Unknown;
2109
2110
1.38k
  zend_object_std_init(&intern->std, class_type);
2111
1.38k
  object_properties_init(&intern->std, class_type);
2112
2113
1.38k
  return &intern->std;
2114
1.38k
}
2115
/* }}} */
2116
2117
/* Returns the relative position for the current iterator position. */
2118
static zend_long spl_limit_it_relative_pos(spl_dual_it_object *intern)
2119
0
{
2120
0
  return intern->current.pos - intern->u.limit.offset;
2121
0
}
2122
2123
/* Returns the relative position for an arbitrary position. */
2124
static zend_long spl_limit_it_relative_pos_for(spl_dual_it_object *intern, zend_long pos)
2125
3
{
2126
3
  return pos - intern->u.limit.offset;
2127
3
}
2128
2129
static inline zend_result spl_limit_it_valid(spl_dual_it_object *intern)
2130
0
{
2131
  /* FAILURE / SUCCESS */
2132
0
  if (intern->u.limit.count != -1 &&
2133
0
    spl_limit_it_relative_pos(intern) >= intern->u.limit.count) {
2134
0
    return FAILURE;
2135
0
  }
2136
2137
0
  return spl_dual_it_valid(intern);
2138
0
}
2139
2140
static inline void spl_limit_it_seek(spl_dual_it_object *intern, zend_long pos)
2141
3
{
2142
3
  zval  zpos;
2143
2144
3
  spl_dual_it_free(intern);
2145
3
  if (pos < intern->u.limit.offset) {
2146
0
    zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is below the offset " ZEND_LONG_FMT, pos, intern->u.limit.offset);
2147
0
    return;
2148
0
  }
2149
3
  if (spl_limit_it_relative_pos_for(intern, pos) >= intern->u.limit.count && intern->u.limit.count != -1) {
2150
1
    zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is behind offset " ZEND_LONG_FMT " plus count " ZEND_LONG_FMT, pos, intern->u.limit.offset, intern->u.limit.count);
2151
1
    return;
2152
1
  }
2153
2
  if (pos != intern->current.pos && instanceof_function(intern->inner.ce, spl_ce_SeekableIterator)) {
2154
1
    ZVAL_LONG(&zpos, pos);
2155
1
    spl_dual_it_free(intern);
2156
1
    zend_call_method_with_1_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "seek", NULL, &zpos);
2157
1
    if (!EG(exception)) {
2158
0
      intern->current.pos = pos;
2159
0
      if (spl_limit_it_valid(intern) == SUCCESS) {
2160
0
        spl_dual_it_fetch(intern, 0);
2161
0
      }
2162
0
    }
2163
1
  } else {
2164
    /* emulate the forward seek, by next() calls */
2165
    /* a back ward seek is done by a previous rewind() */
2166
1
    if (pos < intern->current.pos) {
2167
0
      spl_dual_it_rewind(intern);
2168
0
    }
2169
1
    while (pos > intern->current.pos && spl_dual_it_valid(intern) == SUCCESS) {
2170
0
      spl_dual_it_next(intern, 1);
2171
0
    }
2172
1
    if (spl_dual_it_valid(intern) == SUCCESS) {
2173
1
      spl_dual_it_fetch(intern, 1);
2174
1
    }
2175
1
  }
2176
2
}
2177
2178
/* {{{ Construct a LimitIterator from an Iterator with a given starting offset and optionally a maximum count */
2179
PHP_METHOD(LimitIterator, __construct)
2180
6
{
2181
6
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_LimitIterator, zend_ce_iterator, DIT_LimitIterator);
2182
6
} /* }}} */
2183
2184
/* {{{ Rewind the iterator to the specified starting offset */
2185
PHP_METHOD(LimitIterator, rewind)
2186
3
{
2187
3
  spl_dual_it_object   *intern;
2188
2189
3
  ZEND_PARSE_PARAMETERS_NONE();
2190
2191
3
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2192
3
  spl_dual_it_rewind(intern);
2193
3
  spl_limit_it_seek(intern, intern->u.limit.offset);
2194
3
} /* }}} */
2195
2196
/* {{{ Check whether the current element is valid */
2197
PHP_METHOD(LimitIterator, valid)
2198
2
{
2199
2
  spl_dual_it_object   *intern;
2200
2201
2
  ZEND_PARSE_PARAMETERS_NONE();
2202
2203
2
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2204
2205
/*  RETURN_BOOL(spl_limit_it_valid(intern) == SUCCESS);*/
2206
2
  RETURN_BOOL((intern->u.limit.count == -1 || spl_limit_it_relative_pos(intern) < intern->u.limit.count) && Z_TYPE(intern->current.data) != IS_UNDEF);
2207
2
} /* }}} */
2208
2209
/* {{{ Move the iterator forward */
2210
PHP_METHOD(LimitIterator, next)
2211
1
{
2212
1
  spl_dual_it_object   *intern;
2213
2214
1
  ZEND_PARSE_PARAMETERS_NONE();
2215
2216
1
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2217
2218
1
  spl_dual_it_next(intern, 1);
2219
1
  if (intern->u.limit.count == -1 || spl_limit_it_relative_pos(intern) < intern->u.limit.count) {
2220
1
    spl_dual_it_fetch(intern, 1);
2221
1
  }
2222
1
} /* }}} */
2223
2224
/* {{{ Seek to the given position */
2225
PHP_METHOD(LimitIterator, seek)
2226
0
{
2227
0
  spl_dual_it_object   *intern;
2228
0
  zend_long                 pos;
2229
2230
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &pos) == FAILURE) {
2231
0
    RETURN_THROWS();
2232
0
  }
2233
2234
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2235
0
  spl_limit_it_seek(intern, pos);
2236
0
  RETURN_LONG(intern->current.pos);
2237
0
} /* }}} */
2238
2239
/* {{{ Return the current position */
2240
PHP_METHOD(LimitIterator, getPosition)
2241
0
{
2242
0
  spl_dual_it_object   *intern;
2243
2244
0
  ZEND_PARSE_PARAMETERS_NONE();
2245
2246
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2247
0
  RETURN_LONG(intern->current.pos);
2248
0
} /* }}} */
2249
2250
static inline int spl_caching_it_valid(spl_dual_it_object *intern)
2251
0
{
2252
0
  return intern->u.caching.flags & CIT_VALID ? SUCCESS : FAILURE;
2253
0
}
2254
2255
static inline int spl_caching_it_has_next(spl_dual_it_object *intern)
2256
0
{
2257
0
  return spl_dual_it_valid(intern);
2258
0
}
2259
2260
static inline void spl_caching_it_next(spl_dual_it_object *intern)
2261
0
{
2262
0
  if (spl_dual_it_fetch(intern, 1) == SUCCESS) {
2263
0
    intern->u.caching.flags |= CIT_VALID;
2264
    /* Full cache ? */
2265
0
    if (intern->u.caching.flags & CIT_FULL_CACHE) {
2266
0
      zval *key = &intern->current.key;
2267
0
      zval *data = &intern->current.data;
2268
2269
0
      ZVAL_DEREF(data);
2270
0
      array_set_zval_key(Z_ARRVAL(intern->u.caching.zcache), key, data);
2271
0
    }
2272
    /* Recursion ? */
2273
0
    if (intern->dit_type == DIT_RecursiveCachingIterator) {
2274
0
      zval retval;
2275
0
      zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "haschildren", &retval);
2276
0
      if (EG(exception)) {
2277
0
        zval_ptr_dtor(&retval);
2278
0
        if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2279
0
          zend_clear_exception();
2280
0
        } else {
2281
0
          return;
2282
0
        }
2283
0
      } else {
2284
0
        bool has_children = zend_is_true(&retval);
2285
0
        zval_ptr_dtor(&retval);
2286
2287
0
        if (has_children) {
2288
0
          zval args[2];
2289
2290
          /* Store the children in the first constructor argument */
2291
0
          zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &args[0]);
2292
0
          if (EG(exception)) {
2293
0
            zval_ptr_dtor(&args[0]);
2294
0
            if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2295
0
              zend_clear_exception();
2296
0
            } else {
2297
0
              return;
2298
0
            }
2299
0
          } else {
2300
0
            ZVAL_LONG(&args[1], intern->u.caching.flags & CIT_PUBLIC);
2301
2302
0
            zend_result is_initialized = object_init_with_constructor(
2303
0
              &intern->u.caching.zchildren,
2304
0
              spl_ce_RecursiveCachingIterator,
2305
0
              2,
2306
0
              args,
2307
0
              NULL
2308
0
            );
2309
0
            zval_ptr_dtor(&args[0]);
2310
0
            if (is_initialized == FAILURE) {
2311
0
              if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2312
0
                zend_clear_exception();
2313
0
              } else {
2314
0
                return;
2315
0
              }
2316
0
            }
2317
0
          }
2318
0
        }
2319
0
      }
2320
0
    }
2321
0
    if (intern->u.caching.flags & (CIT_TOSTRING_USE_INNER|CIT_CALL_TOSTRING)) {
2322
0
      if (intern->u.caching.flags & CIT_TOSTRING_USE_INNER) {
2323
0
        intern->u.caching.zstr = zval_get_string(&intern->inner.zobject);
2324
0
      } else {
2325
0
        intern->u.caching.zstr = zval_get_string(&intern->current.data);
2326
0
      }
2327
0
    }
2328
0
    spl_dual_it_next(intern, 0);
2329
0
  } else {
2330
0
    intern->u.caching.flags &= ~CIT_VALID;
2331
0
  }
2332
0
}
2333
2334
static inline void spl_caching_it_rewind(spl_dual_it_object *intern)
2335
0
{
2336
0
  spl_dual_it_rewind(intern);
2337
0
  zend_hash_clean(Z_ARRVAL(intern->u.caching.zcache));
2338
0
  spl_caching_it_next(intern);
2339
0
}
2340
2341
/* {{{ Construct a CachingIterator from an Iterator */
2342
PHP_METHOD(CachingIterator, __construct)
2343
2
{
2344
2
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_CachingIterator, zend_ce_iterator, DIT_CachingIterator);
2345
2
} /* }}} */
2346
2347
/* {{{ Rewind the iterator */
2348
PHP_METHOD(CachingIterator, rewind)
2349
0
{
2350
0
  spl_dual_it_object   *intern;
2351
2352
0
  ZEND_PARSE_PARAMETERS_NONE();
2353
2354
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2355
2356
0
  spl_caching_it_rewind(intern);
2357
0
} /* }}} */
2358
2359
/* {{{ Check whether the current element is valid */
2360
PHP_METHOD(CachingIterator, valid)
2361
0
{
2362
0
  spl_dual_it_object   *intern;
2363
2364
0
  ZEND_PARSE_PARAMETERS_NONE();
2365
2366
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2367
2368
0
  RETURN_BOOL(spl_caching_it_valid(intern) == SUCCESS);
2369
0
} /* }}} */
2370
2371
/* {{{ Move the iterator forward */
2372
PHP_METHOD(CachingIterator, next)
2373
0
{
2374
0
  spl_dual_it_object   *intern;
2375
2376
0
  ZEND_PARSE_PARAMETERS_NONE();
2377
2378
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2379
2380
0
  spl_caching_it_next(intern);
2381
0
} /* }}} */
2382
2383
/* {{{ Check whether the inner iterator has a valid next element */
2384
PHP_METHOD(CachingIterator, hasNext)
2385
0
{
2386
0
  spl_dual_it_object   *intern;
2387
2388
0
  ZEND_PARSE_PARAMETERS_NONE();
2389
2390
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2391
2392
0
  RETURN_BOOL(spl_caching_it_has_next(intern) == SUCCESS);
2393
0
} /* }}} */
2394
2395
/* {{{ Return the string representation of the current element */
2396
PHP_METHOD(CachingIterator, __toString)
2397
0
{
2398
0
  spl_dual_it_object *intern;
2399
2400
0
  ZEND_PARSE_PARAMETERS_NONE();
2401
2402
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2403
2404
0
  if (!(intern->u.caching.flags & (CIT_CALL_TOSTRING|CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT|CIT_TOSTRING_USE_INNER))) {
2405
0
    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not fetch string value (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2406
0
    RETURN_THROWS();
2407
0
  }
2408
2409
0
  if (intern->u.caching.flags & CIT_TOSTRING_USE_KEY) {
2410
0
    ZVAL_COPY(return_value, &intern->current.key);
2411
0
    convert_to_string(return_value);
2412
0
    return;
2413
0
  } else if (intern->u.caching.flags & CIT_TOSTRING_USE_CURRENT) {
2414
0
    ZVAL_COPY(return_value, &intern->current.data);
2415
0
    convert_to_string(return_value);
2416
0
    return;
2417
0
  }
2418
0
  if (intern->u.caching.zstr) {
2419
0
    RETURN_STR_COPY(intern->u.caching.zstr);
2420
0
  } else {
2421
0
    RETURN_EMPTY_STRING();
2422
0
  }
2423
0
} /* }}} */
2424
2425
/* {{{ Set given index in cache */
2426
PHP_METHOD(CachingIterator, offsetSet)
2427
0
{
2428
0
  spl_dual_it_object   *intern;
2429
0
  zend_string *key;
2430
0
  zval *value;
2431
2432
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &key, &value) == FAILURE) {
2433
0
    RETURN_THROWS();
2434
0
  }
2435
2436
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2437
2438
0
  if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2439
0
    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2440
0
    RETURN_THROWS();
2441
0
  }
2442
2443
0
  Z_TRY_ADDREF_P(value);
2444
0
  zend_symtable_update(Z_ARRVAL(intern->u.caching.zcache), key, value);
2445
0
}
2446
/* }}} */
2447
2448
/* {{{ Return the internal cache if used */
2449
PHP_METHOD(CachingIterator, offsetGet)
2450
0
{
2451
0
  spl_dual_it_object   *intern;
2452
0
  zend_string *key;
2453
0
  zval *value;
2454
2455
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &key) == FAILURE) {
2456
0
    RETURN_THROWS();
2457
0
  }
2458
2459
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2460
2461
0
  if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2462
0
    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2463
0
    RETURN_THROWS();
2464
0
  }
2465
2466
0
  if ((value = zend_symtable_find(Z_ARRVAL(intern->u.caching.zcache), key)) == NULL) {
2467
0
    zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(key));
2468
0
    return;
2469
0
  }
2470
2471
0
  RETURN_COPY_DEREF(value);
2472
0
}
2473
/* }}} */
2474
2475
/* {{{ Unset given index in cache */
2476
PHP_METHOD(CachingIterator, offsetUnset)
2477
0
{
2478
0
  spl_dual_it_object   *intern;
2479
0
  zend_string *key;
2480
2481
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2482
2483
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &key) == FAILURE) {
2484
0
    RETURN_THROWS();
2485
0
  }
2486
2487
0
  if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2488
0
    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2489
0
    RETURN_THROWS();
2490
0
  }
2491
2492
0
  zend_symtable_del(Z_ARRVAL(intern->u.caching.zcache), key);
2493
0
}
2494
/* }}} */
2495
2496
/* {{{ Return whether the requested index exists */
2497
PHP_METHOD(CachingIterator, offsetExists)
2498
0
{
2499
0
  spl_dual_it_object   *intern;
2500
0
  zend_string *key;
2501
2502
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &key) == FAILURE) {
2503
0
    RETURN_THROWS();
2504
0
  }
2505
2506
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2507
2508
0
  if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2509
0
    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2510
0
    RETURN_THROWS();
2511
0
  }
2512
2513
0
  RETURN_BOOL(zend_symtable_exists(Z_ARRVAL(intern->u.caching.zcache), key));
2514
0
}
2515
/* }}} */
2516
2517
/* {{{ Return the cache */
2518
PHP_METHOD(CachingIterator, getCache)
2519
0
{
2520
0
  spl_dual_it_object *intern;
2521
2522
0
  ZEND_PARSE_PARAMETERS_NONE();
2523
2524
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2525
2526
0
  if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2527
0
    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2528
0
    RETURN_THROWS();
2529
0
  }
2530
2531
0
  ZVAL_COPY(return_value, &intern->u.caching.zcache);
2532
0
}
2533
/* }}} */
2534
2535
/* {{{ Return the internal flags */
2536
PHP_METHOD(CachingIterator, getFlags)
2537
0
{
2538
0
  spl_dual_it_object   *intern;
2539
2540
0
  ZEND_PARSE_PARAMETERS_NONE();
2541
2542
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2543
2544
0
  RETURN_LONG(intern->u.caching.flags);
2545
0
}
2546
/* }}} */
2547
2548
/* {{{ Set the internal flags */
2549
PHP_METHOD(CachingIterator, setFlags)
2550
0
{
2551
0
  spl_dual_it_object   *intern;
2552
0
  zend_long flags;
2553
2554
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &flags) == FAILURE) {
2555
0
    RETURN_THROWS();
2556
0
  }
2557
2558
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2559
2560
0
  if (spl_cit_check_flags(flags) != SUCCESS) {
2561
0
    zend_argument_value_error(1, "must contain only one of CachingIterator::CALL_TOSTRING, "
2562
0
      "CachingIterator::TOSTRING_USE_KEY, CachingIterator::TOSTRING_USE_CURRENT, "
2563
0
      "or CachingIterator::TOSTRING_USE_INNER");
2564
0
    RETURN_THROWS();
2565
0
  }
2566
0
  if ((intern->u.caching.flags & CIT_CALL_TOSTRING) != 0 && (flags & CIT_CALL_TOSTRING) == 0) {
2567
0
    zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag CALL_TO_STRING is not possible", 0);
2568
0
    RETURN_THROWS();
2569
0
  }
2570
0
  if ((intern->u.caching.flags & CIT_TOSTRING_USE_INNER) != 0 && (flags & CIT_TOSTRING_USE_INNER) == 0) {
2571
0
    zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag TOSTRING_USE_INNER is not possible", 0);
2572
0
    RETURN_THROWS();
2573
0
  }
2574
0
  if ((flags & CIT_FULL_CACHE) != 0 && (intern->u.caching.flags & CIT_FULL_CACHE) == 0) {
2575
    /* clear on (re)enable */
2576
0
    zend_hash_clean(Z_ARRVAL(intern->u.caching.zcache));
2577
0
  }
2578
0
  intern->u.caching.flags = (intern->u.caching.flags & ~CIT_PUBLIC) | (flags & CIT_PUBLIC);
2579
0
}
2580
/* }}} */
2581
2582
/* {{{ Number of cached elements */
2583
PHP_METHOD(CachingIterator, count)
2584
0
{
2585
0
  spl_dual_it_object   *intern;
2586
2587
0
  ZEND_PARSE_PARAMETERS_NONE();
2588
2589
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2590
2591
0
  if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2592
0
    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2593
0
    RETURN_THROWS();
2594
0
  }
2595
2596
0
  RETURN_LONG(zend_hash_num_elements(Z_ARRVAL(intern->u.caching.zcache)));
2597
0
}
2598
/* }}} */
2599
2600
/* {{{ Create an iterator from a RecursiveIterator */
2601
PHP_METHOD(RecursiveCachingIterator, __construct)
2602
2
{
2603
2
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveCachingIterator, spl_ce_RecursiveIterator, DIT_RecursiveCachingIterator);
2604
2
} /* }}} */
2605
2606
/* {{{ Check whether the current element of the inner iterator has children */
2607
PHP_METHOD(RecursiveCachingIterator, hasChildren)
2608
0
{
2609
0
  spl_dual_it_object   *intern;
2610
2611
0
  ZEND_PARSE_PARAMETERS_NONE();
2612
2613
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2614
2615
0
  RETURN_BOOL(Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF);
2616
0
} /* }}} */
2617
2618
/* {{{ Return the inner iterator's children as a RecursiveCachingIterator */
2619
PHP_METHOD(RecursiveCachingIterator, getChildren)
2620
0
{
2621
0
  spl_dual_it_object   *intern;
2622
2623
0
  ZEND_PARSE_PARAMETERS_NONE();
2624
2625
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2626
2627
0
  if (Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF) {
2628
0
    zval *value = &intern->u.caching.zchildren;
2629
2630
0
    RETURN_COPY_DEREF(value);
2631
0
  } else {
2632
0
    RETURN_NULL();
2633
0
  }
2634
0
} /* }}} */
2635
2636
/* {{{ Create an iterator from anything that is traversable */
2637
PHP_METHOD(IteratorIterator, __construct)
2638
59
{
2639
59
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_IteratorIterator, zend_ce_traversable, DIT_IteratorIterator);
2640
59
} /* }}} */
2641
2642
/* {{{ Create an iterator from another iterator */
2643
PHP_METHOD(NoRewindIterator, __construct)
2644
2
{
2645
2
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_NoRewindIterator, zend_ce_iterator, DIT_NoRewindIterator);
2646
2
} /* }}} */
2647
2648
/* {{{ Prevent a call to inner iterators rewind() */
2649
PHP_METHOD(NoRewindIterator, rewind)
2650
0
{
2651
0
  ZEND_PARSE_PARAMETERS_NONE();
2652
  /* nothing to do */
2653
0
} /* }}} */
2654
2655
/* {{{ Return inner iterators valid() */
2656
PHP_METHOD(NoRewindIterator, valid)
2657
0
{
2658
0
  spl_dual_it_object   *intern;
2659
2660
0
  ZEND_PARSE_PARAMETERS_NONE();
2661
2662
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2663
0
  RETURN_BOOL(intern->inner.iterator->funcs->valid(intern->inner.iterator) == SUCCESS);
2664
0
} /* }}} */
2665
2666
/* {{{ Return inner iterators key() */
2667
PHP_METHOD(NoRewindIterator, key)
2668
0
{
2669
0
  spl_dual_it_object   *intern;
2670
2671
0
  ZEND_PARSE_PARAMETERS_NONE();
2672
2673
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2674
2675
0
  if (intern->inner.iterator->funcs->get_current_key) {
2676
0
    intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, return_value);
2677
0
  } else {
2678
0
    RETURN_NULL();
2679
0
  }
2680
0
} /* }}} */
2681
2682
/* {{{ Return inner iterators current() */
2683
PHP_METHOD(NoRewindIterator, current)
2684
0
{
2685
0
  spl_dual_it_object   *intern;
2686
0
  zval *data;
2687
2688
0
  ZEND_PARSE_PARAMETERS_NONE();
2689
2690
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2691
0
  data = intern->inner.iterator->funcs->get_current_data(intern->inner.iterator);
2692
0
  if (data) {
2693
0
    RETURN_COPY_DEREF(data);
2694
0
  }
2695
0
} /* }}} */
2696
2697
/* {{{ Return inner iterators next() */
2698
PHP_METHOD(NoRewindIterator, next)
2699
0
{
2700
0
  spl_dual_it_object   *intern;
2701
2702
0
  ZEND_PARSE_PARAMETERS_NONE();
2703
2704
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2705
0
  intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
2706
0
} /* }}} */
2707
2708
/* {{{ Create an iterator from another iterator */
2709
PHP_METHOD(InfiniteIterator, __construct)
2710
0
{
2711
0
  spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_InfiniteIterator, zend_ce_iterator, DIT_InfiniteIterator);
2712
0
} /* }}} */
2713
2714
/* {{{ Prevent a call to inner iterators rewind() (internally the current data will be fetched if valid()) */
2715
PHP_METHOD(InfiniteIterator, next)
2716
0
{
2717
0
  spl_dual_it_object   *intern;
2718
2719
0
  ZEND_PARSE_PARAMETERS_NONE();
2720
2721
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2722
2723
0
  spl_dual_it_next(intern, 1);
2724
0
  if (spl_dual_it_valid(intern) == SUCCESS) {
2725
0
    spl_dual_it_fetch(intern, 0);
2726
0
  } else {
2727
0
    spl_dual_it_rewind(intern);
2728
0
    if (spl_dual_it_valid(intern) == SUCCESS) {
2729
0
      spl_dual_it_fetch(intern, 0);
2730
0
    }
2731
0
  }
2732
0
} /* }}} */
2733
2734
/* {{{ Does nothing  */
2735
PHP_METHOD(EmptyIterator, rewind)
2736
0
{
2737
0
  ZEND_PARSE_PARAMETERS_NONE();
2738
0
} /* }}} */
2739
2740
/* {{{ Return false */
2741
PHP_METHOD(EmptyIterator, valid)
2742
0
{
2743
0
  ZEND_PARSE_PARAMETERS_NONE();
2744
0
  RETURN_FALSE;
2745
0
} /* }}} */
2746
2747
/* {{{ Throws exception BadMethodCallException */
2748
PHP_METHOD(EmptyIterator, key)
2749
0
{
2750
0
  ZEND_PARSE_PARAMETERS_NONE();
2751
0
  zend_throw_exception(spl_ce_BadMethodCallException, "Accessing the key of an EmptyIterator", 0);
2752
0
} /* }}} */
2753
2754
/* {{{ Throws exception BadMethodCallException */
2755
PHP_METHOD(EmptyIterator, current)
2756
0
{
2757
0
  ZEND_PARSE_PARAMETERS_NONE();
2758
0
  zend_throw_exception(spl_ce_BadMethodCallException, "Accessing the value of an EmptyIterator", 0);
2759
0
} /* }}} */
2760
2761
/* {{{ Does nothing */
2762
PHP_METHOD(EmptyIterator, next)
2763
0
{
2764
0
  ZEND_PARSE_PARAMETERS_NONE();
2765
0
} /* }}} */
2766
2767
static zend_result spl_append_it_next_iterator(spl_dual_it_object *intern) /* {{{*/
2768
0
{
2769
0
  spl_dual_it_free(intern);
2770
2771
0
  if (!Z_ISUNDEF(intern->inner.zobject)) {
2772
0
    zval_ptr_dtor(&intern->inner.zobject);
2773
0
    ZVAL_UNDEF(&intern->inner.zobject);
2774
0
    intern->inner.ce = NULL;
2775
0
    if (intern->inner.iterator) {
2776
0
      zend_iterator_dtor(intern->inner.iterator);
2777
0
      intern->inner.iterator = NULL;
2778
0
    }
2779
0
  }
2780
0
  if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) == SUCCESS) {
2781
0
    zval *it;
2782
2783
0
    it  = intern->u.append.iterator->funcs->get_current_data(intern->u.append.iterator);
2784
0
    ZVAL_COPY(&intern->inner.zobject, it);
2785
0
    intern->inner.ce = Z_OBJCE_P(it);
2786
0
    intern->inner.iterator = intern->inner.ce->get_iterator(intern->inner.ce, it, 0);
2787
0
    spl_dual_it_rewind(intern);
2788
0
    return SUCCESS;
2789
0
  } else {
2790
0
    return FAILURE;
2791
0
  }
2792
0
} /* }}} */
2793
2794
static void spl_append_it_fetch(spl_dual_it_object *intern) /* {{{*/
2795
0
{
2796
0
  while (spl_dual_it_valid(intern) != SUCCESS) {
2797
0
    intern->u.append.iterator->funcs->move_forward(intern->u.append.iterator);
2798
0
    if (spl_append_it_next_iterator(intern) != SUCCESS) {
2799
0
      return;
2800
0
    }
2801
0
  }
2802
0
  spl_dual_it_fetch(intern, 0);
2803
0
} /* }}} */
2804
2805
static void spl_append_it_next(spl_dual_it_object *intern) /* {{{ */
2806
0
{
2807
0
  if (spl_dual_it_valid(intern) == SUCCESS) {
2808
0
    spl_dual_it_next(intern, 1);
2809
0
  }
2810
0
  spl_append_it_fetch(intern);
2811
0
} /* }}} */
2812
2813
/* {{{ Create an AppendIterator */
2814
PHP_METHOD(AppendIterator, __construct)
2815
0
{
2816
0
  ZEND_PARSE_PARAMETERS_NONE();
2817
2818
0
  spl_dual_it_object *intern = Z_SPLDUAL_IT_P(ZEND_THIS);
2819
2820
  /* TODO: This should be converted to a normal Error as this is triggered when calling the constructor twice */
2821
0
  if (intern->dit_type != DIT_Unknown) {
2822
0
    zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s::getIterator() must be called exactly once per instance", ZSTR_VAL(spl_ce_AppendIterator->name));
2823
0
    RETURN_THROWS();
2824
0
  }
2825
2826
0
  intern->dit_type = DIT_AppendIterator;
2827
0
  object_init_ex(&intern->u.append.zarrayit, spl_ce_ArrayIterator);
2828
0
  zend_call_method_with_0_params(Z_OBJ(intern->u.append.zarrayit), spl_ce_ArrayIterator, &spl_ce_ArrayIterator->constructor, "__construct", NULL);
2829
0
  intern->u.append.iterator = spl_ce_ArrayIterator->get_iterator(spl_ce_ArrayIterator, &intern->u.append.zarrayit, 0);
2830
2831
0
} /* }}} */
2832
2833
/* {{{ Append an iterator */
2834
PHP_METHOD(AppendIterator, append)
2835
0
{
2836
0
  spl_dual_it_object   *intern;
2837
0
  zval *it;
2838
2839
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &it, zend_ce_iterator) == FAILURE) {
2840
0
    RETURN_THROWS();
2841
0
  }
2842
2843
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2844
2845
0
  if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) == SUCCESS && spl_dual_it_valid(intern) != SUCCESS) {
2846
0
    spl_array_iterator_append(&intern->u.append.zarrayit, it);
2847
0
    intern->u.append.iterator->funcs->move_forward(intern->u.append.iterator);
2848
0
  }else{
2849
0
    spl_array_iterator_append(&intern->u.append.zarrayit, it);
2850
0
  }
2851
2852
0
  if (!intern->inner.iterator || spl_dual_it_valid(intern) != SUCCESS) {
2853
0
    if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) != SUCCESS) {
2854
0
      intern->u.append.iterator->funcs->rewind(intern->u.append.iterator);
2855
0
    }
2856
0
    do {
2857
0
      spl_append_it_next_iterator(intern);
2858
0
    } while (Z_OBJ(intern->inner.zobject) != Z_OBJ_P(it));
2859
0
    spl_append_it_fetch(intern);
2860
0
  }
2861
0
} /* }}} */
2862
2863
/* {{{ Get the current element value */
2864
PHP_METHOD(AppendIterator, current)
2865
0
{
2866
0
  spl_dual_it_object   *intern;
2867
2868
0
  ZEND_PARSE_PARAMETERS_NONE();
2869
2870
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2871
2872
0
  spl_dual_it_fetch(intern, 1);
2873
0
  if (Z_TYPE(intern->current.data) != IS_UNDEF) {
2874
0
    RETURN_COPY_DEREF(&intern->current.data);
2875
0
  } else {
2876
0
    RETURN_NULL();
2877
0
  }
2878
0
} /* }}} */
2879
2880
/* {{{ Rewind to the first iterator and rewind the first iterator, too */
2881
PHP_METHOD(AppendIterator, rewind)
2882
0
{
2883
0
  spl_dual_it_object   *intern;
2884
2885
0
  ZEND_PARSE_PARAMETERS_NONE();
2886
2887
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2888
2889
0
  intern->u.append.iterator->funcs->rewind(intern->u.append.iterator);
2890
0
  if (spl_append_it_next_iterator(intern) == SUCCESS) {
2891
0
    spl_append_it_fetch(intern);
2892
0
  }
2893
0
} /* }}} */
2894
2895
/* {{{ Check if the current state is valid */
2896
PHP_METHOD(AppendIterator, valid)
2897
0
{
2898
0
  spl_dual_it_object   *intern;
2899
2900
0
  ZEND_PARSE_PARAMETERS_NONE();
2901
2902
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2903
2904
0
  RETURN_BOOL(Z_TYPE(intern->current.data) != IS_UNDEF);
2905
0
} /* }}} */
2906
2907
/* {{{ Forward to next element */
2908
PHP_METHOD(AppendIterator, next)
2909
0
{
2910
0
  spl_dual_it_object   *intern;
2911
2912
0
  ZEND_PARSE_PARAMETERS_NONE();
2913
2914
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2915
2916
0
  spl_append_it_next(intern);
2917
0
} /* }}} */
2918
2919
/* {{{ Get index of iterator */
2920
PHP_METHOD(AppendIterator, getIteratorIndex)
2921
0
{
2922
0
  spl_dual_it_object   *intern;
2923
2924
0
  ZEND_PARSE_PARAMETERS_NONE();
2925
2926
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2927
2928
0
  APPENDIT_CHECK_CTOR(intern);
2929
0
  spl_array_iterator_key(&intern->u.append.zarrayit, return_value);
2930
0
} /* }}} */
2931
2932
/* {{{ Get access to inner ArrayIterator */
2933
PHP_METHOD(AppendIterator, getArrayIterator)
2934
0
{
2935
0
  spl_dual_it_object   *intern;
2936
0
  zval *value;
2937
2938
0
  ZEND_PARSE_PARAMETERS_NONE();
2939
2940
0
  SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS);
2941
2942
0
  value = &intern->u.append.zarrayit;
2943
0
  RETURN_COPY_DEREF(value);
2944
0
} /* }}} */
2945
2946
PHPAPI zend_result spl_iterator_apply(zval *obj, spl_iterator_apply_func_t apply_func, void *puser)
2947
62
{
2948
62
  zend_object_iterator   *iter;
2949
62
  zend_class_entry       *ce = Z_OBJCE_P(obj);
2950
2951
62
  iter = ce->get_iterator(ce, obj, 0);
2952
2953
62
  if (EG(exception)) {
2954
0
    goto done;
2955
0
  }
2956
2957
62
  iter->index = 0;
2958
62
  if (iter->funcs->rewind) {
2959
62
    iter->funcs->rewind(iter);
2960
62
    if (EG(exception)) {
2961
4
      goto done;
2962
4
    }
2963
62
  }
2964
2965
163
  while (iter->funcs->valid(iter) == SUCCESS) {
2966
110
    if (EG(exception)) {
2967
0
      goto done;
2968
0
    }
2969
110
    if (apply_func(iter, puser) == ZEND_HASH_APPLY_STOP || EG(exception)) {
2970
5
      goto done;
2971
5
    }
2972
105
    iter->index++;
2973
105
    iter->funcs->move_forward(iter);
2974
105
    if (EG(exception)) {
2975
0
      goto done;
2976
0
    }
2977
105
  }
2978
2979
62
done:
2980
62
  if (iter) {
2981
62
    zend_iterator_dtor(iter);
2982
62
  }
2983
62
  return EG(exception) ? FAILURE : SUCCESS;
2984
58
}
2985
/* }}} */
2986
2987
static int spl_iterator_to_array_apply(zend_object_iterator *iter, void *puser) /* {{{ */
2988
104
{
2989
104
  zval *data, *return_value = (zval*)puser;
2990
2991
104
  data = iter->funcs->get_current_data(iter);
2992
104
  if (EG(exception)) {
2993
5
    return ZEND_HASH_APPLY_STOP;
2994
5
  }
2995
99
  if (data == NULL) {
2996
0
    return ZEND_HASH_APPLY_STOP;
2997
0
  }
2998
99
  if (iter->funcs->get_current_key) {
2999
99
    zval key;
3000
99
    iter->funcs->get_current_key(iter, &key);
3001
99
    if (EG(exception)) {
3002
0
      return ZEND_HASH_APPLY_STOP;
3003
0
    }
3004
99
    array_set_zval_key(Z_ARRVAL_P(return_value), &key, data);
3005
99
    zval_ptr_dtor(&key);
3006
99
  } else {
3007
0
    Z_TRY_ADDREF_P(data);
3008
0
    add_next_index_zval(return_value, data);
3009
0
  }
3010
99
  return ZEND_HASH_APPLY_KEEP;
3011
99
}
3012
/* }}} */
3013
3014
static int spl_iterator_to_values_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3015
6
{
3016
6
  zval *data, *return_value = (zval*)puser;
3017
3018
6
  data = iter->funcs->get_current_data(iter);
3019
6
  if (EG(exception)) {
3020
0
    return ZEND_HASH_APPLY_STOP;
3021
0
  }
3022
6
  if (data == NULL) {
3023
0
    return ZEND_HASH_APPLY_STOP;
3024
0
  }
3025
6
  Z_TRY_ADDREF_P(data);
3026
6
  add_next_index_zval(return_value, data);
3027
6
  return ZEND_HASH_APPLY_KEEP;
3028
6
}
3029
/* }}} */
3030
3031
/* {{{ Copy the iterator into an array */
3032
PHP_FUNCTION(iterator_to_array)
3033
68
{
3034
68
  zval  *obj;
3035
68
  bool use_keys = 1;
3036
3037
204
  ZEND_PARSE_PARAMETERS_START(1, 2)
3038
272
    Z_PARAM_ITERABLE(obj)
3039
62
    Z_PARAM_OPTIONAL
3040
136
    Z_PARAM_BOOL(use_keys)
3041
68
  ZEND_PARSE_PARAMETERS_END();
3042
3043
62
  if (Z_TYPE_P(obj) == IS_ARRAY) {
3044
0
    if (use_keys) {
3045
0
      RETURN_COPY(obj);
3046
0
    } else {
3047
0
      RETURN_ARR(zend_array_to_list(Z_ARRVAL_P(obj)));
3048
0
    }
3049
0
  }
3050
3051
62
  array_init(return_value);
3052
62
  spl_iterator_apply(obj, use_keys ? spl_iterator_to_array_apply : spl_iterator_to_values_apply, (void*)return_value);
3053
62
} /* }}} */
3054
3055
static int spl_iterator_count_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3056
0
{
3057
0
  if (UNEXPECTED(*(zend_long*)puser == ZEND_LONG_MAX)) {
3058
0
    return ZEND_HASH_APPLY_STOP;
3059
0
  }
3060
0
  (*(zend_long*)puser)++;
3061
0
  return ZEND_HASH_APPLY_KEEP;
3062
0
}
3063
/* }}} */
3064
3065
/* {{{ Count the elements in an iterator */
3066
PHP_FUNCTION(iterator_count)
3067
0
{
3068
0
  zval  *obj;
3069
0
  zend_long  count = 0;
3070
3071
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
3072
0
    Z_PARAM_ITERABLE(obj)
3073
0
  ZEND_PARSE_PARAMETERS_END();
3074
3075
0
  if (Z_TYPE_P(obj) == IS_ARRAY) {
3076
0
    count =  zend_hash_num_elements(Z_ARRVAL_P(obj));
3077
0
  } else {
3078
0
    if (spl_iterator_apply(obj, spl_iterator_count_apply, (void*)&count) == FAILURE) {
3079
0
      RETURN_THROWS();
3080
0
    }
3081
0
  }
3082
3083
0
  RETURN_LONG(count);
3084
0
}
3085
/* }}} */
3086
3087
typedef struct {
3088
  zend_long count;
3089
  HashTable *params_ht;
3090
  zend_fcall_info_cache fcc;
3091
} spl_iterator_apply_info;
3092
3093
static int spl_iterator_func_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3094
0
{
3095
0
  zval retval;
3096
0
  spl_iterator_apply_info  *apply_info = (spl_iterator_apply_info*)puser;
3097
0
  int result;
3098
3099
0
  apply_info->count++;
3100
0
  zend_call_known_fcc(&apply_info->fcc, &retval, 0, NULL, apply_info->params_ht);
3101
0
  result = zend_is_true(&retval) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_STOP;
3102
0
  zval_ptr_dtor(&retval);
3103
0
  return result;
3104
0
}
3105
/* }}} */
3106
3107
/* {{{ Calls a function for every element in an iterator */
3108
PHP_FUNCTION(iterator_apply)
3109
5
{
3110
5
  zval *traversable;
3111
5
  zend_fcall_info dummy_fci;
3112
5
  spl_iterator_apply_info apply_info = {
3113
5
    .count = 0,
3114
5
    .params_ht = NULL,
3115
5
    .fcc = { 0 },
3116
5
  };
3117
3118
  /* The HashTable is used to determine positional arguments */
3119
5
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "OF|h!", &traversable, zend_ce_traversable,
3120
5
      &dummy_fci, &apply_info.fcc, &apply_info.params_ht) == FAILURE) {
3121
5
    zend_release_fcall_info_cache(&apply_info.fcc);
3122
5
    RETURN_THROWS();
3123
5
  }
3124
3125
0
  if (spl_iterator_apply(traversable, spl_iterator_func_apply, (void*)&apply_info) == FAILURE) {
3126
0
    zend_release_fcall_info_cache(&apply_info.fcc);
3127
0
    RETURN_THROWS();
3128
0
  }
3129
0
  zend_release_fcall_info_cache(&apply_info.fcc);
3130
0
  RETURN_LONG(apply_info.count);
3131
0
}
3132
/* }}} */
3133
3134
/* {{{ PHP_MINIT_FUNCTION(spl_iterators) */
3135
PHP_MINIT_FUNCTION(spl_iterators)
3136
16
{
3137
16
  spl_ce_RecursiveIterator = register_class_RecursiveIterator(zend_ce_iterator);
3138
3139
16
  spl_ce_OuterIterator = register_class_OuterIterator(zend_ce_iterator);
3140
3141
16
  spl_ce_RecursiveIteratorIterator = register_class_RecursiveIteratorIterator(spl_ce_OuterIterator);
3142
16
  spl_ce_RecursiveIteratorIterator->create_object = spl_RecursiveIteratorIterator_new;
3143
16
  spl_ce_RecursiveIteratorIterator->default_object_handlers = &spl_handlers_rec_it_it;
3144
16
  spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator;
3145
3146
16
  memcpy(&spl_handlers_rec_it_it, &std_object_handlers, sizeof(zend_object_handlers));
3147
16
  spl_handlers_rec_it_it.offset = offsetof(spl_recursive_it_object, std);
3148
16
  spl_handlers_rec_it_it.get_method = spl_recursive_it_get_method;
3149
16
  spl_handlers_rec_it_it.clone_obj = NULL;
3150
16
  spl_handlers_rec_it_it.free_obj = spl_RecursiveIteratorIterator_free_storage;
3151
16
  spl_handlers_rec_it_it.get_gc = spl_RecursiveIteratorIterator_get_gc;
3152
3153
16
  memcpy(&spl_handlers_dual_it, &std_object_handlers, sizeof(zend_object_handlers));
3154
16
  spl_handlers_dual_it.offset = offsetof(spl_dual_it_object, std);
3155
16
  spl_handlers_dual_it.get_method = spl_dual_it_get_method;
3156
16
  spl_handlers_dual_it.clone_obj = NULL;
3157
16
  spl_handlers_dual_it.free_obj = spl_dual_it_free_storage;
3158
16
  spl_handlers_dual_it.get_gc = spl_dual_it_get_gc;
3159
3160
16
  spl_ce_IteratorIterator = register_class_IteratorIterator(spl_ce_OuterIterator);
3161
16
  spl_ce_IteratorIterator->create_object = spl_dual_it_new;
3162
16
  spl_ce_IteratorIterator->default_object_handlers = &spl_handlers_dual_it;
3163
3164
16
  spl_ce_FilterIterator = register_class_FilterIterator(spl_ce_IteratorIterator);
3165
16
  spl_ce_FilterIterator->create_object = spl_dual_it_new;
3166
3167
16
  spl_ce_RecursiveFilterIterator = register_class_RecursiveFilterIterator(spl_ce_FilterIterator, spl_ce_RecursiveIterator);
3168
16
  spl_ce_RecursiveFilterIterator->create_object = spl_dual_it_new;
3169
3170
16
  spl_ce_CallbackFilterIterator = register_class_CallbackFilterIterator(spl_ce_FilterIterator);
3171
16
  spl_ce_CallbackFilterIterator->create_object = spl_dual_it_new;
3172
3173
16
  spl_ce_RecursiveCallbackFilterIterator = register_class_RecursiveCallbackFilterIterator(spl_ce_CallbackFilterIterator, spl_ce_RecursiveIterator);
3174
16
  spl_ce_RecursiveCallbackFilterIterator->create_object = spl_dual_it_new;
3175
3176
16
  spl_ce_ParentIterator = register_class_ParentIterator(spl_ce_RecursiveFilterIterator);
3177
16
  spl_ce_ParentIterator->create_object = spl_dual_it_new;
3178
3179
16
  spl_ce_SeekableIterator = register_class_SeekableIterator(zend_ce_iterator);
3180
3181
16
  spl_ce_LimitIterator = register_class_LimitIterator(spl_ce_IteratorIterator);
3182
16
  spl_ce_LimitIterator->create_object = spl_dual_it_new;
3183
3184
16
  spl_ce_CachingIterator = register_class_CachingIterator(spl_ce_IteratorIterator, zend_ce_arrayaccess, zend_ce_countable, zend_ce_stringable);
3185
16
  spl_ce_CachingIterator->create_object = spl_dual_it_new;
3186
3187
16
  spl_ce_RecursiveCachingIterator = register_class_RecursiveCachingIterator(spl_ce_CachingIterator, spl_ce_RecursiveIterator);
3188
16
  spl_ce_RecursiveCachingIterator->create_object = spl_dual_it_new;
3189
3190
16
  spl_ce_NoRewindIterator = register_class_NoRewindIterator(spl_ce_IteratorIterator);
3191
16
  spl_ce_NoRewindIterator->create_object = spl_dual_it_new;
3192
3193
16
  spl_ce_AppendIterator = register_class_AppendIterator(spl_ce_IteratorIterator);
3194
16
  spl_ce_AppendIterator->create_object = spl_dual_it_new;
3195
3196
16
  spl_ce_InfiniteIterator = register_class_InfiniteIterator(spl_ce_IteratorIterator);
3197
16
  spl_ce_InfiniteIterator->create_object = spl_dual_it_new;
3198
3199
16
  spl_ce_RegexIterator = register_class_RegexIterator(spl_ce_FilterIterator);
3200
16
  spl_ce_RegexIterator->create_object = spl_dual_it_new;
3201
3202
16
  spl_ce_RecursiveRegexIterator = register_class_RecursiveRegexIterator(spl_ce_RegexIterator, spl_ce_RecursiveIterator);
3203
16
  spl_ce_RecursiveRegexIterator->create_object = spl_dual_it_new;
3204
3205
16
  spl_ce_EmptyIterator = register_class_EmptyIterator(zend_ce_iterator);
3206
3207
16
  spl_ce_RecursiveTreeIterator = register_class_RecursiveTreeIterator(spl_ce_RecursiveIteratorIterator);
3208
16
  spl_ce_RecursiveTreeIterator->create_object = spl_RecursiveTreeIterator_new;
3209
3210
16
  return SUCCESS;
3211
16
}
3212
/* }}} */