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