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