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