php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
spl_iterators.c
Go to the documentation of this file.
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"
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
56
64
73
93
97
137
138static zend_object_handlers spl_handlers_rec_it_it;
139static zend_object_handlers spl_handlers_dual_it;
140
141static 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
148static 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
185static void spl_recursive_it_dtor(zend_object_iterator *_iter)
186{
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
207static 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
229static 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
234static zval *spl_recursive_it_get_current_data(zend_object_iterator *iter)
235{
237 zend_object_iterator *sub_iter = object->iterators[object->level].iterator;
238
239 return sub_iter->funcs->get_current_data(sub_iter);
240}
241
242static void spl_recursive_it_get_current_key(zend_object_iterator *iter, zval *key)
243{
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
254static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zval *zthis)
255{
256 zend_object_iterator *iterator;
258 zval retval, child;
259 zend_object_iterator *sub_iter;
260
261 SPL_FETCH_SUB_ITERATOR(iterator, object);
262
263 while (!EG(exception)) {
264next_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 {
274 }
275 }
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 */
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 {
301 }
302 }
303 if (Z_TYPE(retval) != IS_UNDEF) {
304 bool has_children = zend_is_true(&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 {
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 {
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 {
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 {
423 }
424 }
425 }
426 if (object->level > 0) {
428 ZVAL_COPY_VALUE(&garbage, &object->iterators[object->level].zobject);
429 ZVAL_UNDEF(&object->iterators[object->level].zobject);
431 zend_iterator_dtor(iterator);
432 object->level--;
433 }
434 } else {
435 return; /* done completeley */
436 }
437 }
438}
439
440static 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
467static 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
472static 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
477static 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
488static 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
496 if (object->iterators == NULL) {
497 zend_throw_error(NULL, "Object is not initialized");
498 return NULL;
499 }
500
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
509static zend_result spl_get_iterator_from_aggregate(zval *retval, zend_class_entry *ce, zend_object *obj) {
510 zend_function **getiterator_cache =
512 zend_call_method_with_0_params(obj, ce, getiterator_cache, "getiterator", retval);
513 if (EG(exception)) {
514 return FAILURE;
515 }
517 || !instanceof_function(Z_OBJCE_P(retval), zend_ce_traversable)) {
519 "%s::getIterator() must return an object that implements Traversable",
520 ZSTR_VAL(ce->name));
522 return FAILURE;
523 }
524 return SUCCESS;
525}
526
527static void spl_RecursiveIteratorIterator_free_iterators(spl_recursive_it_object *object)
528{
529 if (object->iterators) {
530 while (object->level >= 0) {
531 zend_object_iterator *sub_iter = object->iterators[object->level].iterator;
532 zend_iterator_dtor(sub_iter);
533 zval_ptr_dtor(&object->iterators[object->level].zobject);
534 object->level--;
535 }
536 efree(object->iterators);
537 object->iterators = NULL;
538 }
539}
540
541static void spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_base, zend_class_entry *ce_inner, recursive_it_it_type rit_type)
542{
543 zval *object = ZEND_THIS;
545 zval *iterator;
546 zend_class_entry *ce_iterator;
548 zval caching_it, aggregate_retval;
549
550 switch (rit_type) {
552 zend_long user_caching_it_flags = CIT_CATCH_GET_CHILD;
555
556 if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|lll", &iterator, &flags, &user_caching_it_flags, &mode) == FAILURE) {
558 }
559
560 if (instanceof_function(Z_OBJCE_P(iterator), zend_ce_aggregate)) {
561 if (spl_get_iterator_from_aggregate(
562 &aggregate_retval, Z_OBJCE_P(iterator), Z_OBJ_P(iterator)) == FAILURE) {
564 }
565 iterator = &aggregate_retval;
566 } else {
567 Z_ADDREF_P(iterator);
568 }
569
570 zval params[2];
571 ZVAL_COPY_VALUE(&params[0], iterator);
572 ZVAL_LONG(&params[1], user_caching_it_flags);
573 zend_result is_initialized = object_init_with_constructor(&caching_it, spl_ce_RecursiveCachingIterator, 2, params, NULL);
574 zval_ptr_dtor(&params[0]);
575 if (is_initialized == FAILURE) {
577 }
578
579 iterator = &caching_it;
580 break;
581 }
583 default: {
585 flags = 0;
586 if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|ll", &iterator, &mode, &flags) == FAILURE) {
588 }
589
590 if (instanceof_function(Z_OBJCE_P(iterator), zend_ce_aggregate)) {
591 if (spl_get_iterator_from_aggregate(
592 &aggregate_retval, Z_OBJCE_P(iterator), Z_OBJ_P(iterator)) == FAILURE) {
594 }
595 iterator = &aggregate_retval;
596 } else {
597 Z_ADDREF_P(iterator);
598 }
599 break;
600 }
601 }
602 if (!instanceof_function(Z_OBJCE_P(iterator), spl_ce_RecursiveIterator)) {
603 if (iterator) {
604 zval_ptr_dtor(iterator);
605 }
606 zend_throw_exception(spl_ce_InvalidArgumentException, "An instance of RecursiveIterator or IteratorAggregate creating it is required", 0);
607 return;
608 }
609
610 intern = Z_SPLRECURSIVE_IT_P(object);
611 spl_RecursiveIteratorIterator_free_iterators(intern);
612 intern->iterators = emalloc(sizeof(spl_sub_iterator));
613 intern->level = 0;
614 intern->mode = mode;
615 intern->flags = (int)flags;
616 intern->max_depth = -1;
617 intern->in_iteration = 0;
618 intern->ce = Z_OBJCE_P(object);
619
620 intern->beginIteration = zend_hash_str_find_ptr(&intern->ce->function_table, "beginiteration", sizeof("beginiteration") - 1);
621 if (intern->beginIteration->common.scope == ce_base) {
622 intern->beginIteration = NULL;
623 }
624 intern->endIteration = zend_hash_str_find_ptr(&intern->ce->function_table, "enditeration", sizeof("enditeration") - 1);
625 if (intern->endIteration->common.scope == ce_base) {
626 intern->endIteration = NULL;
627 }
628 intern->callHasChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "callhaschildren", sizeof("callHasChildren") - 1);
629 if (intern->callHasChildren->common.scope == ce_base) {
630 intern->callHasChildren = NULL;
631 }
632 intern->callGetChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "callgetchildren", sizeof("callGetChildren") - 1);
633 if (intern->callGetChildren->common.scope == ce_base) {
634 intern->callGetChildren = NULL;
635 }
636 intern->beginChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "beginchildren", sizeof("beginchildren") - 1);
637 if (intern->beginChildren->common.scope == ce_base) {
638 intern->beginChildren = NULL;
639 }
640 intern->endChildren = zend_hash_str_find_ptr(&intern->ce->function_table, "endchildren", sizeof("endchildren") - 1);
641 if (intern->endChildren->common.scope == ce_base) {
642 intern->endChildren = NULL;
643 }
644 intern->nextElement = zend_hash_str_find_ptr(&intern->ce->function_table, "nextelement", sizeof("nextElement") - 1);
645 if (intern->nextElement->common.scope == ce_base) {
646 intern->nextElement = NULL;
647 }
648
649 ce_iterator = Z_OBJCE_P(iterator); /* respect inheritance, don't use spl_ce_RecursiveIterator */
650 intern->iterators[0].iterator = ce_iterator->get_iterator(ce_iterator, iterator, 0);
651 ZVAL_OBJ(&intern->iterators[0].zobject, Z_OBJ_P(iterator));
652 intern->iterators[0].ce = ce_iterator;
653 intern->iterators[0].state = RS_START;
654 intern->iterators[0].haschildren = NULL;
655 intern->iterators[0].getchildren = NULL;
656
657 if (EG(exception)) {
658 // TODO: use spl_RecursiveIteratorIterator_free_iterators
659 zend_object_iterator *sub_iter;
660
661 while (intern->level >= 0) {
662 sub_iter = intern->iterators[intern->level].iterator;
663 zend_iterator_dtor(sub_iter);
664 zval_ptr_dtor(&intern->iterators[intern->level--].zobject);
665 }
666 efree(intern->iterators);
667 intern->iterators = NULL;
668 }
669}
670
671/* {{{ Creates a RecursiveIteratorIterator from a RecursiveIterator. */
676
677/* {{{ Rewind the iterator to the first element of the top level inner iterator. */
679{
681
683 spl_recursive_it_rewind_ex(object, ZEND_THIS);
684} /* }}} */
685
686/* {{{ Check whether the current position is valid */
688{
690
692 RETURN_BOOL(spl_recursive_it_valid_ex(object, ZEND_THIS) == SUCCESS);
693} /* }}} */
694
695/* {{{ Access the current key */
697{
699 zend_object_iterator *iterator;
700
702
703 SPL_FETCH_SUB_ITERATOR(iterator, object);
704
705 if (iterator->funcs->get_current_key) {
706 iterator->funcs->get_current_key(iterator, return_value);
707 } else {
708 RETURN_NULL();
709 }
710} /* }}} */
711
712/* {{{ Access the current element value */
714{
716 zend_object_iterator *iterator;
717 zval *data;
718
720
721 SPL_FETCH_SUB_ITERATOR(iterator, object);
722
723 data = iterator->funcs->get_current_data(iterator);
724 if (data) {
726 }
727} /* }}} */
728
729/* {{{ Move forward to the next element */
731{
733
735 spl_recursive_it_move_forward_ex(object, ZEND_THIS);
736} /* }}} */
737
738/* {{{ Get the current depth of the recursive iteration */
746
747/* {{{ The current active sub iterator or the iterator at specified level */
749{
751 zend_long level;
752 bool level_is_null = 1;
753 zval *value;
754
755 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!", &level, &level_is_null) == FAILURE) {
757 }
758
759 if (level_is_null) {
760 level = object->level;
761 } else if (level < 0 || level > object->level) {
762 RETURN_NULL();
763 }
764
765 if(!object->iterators) {
766 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
768 }
769
770 value = &object->iterators[level].zobject;
772} /* }}} */
773
774/* {{{ The current active sub iterator */
776{
778 zval *zobject;
779
781 SPL_FETCH_SUB_ELEMENT_ADDR(zobject, object, zobject);
782 RETURN_COPY_DEREF(zobject);
783} /* }}} */
784
785/* {{{ Called when iteration begins (after first rewind() call) */
787{
789 /* nothing to do */
790} /* }}} */
791
792/* {{{ Called when iteration ends (when valid() first returns false */
794{
796 /* nothing to do */
797} /* }}} */
798
799/* {{{ Called for each element to test whether it has children */
801{
804 zval *zobject;
805
807
808 if (!object->iterators) {
810 }
811
812 SPL_FETCH_SUB_ELEMENT(ce, object, ce);
813
814 zobject = &object->iterators[object->level].zobject;
815 if (Z_TYPE_P(zobject) == IS_UNDEF) {
817 } else {
818 zend_call_method_with_0_params(Z_OBJ_P(zobject), ce, &object->iterators[object->level].haschildren, "haschildren", return_value);
821 }
822 }
823} /* }}} */
824
825/* {{{ Return children of current element */
827{
830 zval *zobject;
831
833
834 SPL_FETCH_SUB_ELEMENT(ce, object, ce);
835
836 zobject = &object->iterators[object->level].zobject;
837 if (Z_TYPE_P(zobject) == IS_UNDEF) {
838 RETURN_NULL();
839 } else {
840 zend_call_method_with_0_params(Z_OBJ_P(zobject), ce, &object->iterators[object->level].getchildren, "getchildren", return_value);
842 RETURN_NULL();
843 }
844 }
845} /* }}} */
846
847/* {{{ Called when recursing one level down */
849{
851 /* nothing to do */
852} /* }}} */
853
854/* {{{ Called when end recursing one level */
856{
858 /* nothing to do */
859} /* }}} */
860
861/* {{{ Called when the next element is available */
863{
865 /* nothing to do */
866} /* }}} */
867
868/* {{{ Set the maximum allowed depth (or any depth if pmax_depth = -1] */
870{
872 zend_long max_depth = -1;
873
874 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &max_depth) == FAILURE) {
876 }
877 if (max_depth < -1) {
878 zend_argument_value_error(1, "must be greater than or equal to -1");
880 } else if (max_depth > INT_MAX) {
881 max_depth = INT_MAX;
882 }
883
884 object->max_depth = (int)max_depth;
885} /* }}} */
886
887/* {{{ Return the maximum accepted depth or false if any depth is allowed */
889{
891
893
894 if (object->max_depth == -1) {
896 } else {
897 RETURN_LONG(object->max_depth);
898 }
899} /* }}} */
900
901static zend_function *spl_recursive_it_get_method(zend_object **zobject, zend_string *method, const zval *key)
902{
903 zend_function *function_handler;
904 spl_recursive_it_object *object = spl_recursive_it_from_obj(*zobject);
905 zend_long level = object->level;
906 zval *zobj;
907
908 if (!object->iterators) {
909 zend_throw_error(NULL, "The %s instance wasn't initialized properly", ZSTR_VAL((*zobject)->ce->name));
910 return NULL;
911 }
912 zobj = &object->iterators[level].zobject;
913
914 function_handler = zend_std_get_method(zobject, method, key);
915 if (!function_handler) {
916 if ((function_handler = zend_hash_find_ptr(&Z_OBJCE_P(zobj)->function_table, method)) == NULL) {
917 *zobject = Z_OBJ_P(zobj);
918 function_handler = (*zobject)->handlers->get_method(zobject, method, key);
919 } else {
920 *zobject = Z_OBJ_P(zobj);
921 }
922 }
923 return function_handler;
924}
925
926/* {{{ spl_RecursiveIteratorIterator_free_storage */
927static void spl_RecursiveIteratorIterator_free_storage(zend_object *_object)
928{
929 spl_recursive_it_object *object = spl_recursive_it_from_obj(_object);
930
931 spl_RecursiveIteratorIterator_free_iterators(object);
932
934 for (size_t i = 0; i < 6; i++) {
935 if (object->prefix[i]) {
936 zend_string_release(object->prefix[i]);
937 }
938 }
939
940 if (object->postfix[0]) {
941 zend_string_release(object->postfix[0]);
942 }
943}
944/* }}} */
945
946static HashTable *spl_RecursiveIteratorIterator_get_gc(zend_object *obj, zval **table, int *n)
947{
948 spl_recursive_it_object *object = spl_recursive_it_from_obj(obj);
950
951 if (object->iterators) {
952 for (int level = 0; level <= object->level; level++) {
953 zend_get_gc_buffer_add_zval(gc_buffer, &object->iterators[level].zobject);
954 zend_get_gc_buffer_add_obj(gc_buffer, &object->iterators[level].iterator->std);
955 }
956 }
957
958 zend_get_gc_buffer_use(gc_buffer, table, n);
959 return zend_std_get_properties(obj);
960}
961
962/* {{{ spl_RecursiveIteratorIterator_new_ex */
963static zend_object *spl_RecursiveIteratorIterator_new_ex(zend_class_entry *class_type, int init_prefix)
964{
966
967 intern = zend_object_alloc(sizeof(spl_recursive_it_object), class_type);
968
969 if (init_prefix) {
970 intern->prefix[0] = ZSTR_EMPTY_ALLOC();
971 intern->prefix[1] = ZSTR_INIT_LITERAL("| ", 0);
972 intern->prefix[2] = ZSTR_INIT_LITERAL(" ", 0);
973 intern->prefix[3] = ZSTR_INIT_LITERAL("|-", 0);
974 intern->prefix[4] = ZSTR_INIT_LITERAL("\\-", 0);
975 intern->prefix[5] = ZSTR_EMPTY_ALLOC();
976
977 intern->postfix[0] = ZSTR_EMPTY_ALLOC();
978 }
979
980 zend_object_std_init(&intern->std, class_type);
981 object_properties_init(&intern->std, class_type);
982
983 return &intern->std;
984}
985/* }}} */
986
987/* {{{ spl_RecursiveIteratorIterator_new */
988static zend_object *spl_RecursiveIteratorIterator_new(zend_class_entry *class_type)
989{
990 return spl_RecursiveIteratorIterator_new_ex(class_type, 0);
991}
992/* }}} */
993
994/* {{{ spl_RecursiveTreeIterator_new */
995static zend_object *spl_RecursiveTreeIterator_new(zend_class_entry *class_type)
996{
997 return spl_RecursiveIteratorIterator_new_ex(class_type, 1);
998}
999/* }}} */
1000
1001static zend_string *spl_recursive_tree_iterator_get_prefix(spl_recursive_it_object *object)
1002{
1003 smart_str str = {0};
1004 zval has_next;
1005 int level;
1006
1007 smart_str_append(&str, object->prefix[0]);
1008
1009 for (level = 0; level < object->level; ++level) {
1010 zend_call_method_with_0_params(Z_OBJ(object->iterators[level].zobject), object->iterators[level].ce, NULL, "hasnext", &has_next);
1011 if (Z_TYPE(has_next) != IS_UNDEF) {
1012 if (Z_TYPE(has_next) == IS_TRUE) {
1013 smart_str_append(&str, object->prefix[1]);
1014 } else {
1015 smart_str_append(&str, object->prefix[2]);
1016 }
1017 zval_ptr_dtor(&has_next);
1018 }
1019 }
1020 zend_call_method_with_0_params(Z_OBJ(object->iterators[level].zobject), object->iterators[level].ce, NULL, "hasnext", &has_next);
1021 if (Z_TYPE(has_next) != IS_UNDEF) {
1022 if (Z_TYPE(has_next) == IS_TRUE) {
1023 smart_str_append(&str, object->prefix[3]);
1024 } else {
1025 smart_str_append(&str, object->prefix[4]);
1026 }
1027 zval_ptr_dtor(&has_next);
1028 }
1029
1030 smart_str_append(&str, object->prefix[5]);
1031 smart_str_0(&str);
1032
1033 return str.s;
1034}
1035
1036static zend_string *spl_recursive_tree_iterator_get_entry(spl_recursive_it_object *object)
1037{
1038 zend_object_iterator *iterator = object->iterators[object->level].iterator;
1039 zval *data = iterator->funcs->get_current_data(iterator);
1040 if (!data) {
1041 return NULL;
1042 }
1043
1045 if (Z_TYPE_P(data) == IS_ARRAY) {
1046 /* TODO: Remove this special case? */
1047 return ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED);
1048 }
1049 return zval_get_string(data);
1050}
1051
1052static zend_string *spl_recursive_tree_iterator_get_postfix(spl_recursive_it_object *object)
1053{
1054 return zend_string_copy(object->postfix[0]);
1055}
1056
1057/* {{{ RecursiveIteratorIterator to generate ASCII graphic trees for the entries in a RecursiveIterator */
1062
1063/* {{{ Sets prefix parts as used in getPrefix() */
1065{
1066 zend_long part;
1069
1070 if (zend_parse_parameters(ZEND_NUM_ARGS(), "lS", &part, &prefix) == FAILURE) {
1071 RETURN_THROWS();
1072 }
1073
1074 if (0 > part || part > 5) {
1075 zend_argument_value_error(1, "must be a RecursiveTreeIterator::PREFIX_* constant");
1076 RETURN_THROWS();
1077 }
1078
1079 zend_string_release(object->prefix[part]);
1080 object->prefix[part] = zend_string_copy(prefix);
1081} /* }}} */
1082
1083/* {{{ Returns the string to place in front of current element */
1085{
1087
1089
1090 if(!object->iterators) {
1091 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1092 RETURN_THROWS();
1093 }
1094
1095 RETURN_STR(spl_recursive_tree_iterator_get_prefix(object));
1096} /* }}} */
1097
1098/* {{{ Sets postfix as used in getPostfix() */
1100{
1102 zend_string *postfix;
1103
1104 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &postfix) == FAILURE) {
1105 RETURN_THROWS();
1106 }
1107
1108 zend_string_release(object->postfix[0]);
1109 object->postfix[0] = zend_string_copy(postfix);
1110} /* }}} */
1111
1112/* {{{ Returns the string presentation built for current element */
1114{
1116
1118
1119 if(!object->iterators) {
1120 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1121 RETURN_THROWS();
1122 }
1123
1124 zend_string *entry = spl_recursive_tree_iterator_get_entry(object);
1125 if (!entry) {
1126 // TODO: Can this happen? It's not in the stubs.
1127 RETURN_NULL();
1128 }
1129 RETURN_STR(entry);
1130} /* }}} */
1131
1132/* {{{ Returns the string to place after the current element */
1134{
1136
1138
1139 if(!object->iterators) {
1140 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1141 RETURN_THROWS();
1142 }
1143
1144 RETURN_STR(spl_recursive_tree_iterator_get_postfix(object));
1145} /* }}} */
1146
1147/* {{{ Returns the current element prefixed and postfixed */
1149{
1151
1153
1154 if(!object->iterators) {
1155 zend_throw_error(NULL, "The object is in an invalid state as the parent constructor was not called");
1156 RETURN_THROWS();
1157 }
1158
1159 if (object->flags & RTIT_BYPASS_CURRENT) {
1160 zend_object_iterator *iterator;
1161 zval *data;
1162
1163 SPL_FETCH_SUB_ITERATOR(iterator, object);
1164 data = iterator->funcs->get_current_data(iterator);
1165 if (data) {
1167 } else {
1168 RETURN_NULL();
1169 }
1170 }
1171
1172 zend_string *entry = spl_recursive_tree_iterator_get_entry(object);
1173 if (!entry) {
1174 RETURN_NULL();
1175 }
1176
1177 zend_string *prefix = spl_recursive_tree_iterator_get_prefix(object);
1178 zend_string *postfix = spl_recursive_tree_iterator_get_postfix(object);
1179
1182 ZSTR_VAL(entry), ZSTR_LEN(entry),
1183 ZSTR_VAL(postfix), ZSTR_LEN(postfix));
1184
1185 zend_string_release(entry);
1186 zend_string_release(prefix);
1187 zend_string_release(postfix);
1188
1190} /* }}} */
1191
1192/* {{{ Returns the current key prefixed and postfixed */
1194{
1196 zend_object_iterator *iterator;
1197 zval key;
1198
1200
1201 SPL_FETCH_SUB_ITERATOR(iterator, object);
1202
1203 if (iterator->funcs->get_current_key) {
1204 iterator->funcs->get_current_key(iterator, &key);
1205 } else {
1206 ZVAL_NULL(&key);
1207 }
1208
1209 if (object->flags & RTIT_BYPASS_KEY) {
1211 }
1212
1213 zend_string *key_str = zval_get_string(&key);
1214 zend_string *prefix = spl_recursive_tree_iterator_get_prefix(object);
1215 zend_string *postfix = spl_recursive_tree_iterator_get_postfix(object);
1216
1219 ZSTR_VAL(key_str), ZSTR_LEN(key_str),
1220 ZSTR_VAL(postfix), ZSTR_LEN(postfix));
1221
1222 zend_string_release(key_str);
1223 zend_string_release(prefix);
1224 zend_string_release(postfix);
1226
1228} /* }}} */
1229
1230static zend_function *spl_dual_it_get_method(zend_object **object, zend_string *method, const zval *key)
1231{
1232 zend_function *function_handler;
1233 spl_dual_it_object *intern;
1234
1235 intern = spl_dual_it_from_obj(*object);
1236
1237 function_handler = zend_std_get_method(object, method, key);
1238 if (!function_handler && intern->inner.ce) {
1239 if ((function_handler = zend_hash_find_ptr(&intern->inner.ce->function_table, method)) == NULL) {
1240 if (Z_OBJ_HT(intern->inner.zobject)->get_method) {
1241 *object = Z_OBJ(intern->inner.zobject);
1242 function_handler = (*object)->handlers->get_method(object, method, key);
1243 }
1244 } else {
1245 *object = Z_OBJ(intern->inner.zobject);
1246 }
1247 }
1248 return function_handler;
1249}
1250
1251#define SPL_CHECK_CTOR(intern, classname) \
1252 if (intern->dit_type == DIT_Unknown) { \
1253 /* TODO Normal Error? */ \
1254 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Classes derived from %s must call %s::__construct()", \
1255 ZSTR_VAL((spl_ce_##classname)->name), ZSTR_VAL((spl_ce_##classname)->name)); \
1256 RETURN_THROWS(); \
1257 }
1258
1259#define APPENDIT_CHECK_CTOR(intern) SPL_CHECK_CTOR(intern, AppendIterator)
1260
1261static inline zend_result spl_dual_it_fetch(spl_dual_it_object *intern, int check_more);
1262
1263static inline zend_result spl_cit_check_flags(zend_long flags)
1264{
1265 zend_long cnt = 0;
1266
1267 cnt += (flags & CIT_CALL_TOSTRING) ? 1 : 0;
1268 cnt += (flags & CIT_TOSTRING_USE_KEY) ? 1 : 0;
1269 cnt += (flags & CIT_TOSTRING_USE_CURRENT) ? 1 : 0;
1270 cnt += (flags & CIT_TOSTRING_USE_INNER) ? 1 : 0;
1271
1272 return cnt <= 1 ? SUCCESS : FAILURE;
1273}
1274
1275static spl_dual_it_object* spl_dual_it_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_base, zend_class_entry *ce_inner, dual_it_type dit_type)
1276{
1277 zval *zobject, retval;
1278 spl_dual_it_object *intern;
1279 zend_class_entry *ce = NULL;
1280 int inc_refcount = 1;
1281 zend_error_handling error_handling;
1282
1283 intern = Z_SPLDUAL_IT_P(ZEND_THIS);
1284
1285 if (intern->dit_type != DIT_Unknown) {
1286 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s::getIterator() must be called exactly once per instance", ZSTR_VAL(ce_base->name));
1287 return NULL;
1288 }
1289
1290 switch (dit_type) {
1291 case DIT_LimitIterator: {
1292 intern->u.limit.offset = 0; /* start at beginning */
1293 intern->u.limit.count = -1; /* get all */
1294 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|ll", &zobject, ce_inner, &intern->u.limit.offset, &intern->u.limit.count) == FAILURE) {
1295 return NULL;
1296 }
1297 if (intern->u.limit.offset < 0) {
1298 zend_argument_value_error(2, "must be greater than or equal to 0");
1299 return NULL;
1300 }
1301 if (intern->u.limit.count < -1) {
1302 zend_argument_value_error(3, "must be greater than or equal to -1");
1303 return NULL;
1304 }
1305 break;
1306 }
1310 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &zobject, ce_inner, &flags) == FAILURE) {
1311 return NULL;
1312 }
1313 if (spl_cit_check_flags(flags) != SUCCESS) {
1314 zend_argument_value_error(2, "must contain only one of CachingIterator::CALL_TOSTRING, "
1315 "CachingIterator::TOSTRING_USE_KEY, CachingIterator::TOSTRING_USE_CURRENT, "
1316 "or CachingIterator::TOSTRING_USE_INNER");
1317 return NULL;
1318 }
1319 intern->u.caching.flags |= flags & CIT_PUBLIC;
1320 array_init(&intern->u.caching.zcache);
1321 break;
1322 }
1323 case DIT_IteratorIterator: {
1324 zend_class_entry *ce_cast;
1325 zend_string *class_name = NULL;
1326
1327 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|S!", &zobject, ce_inner, &class_name) == FAILURE) {
1328 return NULL;
1329 }
1330 ce = Z_OBJCE_P(zobject);
1331 if (!instanceof_function(ce, zend_ce_iterator)) {
1332 if (class_name) {
1333 if (!(ce_cast = zend_lookup_class(class_name))
1334 || !instanceof_function(ce, ce_cast)
1335 || !ce_cast->get_iterator
1336 ) {
1337 zend_throw_exception(spl_ce_LogicException, "Class to downcast to not found or not base class or does not implement Traversable", 0);
1338 return NULL;
1339 }
1340 ce = ce_cast;
1341 }
1342 if (instanceof_function(ce, zend_ce_aggregate)) {
1343 if (spl_get_iterator_from_aggregate(&retval, ce, Z_OBJ_P(zobject)) == FAILURE) {
1344 return NULL;
1345 }
1346 zobject = &retval;
1347 ce = Z_OBJCE_P(zobject);
1348 inc_refcount = 0;
1349 }
1350 }
1351 break;
1352 }
1353 case DIT_AppendIterator:
1355 return NULL;
1356 }
1357 intern->dit_type = DIT_AppendIterator;
1359 zend_call_method_with_0_params(Z_OBJ(intern->u.append.zarrayit), spl_ce_ArrayIterator, &spl_ce_ArrayIterator->constructor, "__construct", NULL);
1360 intern->u.append.iterator = spl_ce_ArrayIterator->get_iterator(spl_ce_ArrayIterator, &intern->u.append.zarrayit, 0);
1361 return intern;
1362 case DIT_RegexIterator:
1364 zend_string *regex;
1366
1367 intern->u.regex.flags = 0;
1368 intern->u.regex.preg_flags = 0;
1369 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS|lll", &zobject, ce_inner, &regex, &mode, &intern->u.regex.flags, &intern->u.regex.preg_flags) == FAILURE) {
1370 return NULL;
1371 }
1373 zend_argument_value_error(3, "must be RegexIterator::MATCH, RegexIterator::GET_MATCH, "
1374 "RegexIterator::ALL_MATCHES, RegexIterator::SPLIT, or RegexIterator::REPLACE");
1375 return NULL;
1376 }
1377
1378 /* pcre_get_compiled_regex_cache() might emit E_WARNINGs that we want to promote to exception */
1380 intern->u.regex.pce = pcre_get_compiled_regex_cache(regex);
1381 zend_restore_error_handling(&error_handling);
1382
1383 if (intern->u.regex.pce == NULL) {
1384 /* pcre_get_compiled_regex_cache has already sent error */
1385 return NULL;
1386 }
1387 intern->u.regex.mode = mode;
1388 intern->u.regex.regex = zend_string_copy(regex);
1389 php_pcre_pce_incref(intern->u.regex.pce);
1390 break;
1391 }
1394 zend_fcall_info fci;
1395 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OF", &zobject, ce_inner, &fci, &intern->u.callback_filter) == FAILURE) {
1396 return NULL;
1397 }
1398 zend_fcc_addref(&intern->u.callback_filter);
1399 break;
1400 }
1401 default:
1402 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zobject, ce_inner) == FAILURE) {
1403 return NULL;
1404 }
1405 break;
1406 }
1407
1408 intern->dit_type = dit_type;
1409 if (inc_refcount) {
1410 Z_ADDREF_P(zobject);
1411 }
1412 ZVAL_OBJ(&intern->inner.zobject, Z_OBJ_P(zobject));
1413
1414 intern->inner.ce = dit_type == DIT_IteratorIterator ? ce : Z_OBJCE_P(zobject);
1415 intern->inner.object = Z_OBJ_P(zobject);
1416 intern->inner.iterator = intern->inner.ce->get_iterator(intern->inner.ce, zobject, 0);
1417
1418 return intern;
1419}
1420
1421/* {{{ Create an Iterator from another iterator */
1426
1427/* {{{ Create an Iterator from another iterator */
1432
1433/* {{{ Get the inner iterator */
1435{
1436 spl_dual_it_object *intern;
1437
1439
1441
1442 if (!Z_ISUNDEF(intern->inner.zobject)) {
1443 zval *value = &intern->inner.zobject;
1445 } else {
1446 RETURN_NULL();
1447 }
1448} /* }}} */
1449
1450static inline void spl_dual_it_free(spl_dual_it_object *intern)
1451{
1452 if (intern->inner.iterator && intern->inner.iterator->funcs->invalidate_current) {
1454 }
1455 if (Z_TYPE(intern->current.data) != IS_UNDEF) {
1456 zval_ptr_dtor(&intern->current.data);
1457 ZVAL_UNDEF(&intern->current.data);
1458 }
1459 if (Z_TYPE(intern->current.key) != IS_UNDEF) {
1460 zval_ptr_dtor(&intern->current.key);
1461 ZVAL_UNDEF(&intern->current.key);
1462 }
1464 if (intern->u.caching.zstr) {
1465 zend_string_release(intern->u.caching.zstr);
1466 intern->u.caching.zstr = NULL;
1467 }
1468 if (Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF) {
1469 zval_ptr_dtor(&intern->u.caching.zchildren);
1470 ZVAL_UNDEF(&intern->u.caching.zchildren);
1471 }
1472 }
1473}
1474
1475static inline void spl_dual_it_rewind(spl_dual_it_object *intern)
1476{
1477 spl_dual_it_free(intern);
1478 intern->current.pos = 0;
1479 if (intern->inner.iterator && intern->inner.iterator->funcs->rewind) {
1480 intern->inner.iterator->funcs->rewind(intern->inner.iterator);
1481 }
1482}
1483
1484static inline zend_result spl_dual_it_valid(spl_dual_it_object *intern)
1485{
1486 if (!intern->inner.iterator) {
1487 return FAILURE;
1488 }
1489 /* FAILURE / SUCCESS */
1490 return intern->inner.iterator->funcs->valid(intern->inner.iterator);
1491}
1492
1493static inline zend_result spl_dual_it_fetch(spl_dual_it_object *intern, int check_more)
1494{
1495 zval *data;
1496
1497 spl_dual_it_free(intern);
1498 if (!check_more || spl_dual_it_valid(intern) == SUCCESS) {
1499 data = intern->inner.iterator->funcs->get_current_data(intern->inner.iterator);
1500 if (data) {
1501 ZVAL_COPY(&intern->current.data, data);
1502 }
1503
1504 if (intern->inner.iterator->funcs->get_current_key) {
1505 intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, &intern->current.key);
1506 if (EG(exception)) {
1507 zval_ptr_dtor(&intern->current.key);
1508 ZVAL_UNDEF(&intern->current.key);
1509 }
1510 } else {
1511 ZVAL_LONG(&intern->current.key, intern->current.pos);
1512 }
1513 return EG(exception) ? FAILURE : SUCCESS;
1514 }
1515 return FAILURE;
1516}
1517
1518static inline void spl_dual_it_next(spl_dual_it_object *intern, int do_free)
1519{
1520 if (do_free) {
1521 spl_dual_it_free(intern);
1522 } else if (!intern->inner.iterator) {
1523 zend_throw_error(NULL, "The inner constructor wasn't initialized with an iterator instance");
1524 return;
1525 }
1526 intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
1527 intern->current.pos++;
1528}
1529
1530/* {{{ Rewind the iterator */
1532{
1533 spl_dual_it_object *intern;
1534
1536
1538
1539 spl_dual_it_rewind(intern);
1540 spl_dual_it_fetch(intern, 1);
1541} /* }}} */
1542
1543/* {{{ Check whether the current element is valid */
1545{
1546 spl_dual_it_object *intern;
1547
1549
1551
1552 RETURN_BOOL(Z_TYPE(intern->current.data) != IS_UNDEF);
1553} /* }}} */
1554
1555/* {{{ Get the current key */
1557{
1558 spl_dual_it_object *intern;
1559
1561
1563
1564 if (Z_TYPE(intern->current.key) != IS_UNDEF) {
1565 RETURN_COPY_DEREF(&intern->current.key);
1566 } else {
1567 RETURN_NULL();
1568 }
1569} /* }}} */
1570
1571/* {{{ Get the current element value */
1573{
1574 spl_dual_it_object *intern;
1575
1577
1579
1580 if (Z_TYPE(intern->current.data) != IS_UNDEF) {
1581 RETURN_COPY_DEREF(&intern->current.data);
1582 } else {
1583 RETURN_NULL();
1584 }
1585} /* }}} */
1586
1587/* {{{ Move the iterator forward */
1589{
1590 spl_dual_it_object *intern;
1591
1593
1595
1596 spl_dual_it_next(intern, 1);
1597 spl_dual_it_fetch(intern, 1);
1598} /* }}} */
1599
1600static inline void spl_filter_it_fetch(zval *zthis, spl_dual_it_object *intern)
1601{
1602 zval retval;
1603
1604 while (spl_dual_it_fetch(intern, 1) == SUCCESS) {
1605 zend_call_method_with_0_params(Z_OBJ_P(zthis), intern->std.ce, NULL, "accept", &retval);
1606 if (Z_TYPE(retval) != IS_UNDEF) {
1607 if (zend_is_true(&retval)) {
1609 return;
1610 }
1612 }
1613 if (EG(exception)) {
1614 return;
1615 }
1616 intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
1617 }
1618 spl_dual_it_free(intern);
1619}
1620
1621static inline void spl_filter_it_rewind(zval *zthis, spl_dual_it_object *intern)
1622{
1623 spl_dual_it_rewind(intern);
1624 spl_filter_it_fetch(zthis, intern);
1625}
1626
1627static inline void spl_filter_it_next(zval *zthis, spl_dual_it_object *intern)
1628{
1629 spl_dual_it_next(intern, 1);
1630 spl_filter_it_fetch(zthis, intern);
1631}
1632
1633/* {{{ Rewind the iterator */
1635{
1636 spl_dual_it_object *intern;
1637
1639
1641 spl_filter_it_rewind(ZEND_THIS, intern);
1642} /* }}} */
1643
1644/* {{{ Move the iterator forward */
1646{
1647 spl_dual_it_object *intern;
1648
1650
1652 spl_filter_it_next(ZEND_THIS, intern);
1653} /* }}} */
1654
1655/* {{{ Create a RecursiveCallbackFilterIterator from a RecursiveIterator */
1660
1661
1662/* {{{ Create a RecursiveFilterIterator from a RecursiveIterator */
1667
1668/* {{{ Check whether the inner iterator's current element has children */
1670{
1671 spl_dual_it_object *intern;
1672
1674
1676
1677 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "haschildren", return_value);
1678} /* }}} */
1679
1680/* {{{ Return the inner iterator's children contained in a RecursiveFilterIterator */
1682{
1683 spl_dual_it_object *intern;
1684
1686
1688
1689 zval childrens;
1690 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &childrens);
1691 if (Z_TYPE(childrens) == IS_UNDEF) {
1692 RETURN_THROWS();
1693 }
1694
1696 zval_ptr_dtor(&childrens);
1697 if (is_initialized == FAILURE) {
1698 RETURN_THROWS();
1699 }
1700} /* }}} */
1701
1702/* {{{ Return the inner iterator's children contained in a RecursiveCallbackFilterIterator */
1704{
1705 spl_dual_it_object *intern;
1706
1708
1710
1711 zval params[2];
1712 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &params[0]);
1713 if (Z_TYPE(params[0]) == IS_UNDEF) {
1714 RETURN_THROWS();
1715 }
1716
1717 /* Get callable to pass to the constructor */
1719
1721 zval_ptr_dtor(&params[0]);
1722 zval_ptr_dtor(&params[1]);
1723 if (is_initialized == FAILURE) {
1724 RETURN_THROWS();
1725 }
1726} /* }}} */
1727/* {{{ Create a ParentIterator from a RecursiveIterator */
1732
1733/* {{{ Create an RegexIterator from another iterator and a regular expression */
1738
1739/* {{{ Calls the callback with the current value, the current key and the inner iterator as arguments */
1741{
1742 spl_dual_it_object *intern;
1743
1745
1747
1748 if (Z_TYPE(intern->current.data) == IS_UNDEF || Z_TYPE(intern->current.key) == IS_UNDEF) {
1750 }
1751
1752 zval params[3];
1753 ZVAL_COPY_VALUE(&params[0], &intern->current.data);
1754 ZVAL_COPY_VALUE(&params[1], &intern->current.key);
1755 ZVAL_COPY_VALUE(&params[2], &intern->inner.zobject);
1756
1757 zend_fcall_info_cache *fcc = &intern->u.callback_filter;
1758
1759 zend_call_known_fcc(fcc, return_value, 3, params, NULL);
1762 } else if (Z_ISREF_P(return_value)) {
1763 zend_unwrap_reference(return_value);
1764 }
1765}
1766/* }}} */
1767
1768/* {{{ Match (string)current() against regular expression */
1770{
1771 spl_dual_it_object *intern;
1772 zend_string *result, *subject;
1773 size_t count = 0;
1774 zval zcount, rv;
1775 pcre2_match_data *match_data;
1776 pcre2_code *re;
1777 int rc;
1778
1780
1782
1783 if (Z_TYPE(intern->current.data) == IS_UNDEF) {
1785 }
1786
1787 if (intern->u.regex.flags & REGIT_USE_KEY) {
1788 subject = zval_get_string(&intern->current.key);
1789 } else {
1790 if (Z_TYPE(intern->current.data) == IS_ARRAY) {
1792 }
1793 subject = zval_get_string(&intern->current.data);
1794 }
1795
1796 /* Exception during string conversion. */
1797 if (EG(exception)) {
1798 RETURN_THROWS();
1799 }
1800
1801 switch (intern->u.regex.mode)
1802 {
1803 case REGIT_MODE_MAX: /* won't happen but makes compiler happy */
1804 case REGIT_MODE_MATCH:
1805 re = php_pcre_pce_re(intern->u.regex.pce);
1806 match_data = php_pcre_create_match_data(0, re);
1807 if (!match_data) {
1809 }
1810 rc = pcre2_match(re, (PCRE2_SPTR)ZSTR_VAL(subject), ZSTR_LEN(subject), 0, 0, match_data, php_pcre_mctx());
1811 RETVAL_BOOL(rc >= 0);
1812 php_pcre_free_match_data(match_data);
1813 break;
1814
1817 zval_ptr_dtor(&intern->current.data);
1818 ZVAL_UNDEF(&intern->current.data);
1819 php_pcre_match_impl(intern->u.regex.pce, subject, &zcount,
1820 &intern->current.data, intern->u.regex.mode == REGIT_MODE_ALL_MATCHES, intern->u.regex.preg_flags, 0);
1821 RETVAL_BOOL(Z_LVAL(zcount) > 0);
1822 break;
1823
1824 case REGIT_MODE_SPLIT:
1825 zval_ptr_dtor(&intern->current.data);
1826 ZVAL_UNDEF(&intern->current.data);
1827 php_pcre_split_impl(intern->u.regex.pce, subject, &intern->current.data, -1, intern->u.regex.preg_flags);
1828 count = zend_hash_num_elements(Z_ARRVAL(intern->current.data));
1829 RETVAL_BOOL(count > 1);
1830 break;
1831
1832 case REGIT_MODE_REPLACE: {
1833 zval *replacement = zend_read_property(intern->std.ce, Z_OBJ_P(ZEND_THIS), "replacement", sizeof("replacement")-1, 1, &rv);
1834 zend_string *replacement_str = zval_try_get_string(replacement);
1835
1836 /* Property type is ?string, so this should always succeed. */
1837 ZEND_ASSERT(replacement_str != NULL);
1838
1839 result = php_pcre_replace_impl(intern->u.regex.pce, subject, ZSTR_VAL(subject), ZSTR_LEN(subject), replacement_str, -1, &count);
1840
1841 if (UNEXPECTED(!result)) {
1842 zend_string_release(replacement_str);
1843 zend_string_release_ex(subject, false);
1845 }
1846
1847 if (intern->u.regex.flags & REGIT_USE_KEY) {
1848 zval_ptr_dtor(&intern->current.key);
1849 ZVAL_STR(&intern->current.key, result);
1850 } else {
1851 zval_ptr_dtor(&intern->current.data);
1852 ZVAL_STR(&intern->current.data, result);
1853 }
1854
1855 zend_string_release(replacement_str);
1856 RETVAL_BOOL(count > 0);
1857 }
1858 }
1859
1860 if (intern->u.regex.flags & REGIT_INVERTED) {
1862 }
1863 zend_string_release_ex(subject, false);
1864} /* }}} */
1865
1866/* {{{ Returns current regular expression */
1868{
1869 spl_dual_it_object *intern;
1870
1872
1874
1875 RETURN_STR_COPY(intern->u.regex.regex);
1876} /* }}} */
1877
1878/* {{{ Returns current operation mode */
1880{
1881 spl_dual_it_object *intern;
1882
1884
1886
1887 RETURN_LONG(intern->u.regex.mode);
1888} /* }}} */
1889
1890/* {{{ Set new operation mode */
1892{
1893 spl_dual_it_object *intern;
1895
1897 RETURN_THROWS();
1898 }
1899
1901 zend_argument_value_error(1, "must be RegexIterator::MATCH, RegexIterator::GET_MATCH, "
1902 "RegexIterator::ALL_MATCHES, RegexIterator::SPLIT, or RegexIterator::REPLACE");
1903 RETURN_THROWS();
1904 }
1905
1907
1908 intern->u.regex.mode = mode;
1909} /* }}} */
1910
1911/* {{{ Returns current operation flags */
1913{
1914 spl_dual_it_object *intern;
1915
1917
1919
1920 RETURN_LONG(intern->u.regex.flags);
1921} /* }}} */
1922
1923/* {{{ Set operation flags */
1925{
1926 spl_dual_it_object *intern;
1928
1930 RETURN_THROWS();
1931 }
1932
1934
1935 intern->u.regex.flags = flags;
1936} /* }}} */
1937
1938/* {{{ Returns current PREG flags (if in use or NULL) */
1940{
1941 spl_dual_it_object *intern;
1942
1944
1946
1947 RETURN_LONG(intern->u.regex.preg_flags);
1948} /* }}} */
1949
1950/* {{{ Set PREG flags */
1952{
1953 spl_dual_it_object *intern;
1954 zend_long preg_flags;
1955
1956 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &preg_flags) == FAILURE) {
1957 RETURN_THROWS();
1958 }
1959
1961
1962 intern->u.regex.preg_flags = preg_flags;
1963} /* }}} */
1964
1965/* {{{ Create an RecursiveRegexIterator from another recursive iterator and a regular expression */
1970
1971/* {{{ Return the inner iterator's children contained in a RecursiveRegexIterator */
1973{
1974 spl_dual_it_object *intern;
1975 zval retval;
1976
1978
1980
1981 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &retval);
1982 if (EG(exception)) {
1984 RETURN_THROWS();
1985 }
1986
1987 zval args[5];
1989 ZVAL_STR_COPY(&args[1], intern->u.regex.regex);
1990 ZVAL_LONG(&args[2], intern->u.regex.mode);
1991 ZVAL_LONG(&args[3], intern->u.regex.flags);
1992 ZVAL_LONG(&args[4], intern->u.regex.preg_flags);
1993
1995
1996 zval_ptr_dtor(&args[0]);
1997 zval_ptr_dtor_str(&args[1]);
1998 if (is_initialized == FAILURE) {
1999 RETURN_THROWS();
2000 }
2001} /* }}} */
2002
2004{
2005 spl_dual_it_object *intern;
2006
2008
2010
2011 if (Z_TYPE(intern->current.data) == IS_UNDEF) {
2013 } else if (Z_TYPE(intern->current.data) == IS_ARRAY) {
2014 RETURN_BOOL(zend_hash_num_elements(Z_ARRVAL(intern->current.data)) > 0);
2015 }
2016
2017 zend_call_method_with_0_params(Z_OBJ_P(ZEND_THIS), spl_ce_RegexIterator, NULL, "accept", return_value);
2018}
2019
2020/* {{{ spl_dual_it_free_storage */
2021static void spl_dual_it_free_storage(zend_object *_object)
2022{
2023 spl_dual_it_object *object = spl_dual_it_from_obj(_object);
2024
2025 spl_dual_it_free(object);
2026
2027 if (object->inner.iterator) {
2028 zend_iterator_dtor(object->inner.iterator);
2029 }
2030
2031 if (!Z_ISUNDEF(object->inner.zobject)) {
2032 zval_ptr_dtor(&object->inner.zobject);
2033 }
2034
2035 if (object->dit_type == DIT_AppendIterator) {
2036 zend_iterator_dtor(object->u.append.iterator);
2037 if (Z_TYPE(object->u.append.zarrayit) != IS_UNDEF) {
2038 zval_ptr_dtor(&object->u.append.zarrayit);
2039 }
2040 }
2041
2042 if (object->dit_type == DIT_CachingIterator || object->dit_type == DIT_RecursiveCachingIterator) {
2043 zval_ptr_dtor(&object->u.caching.zcache);
2044 }
2045
2046 if (object->dit_type == DIT_RegexIterator || object->dit_type == DIT_RecursiveRegexIterator) {
2047 if (object->u.regex.pce) {
2048 php_pcre_pce_decref(object->u.regex.pce);
2049 }
2050 if (object->u.regex.regex) {
2051 zend_string_release_ex(object->u.regex.regex, 0);
2052 }
2053 }
2054
2056 if (ZEND_FCC_INITIALIZED(object->u.callback_filter)) {
2057 zend_fcc_dtor(&object->u.callback_filter);
2058 }
2059 }
2060
2062}
2063/* }}} */
2064
2065static HashTable *spl_dual_it_get_gc(zend_object *obj, zval **table, int *n)
2066{
2067 spl_dual_it_object *object = spl_dual_it_from_obj(obj);
2069
2070 if (object->inner.iterator) {
2071 zend_get_gc_buffer_add_obj(gc_buffer, &object->inner.iterator->std);
2072 }
2073
2074 zend_get_gc_buffer_add_zval(gc_buffer, &object->current.data);
2075 zend_get_gc_buffer_add_zval(gc_buffer, &object->current.key);
2076 zend_get_gc_buffer_add_zval(gc_buffer, &object->inner.zobject);
2077
2078 switch (object->dit_type) {
2079 case DIT_Unknown:
2080 case DIT_Default:
2084 case DIT_LimitIterator:
2085 case DIT_RegexIterator:
2087 /* Nothing to do */
2088 break;
2089 case DIT_AppendIterator:
2090 zend_get_gc_buffer_add_obj(gc_buffer, &object->u.append.iterator->std);
2091 if (Z_TYPE(object->u.append.zarrayit) != IS_UNDEF) {
2092 zend_get_gc_buffer_add_zval(gc_buffer, &object->u.append.zarrayit);
2093 }
2094 break;
2097 zend_get_gc_buffer_add_zval(gc_buffer, &object->u.caching.zcache);
2098 zend_get_gc_buffer_add_zval(gc_buffer, &object->u.caching.zchildren);
2099 break;
2102 if (ZEND_FCC_INITIALIZED(object->u.callback_filter)) {
2103 zend_get_gc_buffer_add_fcc(gc_buffer, &object->u.callback_filter);
2104 }
2105 break;
2106 }
2107
2108 zend_get_gc_buffer_use(gc_buffer, table, n);
2109 return zend_std_get_properties(obj);
2110}
2111
2112/* {{{ spl_dual_it_new */
2113static zend_object *spl_dual_it_new(zend_class_entry *class_type)
2114{
2115 spl_dual_it_object *intern;
2116
2117 intern = zend_object_alloc(sizeof(spl_dual_it_object), class_type);
2118 intern->dit_type = DIT_Unknown;
2119
2120 zend_object_std_init(&intern->std, class_type);
2121 object_properties_init(&intern->std, class_type);
2122
2123 return &intern->std;
2124}
2125/* }}} */
2126
2127static inline zend_result spl_limit_it_valid(spl_dual_it_object *intern)
2128{
2129 /* FAILURE / SUCCESS */
2130 if (intern->u.limit.count != -1 && intern->current.pos >= intern->u.limit.offset + intern->u.limit.count) {
2131 return FAILURE;
2132 } else {
2133 return spl_dual_it_valid(intern);
2134 }
2135}
2136
2137static inline void spl_limit_it_seek(spl_dual_it_object *intern, zend_long pos)
2138{
2139 zval zpos;
2140
2141 spl_dual_it_free(intern);
2142 if (pos < intern->u.limit.offset) {
2143 zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is below the offset " ZEND_LONG_FMT, pos, intern->u.limit.offset);
2144 return;
2145 }
2146 if (pos - intern->u.limit.offset >= intern->u.limit.count && intern->u.limit.count != -1) {
2147 zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is behind offset " ZEND_LONG_FMT " plus count " ZEND_LONG_FMT, pos, intern->u.limit.offset, intern->u.limit.count);
2148 return;
2149 }
2150 if (pos != intern->current.pos && instanceof_function(intern->inner.ce, spl_ce_SeekableIterator)) {
2151 ZVAL_LONG(&zpos, pos);
2152 spl_dual_it_free(intern);
2153 zend_call_method_with_1_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "seek", NULL, &zpos);
2154 if (!EG(exception)) {
2155 intern->current.pos = pos;
2156 if (spl_limit_it_valid(intern) == SUCCESS) {
2157 spl_dual_it_fetch(intern, 0);
2158 }
2159 }
2160 } else {
2161 /* emulate the forward seek, by next() calls */
2162 /* a back ward seek is done by a previous rewind() */
2163 if (pos < intern->current.pos) {
2164 spl_dual_it_rewind(intern);
2165 }
2166 while (pos > intern->current.pos && spl_dual_it_valid(intern) == SUCCESS) {
2167 spl_dual_it_next(intern, 1);
2168 }
2169 if (spl_dual_it_valid(intern) == SUCCESS) {
2170 spl_dual_it_fetch(intern, 1);
2171 }
2172 }
2173}
2174
2175/* {{{ Construct a LimitIterator from an Iterator with a given starting offset and optionally a maximum count */
2180
2181/* {{{ Rewind the iterator to the specified starting offset */
2183{
2184 spl_dual_it_object *intern;
2185
2187
2189 spl_dual_it_rewind(intern);
2190 spl_limit_it_seek(intern, intern->u.limit.offset);
2191} /* }}} */
2192
2193/* {{{ Check whether the current element is valid */
2195{
2196 spl_dual_it_object *intern;
2197
2199
2201
2202/* RETURN_BOOL(spl_limit_it_valid(intern) == SUCCESS);*/
2203 RETURN_BOOL((intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) && Z_TYPE(intern->current.data) != IS_UNDEF);
2204} /* }}} */
2205
2206/* {{{ Move the iterator forward */
2208{
2209 spl_dual_it_object *intern;
2210
2212
2214
2215 spl_dual_it_next(intern, 1);
2216 if (intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) {
2217 spl_dual_it_fetch(intern, 1);
2218 }
2219} /* }}} */
2220
2221/* {{{ Seek to the given position */
2223{
2224 spl_dual_it_object *intern;
2225 zend_long pos;
2226
2228 RETURN_THROWS();
2229 }
2230
2232 spl_limit_it_seek(intern, pos);
2233 RETURN_LONG(intern->current.pos);
2234} /* }}} */
2235
2236/* {{{ Return the current position */
2238{
2239 spl_dual_it_object *intern;
2240
2242
2244 RETURN_LONG(intern->current.pos);
2245} /* }}} */
2246
2247static inline int spl_caching_it_valid(spl_dual_it_object *intern)
2248{
2249 return intern->u.caching.flags & CIT_VALID ? SUCCESS : FAILURE;
2250}
2251
2252static inline int spl_caching_it_has_next(spl_dual_it_object *intern)
2253{
2254 return spl_dual_it_valid(intern);
2255}
2256
2257static inline void spl_caching_it_next(spl_dual_it_object *intern)
2258{
2259 if (spl_dual_it_fetch(intern, 1) == SUCCESS) {
2260 intern->u.caching.flags |= CIT_VALID;
2261 /* Full cache ? */
2262 if (intern->u.caching.flags & CIT_FULL_CACHE) {
2263 zval *key = &intern->current.key;
2264 zval *data = &intern->current.data;
2265
2268 }
2269 /* Recursion ? */
2270 if (intern->dit_type == DIT_RecursiveCachingIterator) {
2271 zval retval;
2272 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "haschildren", &retval);
2273 if (EG(exception)) {
2275 if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2277 } else {
2278 return;
2279 }
2280 } else {
2281 bool has_children = zend_is_true(&retval);
2283
2284 if (has_children) {
2285 zval args[2];
2286
2287 /* Store the children in the first constructor argument */
2288 zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &args[0]);
2289 if (EG(exception)) {
2290 zval_ptr_dtor(&args[0]);
2291 if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2293 } else {
2294 return;
2295 }
2296 } else {
2297 ZVAL_LONG(&args[1], intern->u.caching.flags & CIT_PUBLIC);
2298
2300 &intern->u.caching.zchildren,
2302 2,
2303 args,
2304 NULL
2305 );
2306 zval_ptr_dtor(&args[0]);
2307 if (is_initialized == FAILURE) {
2308 if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
2310 } else {
2311 return;
2312 }
2313 }
2314 }
2315 }
2316 }
2317 }
2319 if (intern->u.caching.flags & CIT_TOSTRING_USE_INNER) {
2320 intern->u.caching.zstr = zval_get_string(&intern->inner.zobject);
2321 } else {
2322 intern->u.caching.zstr = zval_get_string(&intern->current.data);
2323 }
2324 }
2325 spl_dual_it_next(intern, 0);
2326 } else {
2327 intern->u.caching.flags &= ~CIT_VALID;
2328 }
2329}
2330
2331static inline void spl_caching_it_rewind(spl_dual_it_object *intern)
2332{
2333 spl_dual_it_rewind(intern);
2335 spl_caching_it_next(intern);
2336}
2337
2338/* {{{ Construct a CachingIterator from an Iterator */
2343
2344/* {{{ Rewind the iterator */
2346{
2347 spl_dual_it_object *intern;
2348
2350
2352
2353 spl_caching_it_rewind(intern);
2354} /* }}} */
2355
2356/* {{{ Check whether the current element is valid */
2358{
2359 spl_dual_it_object *intern;
2360
2362
2364
2365 RETURN_BOOL(spl_caching_it_valid(intern) == SUCCESS);
2366} /* }}} */
2367
2368/* {{{ Move the iterator forward */
2370{
2371 spl_dual_it_object *intern;
2372
2374
2376
2377 spl_caching_it_next(intern);
2378} /* }}} */
2379
2380/* {{{ Check whether the inner iterator has a valid next element */
2382{
2383 spl_dual_it_object *intern;
2384
2386
2388
2389 RETURN_BOOL(spl_caching_it_has_next(intern) == SUCCESS);
2390} /* }}} */
2391
2392/* {{{ Return the string representation of the current element */
2394{
2395 spl_dual_it_object *intern;
2396
2398
2400
2402 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not fetch string value (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2403 RETURN_THROWS();
2404 }
2405
2406 if (intern->u.caching.flags & CIT_TOSTRING_USE_KEY) {
2407 ZVAL_COPY(return_value, &intern->current.key);
2409 return;
2410 } else if (intern->u.caching.flags & CIT_TOSTRING_USE_CURRENT) {
2413 return;
2414 }
2415 if (intern->u.caching.zstr) {
2416 RETURN_STR_COPY(intern->u.caching.zstr);
2417 } else {
2419 }
2420} /* }}} */
2421
2422/* {{{ Set given index in cache */
2424{
2425 spl_dual_it_object *intern;
2427 zval *value;
2428
2429 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &key, &value) == FAILURE) {
2430 RETURN_THROWS();
2431 }
2432
2434
2435 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2436 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2437 RETURN_THROWS();
2438 }
2439
2441 zend_symtable_update(Z_ARRVAL(intern->u.caching.zcache), key, value);
2442}
2443/* }}} */
2444
2445/* {{{ Return the internal cache if used */
2447{
2448 spl_dual_it_object *intern;
2450 zval *value;
2451
2453 RETURN_THROWS();
2454 }
2455
2457
2458 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2459 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2460 RETURN_THROWS();
2461 }
2462
2463 if ((value = zend_symtable_find(Z_ARRVAL(intern->u.caching.zcache), key)) == NULL) {
2464 zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(key));
2465 return;
2466 }
2467
2469}
2470/* }}} */
2471
2472/* {{{ Unset given index in cache */
2474{
2475 spl_dual_it_object *intern;
2477
2479
2481 RETURN_THROWS();
2482 }
2483
2484 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2485 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2486 RETURN_THROWS();
2487 }
2488
2489 zend_symtable_del(Z_ARRVAL(intern->u.caching.zcache), key);
2490}
2491/* }}} */
2492
2493/* {{{ Return whether the requested index exists */
2495{
2496 spl_dual_it_object *intern;
2498
2500 RETURN_THROWS();
2501 }
2502
2504
2505 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2506 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2507 RETURN_THROWS();
2508 }
2509
2510 RETURN_BOOL(zend_symtable_exists(Z_ARRVAL(intern->u.caching.zcache), key));
2511}
2512/* }}} */
2513
2514/* {{{ Return the cache */
2516{
2517 spl_dual_it_object *intern;
2518
2520
2522
2523 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2524 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2525 RETURN_THROWS();
2526 }
2527
2529}
2530/* }}} */
2531
2532/* {{{ Return the internal flags */
2534{
2535 spl_dual_it_object *intern;
2536
2538
2540
2541 RETURN_LONG(intern->u.caching.flags);
2542}
2543/* }}} */
2544
2545/* {{{ Set the internal flags */
2547{
2548 spl_dual_it_object *intern;
2550
2552 RETURN_THROWS();
2553 }
2554
2556
2557 if (spl_cit_check_flags(flags) != SUCCESS) {
2558 zend_argument_value_error(1, "must contain only one of CachingIterator::CALL_TOSTRING, "
2559 "CachingIterator::TOSTRING_USE_KEY, CachingIterator::TOSTRING_USE_CURRENT, "
2560 "or CachingIterator::TOSTRING_USE_INNER");
2561 RETURN_THROWS();
2562 }
2563 if ((intern->u.caching.flags & CIT_CALL_TOSTRING) != 0 && (flags & CIT_CALL_TOSTRING) == 0) {
2564 zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag CALL_TO_STRING is not possible", 0);
2565 RETURN_THROWS();
2566 }
2567 if ((intern->u.caching.flags & CIT_TOSTRING_USE_INNER) != 0 && (flags & CIT_TOSTRING_USE_INNER) == 0) {
2568 zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag TOSTRING_USE_INNER is not possible", 0);
2569 RETURN_THROWS();
2570 }
2571 if ((flags & CIT_FULL_CACHE) != 0 && (intern->u.caching.flags & CIT_FULL_CACHE) == 0) {
2572 /* clear on (re)enable */
2574 }
2575 intern->u.caching.flags = (intern->u.caching.flags & ~CIT_PUBLIC) | (flags & CIT_PUBLIC);
2576}
2577/* }}} */
2578
2579/* {{{ Number of cached elements */
2581{
2582 spl_dual_it_object *intern;
2583
2585
2587
2588 if (!(intern->u.caching.flags & CIT_FULL_CACHE)) {
2589 zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s does not use a full cache (see CachingIterator::__construct)", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name));
2590 RETURN_THROWS();
2591 }
2592
2593 RETURN_LONG(zend_hash_num_elements(Z_ARRVAL(intern->u.caching.zcache)));
2594}
2595/* }}} */
2596
2597/* {{{ Create an iterator from a RecursiveIterator */
2602
2603/* {{{ Check whether the current element of the inner iterator has children */
2605{
2606 spl_dual_it_object *intern;
2607
2609
2611
2613} /* }}} */
2614
2615/* {{{ Return the inner iterator's children as a RecursiveCachingIterator */
2617{
2618 spl_dual_it_object *intern;
2619
2621
2623
2624 if (Z_TYPE(intern->u.caching.zchildren) != IS_UNDEF) {
2625 zval *value = &intern->u.caching.zchildren;
2626
2628 } else {
2629 RETURN_NULL();
2630 }
2631} /* }}} */
2632
2633/* {{{ Create an iterator from anything that is traversable */
2638
2639/* {{{ Create an iterator from another iterator */
2644
2645/* {{{ Prevent a call to inner iterators rewind() */
2647{
2649 /* nothing to do */
2650} /* }}} */
2651
2652/* {{{ Return inner iterators valid() */
2654{
2655 spl_dual_it_object *intern;
2656
2658
2660 RETURN_BOOL(intern->inner.iterator->funcs->valid(intern->inner.iterator) == SUCCESS);
2661} /* }}} */
2662
2663/* {{{ Return inner iterators key() */
2665{
2666 spl_dual_it_object *intern;
2667
2669
2671
2672 if (intern->inner.iterator->funcs->get_current_key) {
2674 } else {
2675 RETURN_NULL();
2676 }
2677} /* }}} */
2678
2679/* {{{ Return inner iterators current() */
2681{
2682 spl_dual_it_object *intern;
2683 zval *data;
2684
2686
2688 data = intern->inner.iterator->funcs->get_current_data(intern->inner.iterator);
2689 if (data) {
2691 }
2692} /* }}} */
2693
2694/* {{{ Return inner iterators next() */
2696{
2697 spl_dual_it_object *intern;
2698
2700
2702 intern->inner.iterator->funcs->move_forward(intern->inner.iterator);
2703} /* }}} */
2704
2705/* {{{ Create an iterator from another iterator */
2710
2711/* {{{ Prevent a call to inner iterators rewind() (internally the current data will be fetched if valid()) */
2713{
2714 spl_dual_it_object *intern;
2715
2717
2719
2720 spl_dual_it_next(intern, 1);
2721 if (spl_dual_it_valid(intern) == SUCCESS) {
2722 spl_dual_it_fetch(intern, 0);
2723 } else {
2724 spl_dual_it_rewind(intern);
2725 if (spl_dual_it_valid(intern) == SUCCESS) {
2726 spl_dual_it_fetch(intern, 0);
2727 }
2728 }
2729} /* }}} */
2730
2731/* {{{ Does nothing */
2736
2737/* {{{ Return false */
2739{
2742} /* }}} */
2743
2744/* {{{ Throws exception BadMethodCallException */
2746{
2748 zend_throw_exception(spl_ce_BadMethodCallException, "Accessing the key of an EmptyIterator", 0);
2749} /* }}} */
2750
2751/* {{{ Throws exception BadMethodCallException */
2753{
2755 zend_throw_exception(spl_ce_BadMethodCallException, "Accessing the value of an EmptyIterator", 0);
2756} /* }}} */
2757
2758/* {{{ Does nothing */
2763
2764static zend_result spl_append_it_next_iterator(spl_dual_it_object *intern) /* {{{*/
2765{
2766 spl_dual_it_free(intern);
2767
2768 if (!Z_ISUNDEF(intern->inner.zobject)) {
2769 zval_ptr_dtor(&intern->inner.zobject);
2770 ZVAL_UNDEF(&intern->inner.zobject);
2771 intern->inner.ce = NULL;
2772 if (intern->inner.iterator) {
2774 intern->inner.iterator = NULL;
2775 }
2776 }
2777 if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) == SUCCESS) {
2778 zval *it;
2779
2780 it = intern->u.append.iterator->funcs->get_current_data(intern->u.append.iterator);
2781 ZVAL_COPY(&intern->inner.zobject, it);
2782 intern->inner.ce = Z_OBJCE_P(it);
2783 intern->inner.iterator = intern->inner.ce->get_iterator(intern->inner.ce, it, 0);
2784 spl_dual_it_rewind(intern);
2785 return SUCCESS;
2786 } else {
2787 return FAILURE;
2788 }
2789} /* }}} */
2790
2791static void spl_append_it_fetch(spl_dual_it_object *intern) /* {{{*/
2792{
2793 while (spl_dual_it_valid(intern) != SUCCESS) {
2794 intern->u.append.iterator->funcs->move_forward(intern->u.append.iterator);
2795 if (spl_append_it_next_iterator(intern) != SUCCESS) {
2796 return;
2797 }
2798 }
2799 spl_dual_it_fetch(intern, 0);
2800} /* }}} */
2801
2802static void spl_append_it_next(spl_dual_it_object *intern) /* {{{ */
2803{
2804 if (spl_dual_it_valid(intern) == SUCCESS) {
2805 spl_dual_it_next(intern, 1);
2806 }
2807 spl_append_it_fetch(intern);
2808} /* }}} */
2809
2810/* {{{ Create an AppendIterator */
2815
2816/* {{{ Append an iterator */
2818{
2819 spl_dual_it_object *intern;
2820 zval *it;
2821
2823 RETURN_THROWS();
2824 }
2825
2827
2828 if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) == SUCCESS && spl_dual_it_valid(intern) != SUCCESS) {
2830 intern->u.append.iterator->funcs->move_forward(intern->u.append.iterator);
2831 }else{
2833 }
2834
2835 if (!intern->inner.iterator || spl_dual_it_valid(intern) != SUCCESS) {
2836 if (intern->u.append.iterator->funcs->valid(intern->u.append.iterator) != SUCCESS) {
2837 intern->u.append.iterator->funcs->rewind(intern->u.append.iterator);
2838 }
2839 do {
2840 spl_append_it_next_iterator(intern);
2841 } while (Z_OBJ(intern->inner.zobject) != Z_OBJ_P(it));
2842 spl_append_it_fetch(intern);
2843 }
2844} /* }}} */
2845
2846/* {{{ Get the current element value */
2848{
2849 spl_dual_it_object *intern;
2850
2852
2854
2855 spl_dual_it_fetch(intern, 1);
2856 if (Z_TYPE(intern->current.data) != IS_UNDEF) {
2857 RETURN_COPY_DEREF(&intern->current.data);
2858 } else {
2859 RETURN_NULL();
2860 }
2861} /* }}} */
2862
2863/* {{{ Rewind to the first iterator and rewind the first iterator, too */
2865{
2866 spl_dual_it_object *intern;
2867
2869
2871
2872 intern->u.append.iterator->funcs->rewind(intern->u.append.iterator);
2873 if (spl_append_it_next_iterator(intern) == SUCCESS) {
2874 spl_append_it_fetch(intern);
2875 }
2876} /* }}} */
2877
2878/* {{{ Check if the current state is valid */
2880{
2881 spl_dual_it_object *intern;
2882
2884
2886
2887 RETURN_BOOL(Z_TYPE(intern->current.data) != IS_UNDEF);
2888} /* }}} */
2889
2890/* {{{ Forward to next element */
2892{
2893 spl_dual_it_object *intern;
2894
2896
2898
2899 spl_append_it_next(intern);
2900} /* }}} */
2901
2902/* {{{ Get index of iterator */
2903PHP_METHOD(AppendIterator, getIteratorIndex)
2904{
2905 spl_dual_it_object *intern;
2906
2908
2910
2911 APPENDIT_CHECK_CTOR(intern);
2913} /* }}} */
2914
2915/* {{{ Get access to inner ArrayIterator */
2916PHP_METHOD(AppendIterator, getArrayIterator)
2917{
2918 spl_dual_it_object *intern;
2919 zval *value;
2920
2922
2924
2925 value = &intern->u.append.zarrayit;
2927} /* }}} */
2928
2930{
2932 zend_class_entry *ce = Z_OBJCE_P(obj);
2933
2934 iter = ce->get_iterator(ce, obj, 0);
2935
2936 if (EG(exception)) {
2937 goto done;
2938 }
2939
2940 iter->index = 0;
2941 if (iter->funcs->rewind) {
2942 iter->funcs->rewind(iter);
2943 if (EG(exception)) {
2944 goto done;
2945 }
2946 }
2947
2948 while (iter->funcs->valid(iter) == SUCCESS) {
2949 if (EG(exception)) {
2950 goto done;
2951 }
2952 if (apply_func(iter, puser) == ZEND_HASH_APPLY_STOP || EG(exception)) {
2953 goto done;
2954 }
2955 iter->index++;
2956 iter->funcs->move_forward(iter);
2957 if (EG(exception)) {
2958 goto done;
2959 }
2960 }
2961
2962done:
2963 if (iter) {
2964 zend_iterator_dtor(iter);
2965 }
2966 return EG(exception) ? FAILURE : SUCCESS;
2967}
2968/* }}} */
2969
2970static int spl_iterator_to_array_apply(zend_object_iterator *iter, void *puser) /* {{{ */
2971{
2972 zval *data, *return_value = (zval*)puser;
2973
2974 data = iter->funcs->get_current_data(iter);
2975 if (EG(exception)) {
2976 return ZEND_HASH_APPLY_STOP;
2977 }
2978 if (data == NULL) {
2979 return ZEND_HASH_APPLY_STOP;
2980 }
2981 if (iter->funcs->get_current_key) {
2982 zval key;
2983 iter->funcs->get_current_key(iter, &key);
2984 if (EG(exception)) {
2985 return ZEND_HASH_APPLY_STOP;
2986 }
2989 } else {
2991 add_next_index_zval(return_value, data);
2992 }
2993 return ZEND_HASH_APPLY_KEEP;
2994}
2995/* }}} */
2996
2997static int spl_iterator_to_values_apply(zend_object_iterator *iter, void *puser) /* {{{ */
2998{
2999 zval *data, *return_value = (zval*)puser;
3000
3001 data = iter->funcs->get_current_data(iter);
3002 if (EG(exception)) {
3003 return ZEND_HASH_APPLY_STOP;
3004 }
3005 if (data == NULL) {
3006 return ZEND_HASH_APPLY_STOP;
3007 }
3009 add_next_index_zval(return_value, data);
3010 return ZEND_HASH_APPLY_KEEP;
3011}
3012/* }}} */
3013
3014/* {{{ Copy the iterator into an array */
3016{
3017 zval *obj;
3018 bool use_keys = 1;
3019
3021 Z_PARAM_ITERABLE(obj)
3023 Z_PARAM_BOOL(use_keys)
3025
3026 if (Z_TYPE_P(obj) == IS_ARRAY) {
3027 if (use_keys) {
3028 RETURN_COPY(obj);
3029 } else {
3031 }
3032 }
3033
3035 spl_iterator_apply(obj, use_keys ? spl_iterator_to_array_apply : spl_iterator_to_values_apply, (void*)return_value);
3036} /* }}} */
3037
3038static int spl_iterator_count_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3039{
3040 if (UNEXPECTED(*(zend_long*)puser == ZEND_LONG_MAX)) {
3041 return ZEND_HASH_APPLY_STOP;
3042 }
3043 (*(zend_long*)puser)++;
3044 return ZEND_HASH_APPLY_KEEP;
3045}
3046/* }}} */
3047
3048/* {{{ Count the elements in an iterator */
3050{
3051 zval *obj;
3052 zend_long count = 0;
3053
3055 Z_PARAM_ITERABLE(obj)
3057
3058 if (Z_TYPE_P(obj) == IS_ARRAY) {
3059 count = zend_hash_num_elements(Z_ARRVAL_P(obj));
3060 } else {
3061 if (spl_iterator_apply(obj, spl_iterator_count_apply, (void*)&count) == FAILURE) {
3062 RETURN_THROWS();
3063 }
3064 }
3065
3067}
3068/* }}} */
3069
3076
3077static int spl_iterator_func_apply(zend_object_iterator *iter, void *puser) /* {{{ */
3078{
3079 zval retval;
3081 int result;
3082
3083 apply_info->count++;
3084 zend_call_function_with_return_value(&apply_info->fci, &apply_info->fcc, &retval);
3087 return result;
3088}
3089/* }}} */
3090
3091/* {{{ Calls a function for every element in an iterator */
3093{
3094 spl_iterator_apply_info apply_info;
3095
3096 /* The HashTable is used to determine positional arguments */
3097 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Of|h!", &apply_info.obj, zend_ce_traversable,
3098 &apply_info.fci, &apply_info.fcc, &apply_info.fci.named_params) == FAILURE) {
3099 RETURN_THROWS();
3100 }
3101
3102 apply_info.count = 0;
3103 if (spl_iterator_apply(apply_info.obj, spl_iterator_func_apply, (void*)&apply_info) == FAILURE) {
3104 return;
3105 }
3106 RETURN_LONG(apply_info.count);
3107}
3108/* }}} */
3109
3110/* {{{ PHP_MINIT_FUNCTION(spl_iterators) */
3112{
3113 spl_ce_RecursiveIterator = register_class_RecursiveIterator(zend_ce_iterator);
3114
3115 spl_ce_OuterIterator = register_class_OuterIterator(zend_ce_iterator);
3116
3117 spl_ce_RecursiveIteratorIterator = register_class_RecursiveIteratorIterator(spl_ce_OuterIterator);
3118 spl_ce_RecursiveIteratorIterator->create_object = spl_RecursiveIteratorIterator_new;
3119 spl_ce_RecursiveIteratorIterator->default_object_handlers = &spl_handlers_rec_it_it;
3120 spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator;
3121
3122 memcpy(&spl_handlers_rec_it_it, &std_object_handlers, sizeof(zend_object_handlers));
3123 spl_handlers_rec_it_it.offset = XtOffsetOf(spl_recursive_it_object, std);
3124 spl_handlers_rec_it_it.get_method = spl_recursive_it_get_method;
3125 spl_handlers_rec_it_it.clone_obj = NULL;
3126 spl_handlers_rec_it_it.free_obj = spl_RecursiveIteratorIterator_free_storage;
3127 spl_handlers_rec_it_it.get_gc = spl_RecursiveIteratorIterator_get_gc;
3128
3129 memcpy(&spl_handlers_dual_it, &std_object_handlers, sizeof(zend_object_handlers));
3130 spl_handlers_dual_it.offset = XtOffsetOf(spl_dual_it_object, std);
3131 spl_handlers_dual_it.get_method = spl_dual_it_get_method;
3132 spl_handlers_dual_it.clone_obj = NULL;
3133 spl_handlers_dual_it.free_obj = spl_dual_it_free_storage;
3134 spl_handlers_dual_it.get_gc = spl_dual_it_get_gc;
3135
3136 spl_ce_IteratorIterator = register_class_IteratorIterator(spl_ce_OuterIterator);
3137 spl_ce_IteratorIterator->create_object = spl_dual_it_new;
3138 spl_ce_IteratorIterator->default_object_handlers = &spl_handlers_dual_it;
3139
3140 spl_ce_FilterIterator = register_class_FilterIterator(spl_ce_IteratorIterator);
3141 spl_ce_FilterIterator->create_object = spl_dual_it_new;
3142
3143 spl_ce_RecursiveFilterIterator = register_class_RecursiveFilterIterator(spl_ce_FilterIterator, spl_ce_RecursiveIterator);
3144 spl_ce_RecursiveFilterIterator->create_object = spl_dual_it_new;
3145
3146 spl_ce_CallbackFilterIterator = register_class_CallbackFilterIterator(spl_ce_FilterIterator);
3147 spl_ce_CallbackFilterIterator->create_object = spl_dual_it_new;
3148
3150 spl_ce_RecursiveCallbackFilterIterator->create_object = spl_dual_it_new;
3151
3152 spl_ce_ParentIterator = register_class_ParentIterator(spl_ce_RecursiveFilterIterator);
3153 spl_ce_ParentIterator->create_object = spl_dual_it_new;
3154
3155 spl_ce_SeekableIterator = register_class_SeekableIterator(zend_ce_iterator);
3156
3157 spl_ce_LimitIterator = register_class_LimitIterator(spl_ce_IteratorIterator);
3158 spl_ce_LimitIterator->create_object = spl_dual_it_new;
3159
3161 spl_ce_CachingIterator->create_object = spl_dual_it_new;
3162
3163 spl_ce_RecursiveCachingIterator = register_class_RecursiveCachingIterator(spl_ce_CachingIterator, spl_ce_RecursiveIterator);
3164 spl_ce_RecursiveCachingIterator->create_object = spl_dual_it_new;
3165
3166 spl_ce_NoRewindIterator = register_class_NoRewindIterator(spl_ce_IteratorIterator);
3167 spl_ce_NoRewindIterator->create_object = spl_dual_it_new;
3168
3169 spl_ce_AppendIterator = register_class_AppendIterator(spl_ce_IteratorIterator);
3170 spl_ce_AppendIterator->create_object = spl_dual_it_new;
3171
3172 spl_ce_InfiniteIterator = register_class_InfiniteIterator(spl_ce_IteratorIterator);
3173 spl_ce_InfiniteIterator->create_object = spl_dual_it_new;
3174
3175 spl_ce_RegexIterator = register_class_RegexIterator(spl_ce_FilterIterator);
3176 spl_ce_RegexIterator->create_object = spl_dual_it_new;
3177
3178 spl_ce_RecursiveRegexIterator = register_class_RecursiveRegexIterator(spl_ce_RegexIterator, spl_ce_RecursiveIterator);
3179 spl_ce_RecursiveRegexIterator->create_object = spl_dual_it_new;
3180
3181 spl_ce_EmptyIterator = register_class_EmptyIterator(zend_ce_iterator);
3182
3183 spl_ce_RecursiveTreeIterator = register_class_RecursiveTreeIterator(spl_ce_RecursiveIteratorIterator);
3184 spl_ce_RecursiveTreeIterator->create_object = spl_RecursiveTreeIterator_new;
3185
3186 return SUCCESS;
3187}
3188/* }}} */
bool exception
Definition assert.c:30
rewind($stream)
count(Countable|array $value, int $mode=COUNT_NORMAL)
uint32_t u
Definition cdf.c:78
zend_long n
Definition ffi.c:4979
memcpy(ptr1, ptr2, size)
char * mode
#define NULL
Definition gdcache.h:45
#define prefix
#define SUCCESS
Definition hash_sha3.c:261
#define next(ls)
Definition minilua.c:2661
#define pcre2_code
Definition pcre2.h:822
#define pcre2_match
Definition pcre2.h:881
#define PCRE2_SPTR
Definition pcre2.h:820
#define pcre2_match_data
Definition pcre2.h:844
#define PHP_FUNCTION
Definition php.h:364
#define PHP_MINIT_FUNCTION
Definition php.h:400
#define INT_MAX
Definition php.h:237
#define PHP_METHOD
Definition php.h:365
#define PHPAPI
Definition php.h:71
unsigned const char * pos
Definition php_ffi.h:52
PHPAPI pcre2_match_context * php_pcre_mctx(void)
Definition php_pcre.c:3054
PHPAPI pcre2_code * php_pcre_pce_re(pcre_cache_entry *pce)
Definition php_pcre.c:3082
PHPAPI zend_string * php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *subject_str, const char *subject, size_t subject_len, zend_string *replace_str, size_t limit, size_t *replace_count)
Definition php_pcre.c:1614
PHPAPI pcre2_match_data * php_pcre_create_match_data(uint32_t capture_count, pcre2_code *re)
Definition php_pcre.c:906
PHPAPI void php_pcre_pce_decref(pcre_cache_entry *pce)
Definition php_pcre.c:3075
PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str, zval *return_value, zval *subpats, bool global, zend_long flags, zend_off_t start_offset)
Definition php_pcre.c:1156
PHPAPI void php_pcre_pce_incref(pcre_cache_entry *pce)
Definition php_pcre.c:3069
PHPAPI pcre_cache_entry * pcre_get_compiled_regex_cache(zend_string *regex)
Definition php_pcre.c:885
PHPAPI void php_pcre_free_match_data(pcre2_match_data *match_data)
Definition php_pcre.c:928
PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, zend_string *subject_str, zval *return_value, zend_long limit_val, zend_long flags)
Definition php_pcre.c:2564
struct _pcre_cache_entry pcre_cache_entry
Definition php_pcre.h:37
unsigned char key[REFLECTION_KEY_LEN]
iterator_count(iterable $iterator)
iterator_to_array(iterable $iterator, bool $preserve_keys=true)
iterator_apply(Traversable $iterator, callable $callback, ?array $args=null)
HashTable seek
Definition phpdbg.h:233
zend_constant * data
zval * current
Definition session.c:1024
zval rv
Definition session.c:1024
void spl_array_iterator_append(zval *object, zval *append_value)
Definition spl_array.c:716
PHPAPI zend_class_entry * spl_ce_ArrayIterator
Definition spl_array.c:34
void spl_array_iterator_key(zval *object, zval *return_value)
Definition spl_array.c:1779
PHPAPI zend_class_entry * spl_ce_LogicException
PHPAPI zend_class_entry * spl_ce_BadMethodCallException
PHPAPI zend_class_entry * spl_ce_UnexpectedValueException
PHPAPI zend_class_entry * spl_ce_OutOfBoundsException
PHPAPI zend_class_entry * spl_ce_InvalidArgumentException
PHPAPI zend_class_entry * spl_ce_IteratorIterator
PHPAPI zend_class_entry * spl_ce_RecursiveCallbackFilterIterator
struct _spl_recursive_it_object spl_recursive_it_object
#define SPL_FETCH_SUB_ELEMENT_ADDR(var, object, element)
RecursiveIteratorState
@ RS_SELF
@ RS_NEXT
@ RS_CHILD
@ RS_TEST
@ RS_START
PHPAPI zend_result spl_iterator_apply(zval *obj, spl_iterator_apply_func_t apply_func, void *puser)
PHPAPI zend_class_entry * spl_ce_InfiniteIterator
PHPAPI zend_class_entry * spl_ce_RecursiveFilterIterator
PHPAPI zend_class_entry * spl_ce_RecursiveCachingIterator
PHPAPI zend_class_entry * spl_ce_LimitIterator
PHPAPI zend_class_entry * spl_ce_CallbackFilterIterator
PHPAPI zend_class_entry * spl_ce_RecursiveTreeIterator
PHPAPI zend_class_entry * spl_ce_AppendIterator
PHPAPI zend_class_entry * spl_ce_CachingIterator
#define SPL_FETCH_AND_CHECK_DUAL_IT(var, objzval)
PHPAPI zend_class_entry * spl_ce_RecursiveIteratorIterator
PHPAPI zend_class_entry * spl_ce_RecursiveRegexIterator
#define APPENDIT_CHECK_CTOR(intern)
#define Z_SPLDUAL_IT_P(zv)
PHPAPI zend_class_entry * spl_ce_RegexIterator
struct _spl_dual_it_object spl_dual_it_object
#define Z_SPLRECURSIVE_IT_P(zv)
PHPAPI zend_class_entry * spl_ce_RecursiveIterator
#define SPL_FETCH_SUB_ELEMENT(var, object, element)
PHPAPI zend_class_entry * spl_ce_FilterIterator
PHPAPI zend_class_entry * spl_ce_SeekableIterator
PHPAPI zend_class_entry * spl_ce_EmptyIterator
PHPAPI zend_class_entry * spl_ce_ParentIterator
struct _spl_recursive_it_iterator spl_recursive_it_iterator
PHPAPI zend_class_entry * spl_ce_OuterIterator
#define SPL_FETCH_SUB_ITERATOR(var, object)
struct _spl_sub_iterator spl_sub_iterator
PHPAPI zend_class_entry * spl_ce_NoRewindIterator
int(* spl_iterator_apply_func_t)(zend_object_iterator *iter, void *puser)
recursive_it_it_type
@ RIT_RecursiveTreeIterator
@ RIT_RecursiveIteratorIterator
#define RIT_CATCH_GET_CHILD
@ RTIT_BYPASS_KEY
@ RTIT_BYPASS_CURRENT
RecursiveIteratorMode
@ RIT_CHILD_FIRST
@ RIT_SELF_FIRST
@ RIT_LEAVES_ONLY
regex_mode
@ REGIT_MODE_ALL_MATCHES
@ REGIT_MODE_MAX
@ REGIT_MODE_SPLIT
@ REGIT_MODE_MATCH
@ REGIT_MODE_GET_MATCH
@ REGIT_MODE_REPLACE
@ CIT_TOSTRING_USE_CURRENT
@ CIT_TOSTRING_USE_INNER
@ CIT_PUBLIC
@ CIT_TOSTRING_USE_KEY
@ CIT_CATCH_GET_CHILD
@ CIT_CALL_TOSTRING
@ CIT_FULL_CACHE
@ CIT_VALID
dual_it_type
@ DIT_InfiniteIterator
@ DIT_Unknown
@ DIT_CachingIterator
@ DIT_IteratorIterator
@ DIT_RegexIterator
@ DIT_FilterIterator
@ DIT_RecursiveRegexIterator
@ DIT_RecursiveCachingIterator
@ DIT_Default
@ DIT_RecursiveCallbackFilterIterator
@ DIT_ParentIterator
@ DIT_RecursiveFilterIterator
@ DIT_NoRewindIterator
@ DIT_CallbackFilterIterator
@ DIT_AppendIterator
@ DIT_LimitIterator
@ REGIT_INVERTED
@ REGIT_USE_KEY
zend_string * regex
pcre_cache_entry * pce
struct _spl_dual_it_object::@012240072325330126316172276321013152034333106151::@212234175242011007307133367026020110337331214204 append
union _spl_dual_it_object::@012240072325330126316172276321013152034333106151 u
zend_fcall_info_cache callback_filter
struct _spl_dual_it_object::@005376174137325342047325047243100202123274372045 current
dual_it_type dit_type
struct _spl_dual_it_object::@012240072325330126316172276321013152034333106151::@006272062141275373266122061203141270350377172271 caching
struct _spl_dual_it_object::@012240072325330126316172276321013152034333106151::@342144152202164315055333174206037133114170012047 limit
zend_object * object
zend_object_iterator * iterator
zend_class_entry * ce
struct _spl_dual_it_object::@251115313140126033323014171122330174020351311013 inner
zend_object_iterator intern
zend_function * beginIteration
zend_class_entry * ce
zend_function * callGetChildren
spl_sub_iterator * iterators
zend_function * endIteration
zend_string * postfix[1]
zend_function * beginChildren
zend_function * callHasChildren
RecursiveIteratorMode mode
zend_string * prefix[6]
zend_function * endChildren
zend_function * nextElement
RecursiveIteratorState state
zend_object_iterator * iterator
zend_class_entry * ce
zend_function * getchildren
zend_function * haschildren
zend_class_iterator_funcs * iterator_funcs_ptr
Definition zend.h:189
zend_object_iterator *(* get_iterator)(zend_class_entry *ce, zval *object, int by_ref)
Definition zend.h:198
zend_string * name
Definition zend.h:149
HashTable function_table
Definition zend.h:163
zend_function * zf_new_iterator
HashTable * named_params
Definition zend_API.h:56
void(* get_current_key)(zend_object_iterator *iter, zval *key)
void(* invalidate_current)(zend_object_iterator *iter)
void(* rewind)(zend_object_iterator *iter)
void(* move_forward)(zend_object_iterator *iter)
zend_result(* valid)(zend_object_iterator *iter)
zval *(* get_current_data)(zend_object_iterator *iter)
const zend_object_iterator_funcs * funcs
zend_class_entry * ce
Definition zend_types.h:560
zend_string * s
zend_fcall_info_cache fcc
zend_class_entry * scope
struct _zend_function::@236135173067030250234125302313220025134003177336 common
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format,...)
Definition zend.c:1772
ZEND_API ZEND_COLD void zend_error(int type, const char *format,...)
Definition zend.c:1666
#define INTERNAL_FUNCTION_PARAMETERS
Definition zend.h:49
@ EH_THROW
Definition zend.h:433
ZEND_API void zend_replace_error_handling(zend_error_handling_t error_handling, zend_class_entry *exception_class, zend_error_handling *current)
Definition zend_API.c:5242
ZEND_API void zend_restore_error_handling(zend_error_handling *saved)
Definition zend_API.c:5253
#define INTERNAL_FUNCTION_PARAM_PASSTHRU
Definition zend.h:50
ZEND_API zend_result array_set_zval_key(HashTable *ht, zval *key, zval *value)
Definition zend_API.c:2231
ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type)
Definition zend_API.c:1849
ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *class_type, uint32_t param_count, zval *params, HashTable *named_params)
Definition zend_API.c:1855
ZEND_API zval * zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, bool silent, zval *rv)
Definition zend_API.c:5201
ZEND_API void zend_get_callable_zval_from_fcc(const zend_fcall_info_cache *fcc, zval *callable)
Definition zend_API.c:4468
ZEND_API zend_result zend_parse_parameters(uint32_t num_args, const char *type_spec,...)
Definition zend_API.c:1300
ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type)
Definition zend_API.c:1688
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
#define RETURN_COPY_DEREF(zv)
Definition zend_API.h:1056
#define ZEND_NUM_ARGS()
Definition zend_API.h:530
struct _zend_fcall_info_cache zend_fcall_info_cache
#define RETURN_COPY(zv)
Definition zend_API.h:1054
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define RETURN_FALSE
Definition zend_API.h:1058
#define ZEND_PARSE_PARAMETERS_NONE()
Definition zend_API.h:1623
#define RETURN_NULL()
Definition zend_API.h:1036
#define RETURN_ARR(r)
Definition zend_API.h:1050
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define zend_parse_parameters_none()
Definition zend_API.h:353
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define RETURN_LONG(l)
Definition zend_API.h:1037
#define RETURN_BOOL(b)
Definition zend_API.h:1035
#define RETURN_NEW_STR(s)
Definition zend_API.h:1041
struct _zend_fcall_info zend_fcall_info
#define Z_PARAM_ITERABLE(dest)
Definition zend_API.h:1711
#define RETURN_THROWS()
Definition zend_API.h:1060
#define RETURN_STR(s)
Definition zend_API.h:1039
#define ZEND_THIS
Definition zend_API.h:523
#define RETVAL_BOOL(b)
Definition zend_API.h:1009
#define ZEND_FCC_INITIALIZED(fcc)
Definition zend_API.h:341
#define RETURN_COPY_VALUE(zv)
Definition zend_API.h:1055
#define Z_PARAM_BOOL(dest)
Definition zend_API.h:1726
#define RETURN_EMPTY_STRING()
Definition zend_API.h:1047
#define RETURN_STR_COPY(s)
Definition zend_API.h:1042
#define array_init(arg)
Definition zend_API.h:537
#define efree(ptr)
Definition zend_alloc.h:155
#define erealloc(ptr, size)
Definition zend_alloc.h:159
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
zend_string_release_ex(func->internal_function.function_name, 0)
zval * args
#define E_WARNING
Definition zend_errors.h:24
ZEND_API ZEND_COLD zend_object * zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code)
ZEND_API void zend_clear_exception(void)
ZEND_API ZEND_COLD zend_object * zend_throw_exception_ex(zend_class_entry *exception_ce, zend_long code, const char *format,...)
ZEND_API zend_class_entry * zend_lookup_class(zend_string *name)
union _zend_function zend_function
ZEND_API zend_get_gc_buffer * zend_get_gc_buffer_create(void)
Definition zend_gc.c:2130
#define EG(v)
ZEND_API HashTable * zend_array_to_list(HashTable *source)
Definition zend_hash.c:2526
ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht)
Definition zend_hash.c:1869
#define ZEND_HASH_APPLY_STOP
Definition zend_hash.h:148
#define ZEND_HASH_APPLY_KEEP
Definition zend_hash.h:146
ZEND_API zend_class_entry * zend_ce_countable
ZEND_API zend_class_entry * zend_ce_iterator
ZEND_API zend_class_entry * zend_ce_traversable
ZEND_API zend_class_entry * zend_ce_stringable
ZEND_API zend_class_entry * zend_ce_arrayaccess
ZEND_API zend_class_entry * zend_ce_aggregate
ZEND_API void zend_iterator_init(zend_object_iterator *iter)
ZEND_API void zend_iterator_dtor(zend_object_iterator *iter)
struct _zend_object_iterator zend_object_iterator
struct _zend_object_iterator_funcs zend_object_iterator_funcs
int32_t zend_long
Definition zend_long.h:42
#define ZEND_LONG_FMT
Definition zend_long.h:87
#define ZEND_LONG_MAX
Definition zend_long.h:45
struct _zend_string zend_string
ZEND_API zend_function * zend_std_get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key)
ZEND_API HashTable * zend_std_get_properties(zend_object *zobj)
ZEND_API const zend_object_handlers std_object_handlers
ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce)
ZEND_API void zend_object_std_dtor(zend_object *object)
ZEND_API bool ZEND_FASTCALL zend_is_true(const zval *op)
#define convert_to_string(op)
#define ZEND_FALLTHROUGH
#define XtOffsetOf(s_type, field)
#define ZEND_ASSERT(c)
#define UNEXPECTED(condition)
struct _zend_class_entry zend_class_entry
struct _zend_object zend_object
ZEND_API zend_string * zend_string_concat3(const char *str1, size_t str1_len, const char *str2, size_t str2_len, const char *str3, size_t str3_len)
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_INIT_LITERAL(s, persistent)
#define ZSTR_KNOWN(idx)
#define ZSTR_EMPTY_ALLOC()
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define IS_TRUE
Definition zend_types.h:603
#define ZVAL_STR(z, s)
#define Z_ISREF_P(zval_p)
Definition zend_types.h:954
#define Z_TRY_ADDREF_P(pz)
#define ZVAL_UNDEF(z)
#define IS_UNDEF
Definition zend_types.h:600
#define Z_ISUNDEF_P(zval_p)
Definition zend_types.h:957
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define ZVAL_NULL(z)
#define ZVAL_DEREF(z)
#define ZVAL_LONG(z, l)
#define ZVAL_STR_COPY(z, s)
struct _zend_array HashTable
Definition zend_types.h:386
#define Z_OBJ_P(zval_p)
Definition zend_types.h:990
#define IS_ARRAY
Definition zend_types.h:607
#define Z_ISUNDEF(zval)
Definition zend_types.h:956
#define Z_ADDREF_P(pz)
#define Z_OBJCE_P(zval_p)
#define ZVAL_OBJ(z, o)
@ FAILURE
Definition zend_types.h:61
#define IS_OBJECT
Definition zend_types.h:608
#define ZVAL_COPY(z, v)
#define ZVAL_OBJ_COPY(z, o)
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
struct _zend_object_handlers zend_object_handlers
Definition zend_types.h:88
#define Z_TYPE(zval)
Definition zend_types.h:659
#define Z_OBJ_HT(zval)
Definition zend_types.h:992
#define Z_ARRVAL(zval)
Definition zend_types.h:986
#define ZVAL_COPY_VALUE(z, v)
#define Z_LVAL(zval)
Definition zend_types.h:965
#define Z_OBJCE(zval)
#define Z_OBJ(zval)
Definition zend_types.h:989
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval retval
zval * return_value
zend_string * name
bool result
object
zend_refcounted * garbage
value
zend_object * zobj