Skip to content

Commit 79882b9

Browse files
committed
Introduce InternalIterator
1 parent 9e34c6d commit 79882b9

36 files changed

+445
-26
lines changed

Zend/zend_interfaces.c

Lines changed: 184 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ ZEND_API zend_class_entry *zend_ce_arrayaccess;
2929
ZEND_API zend_class_entry *zend_ce_serializable;
3030
ZEND_API zend_class_entry *zend_ce_countable;
3131
ZEND_API zend_class_entry *zend_ce_stringable;
32+
ZEND_API zend_class_entry *zend_ce_internal_iterator;
33+
34+
static zend_object_handlers zend_internal_iterator_handlers;
3235

3336
/* {{{ zend_call_method
3437
Only returns the returned zval if retval_ptr != NULL */
@@ -287,12 +290,9 @@ ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *c
287290
/* {{{ zend_implement_traversable */
288291
static int zend_implement_traversable(zend_class_entry *interface, zend_class_entry *class_type)
289292
{
290-
/* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
293+
/* Check that class_type implements at least one of 'IteratorAggregate' or 'Iterator' */
291294
uint32_t i;
292295

293-
if (class_type->get_iterator || (class_type->parent && class_type->parent->get_iterator)) {
294-
return SUCCESS;
295-
}
296296
if (class_type->num_interfaces) {
297297
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
298298
for (i = 0; i < class_type->num_interfaces; i++) {
@@ -479,6 +479,164 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e
479479
}
480480
/* }}}*/
481481

482+
typedef struct {
483+
zend_object std;
484+
zend_object_iterator *iter;
485+
zend_bool rewind_called;
486+
} zend_internal_iterator;
487+
488+
static zend_object *zend_internal_iterator_create(zend_class_entry *ce) {
489+
zend_internal_iterator *intern = emalloc(sizeof(zend_internal_iterator));
490+
zend_object_std_init(&intern->std, ce);
491+
intern->std.handlers = &zend_internal_iterator_handlers;
492+
intern->iter = NULL;
493+
intern->rewind_called = 0;
494+
return &intern->std;
495+
}
496+
497+
ZEND_API int zend_create_internal_iterator_zval(zval *return_value, zval *obj) {
498+
zend_class_entry *scope = EG(current_execute_data)->func->common.scope;
499+
ZEND_ASSERT(scope->get_iterator != zend_user_it_get_new_iterator);
500+
zend_object_iterator *iter = scope->get_iterator(Z_OBJCE_P(obj), obj, /* by_ref */ 0);
501+
if (!iter) {
502+
return FAILURE;
503+
}
504+
505+
zend_internal_iterator *intern =
506+
(zend_internal_iterator *) zend_internal_iterator_create(zend_ce_internal_iterator);
507+
intern->iter = iter;
508+
ZVAL_OBJ(return_value, &intern->std);
509+
return SUCCESS;
510+
}
511+
512+
static void zend_internal_iterator_free(zend_object *obj) {
513+
zend_internal_iterator *intern = (zend_internal_iterator *) obj;
514+
if (intern->iter) {
515+
zend_iterator_dtor(intern->iter);
516+
}
517+
zend_object_std_dtor(&intern->std);
518+
}
519+
520+
static zend_internal_iterator *zend_internal_iterator_fetch(zval *This) {
521+
zend_internal_iterator *intern = (zend_internal_iterator *) Z_OBJ_P(This);
522+
if (!intern->iter) {
523+
zend_throw_error(NULL, "The InternalIterator object has not been properly initialized");
524+
return NULL;
525+
}
526+
return intern;
527+
}
528+
529+
/* Many iterators will now behave correctly if rewind() is not called, make sure it happens. */
530+
static int zend_internal_iterator_ensure_rewound(zend_internal_iterator *intern) {
531+
if (!intern->rewind_called) {
532+
zend_object_iterator *iter = intern->iter;
533+
intern->rewind_called = 1;
534+
if (iter->funcs->rewind) {
535+
iter->funcs->rewind(iter);
536+
if (UNEXPECTED(EG(exception))) {
537+
return FAILURE;
538+
}
539+
}
540+
}
541+
return SUCCESS;
542+
}
543+
544+
545+
ZEND_METHOD(InternalIterator, __construct) {
546+
zend_throw_error(NULL, "Cannot manually construct InternalIterator");
547+
}
548+
549+
ZEND_METHOD(InternalIterator, current) {
550+
ZEND_PARSE_PARAMETERS_NONE();
551+
552+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
553+
if (!intern) {
554+
RETURN_THROWS();
555+
}
556+
557+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
558+
RETURN_THROWS();
559+
}
560+
561+
zval *data = intern->iter->funcs->get_current_data(intern->iter);
562+
if (data) {
563+
ZVAL_COPY_DEREF(return_value, data);
564+
}
565+
}
566+
567+
ZEND_METHOD(InternalIterator, key) {
568+
ZEND_PARSE_PARAMETERS_NONE();
569+
570+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
571+
if (!intern) {
572+
RETURN_THROWS();
573+
}
574+
575+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
576+
RETURN_THROWS();
577+
}
578+
579+
if (intern->iter->funcs->get_current_key) {
580+
intern->iter->funcs->get_current_key(intern->iter, return_value);
581+
} else {
582+
RETURN_LONG(intern->iter->index);
583+
}
584+
}
585+
586+
ZEND_METHOD(InternalIterator, next) {
587+
ZEND_PARSE_PARAMETERS_NONE();
588+
589+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
590+
if (!intern) {
591+
RETURN_THROWS();
592+
}
593+
594+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
595+
RETURN_THROWS();
596+
}
597+
598+
intern->iter->funcs->move_forward(intern->iter);
599+
intern->iter->index++;
600+
}
601+
602+
ZEND_METHOD(InternalIterator, valid) {
603+
ZEND_PARSE_PARAMETERS_NONE();
604+
605+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
606+
if (!intern) {
607+
RETURN_THROWS();
608+
}
609+
610+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
611+
RETURN_THROWS();
612+
}
613+
614+
RETURN_BOOL(intern->iter->funcs->valid(intern->iter) == SUCCESS);
615+
}
616+
617+
ZEND_METHOD(InternalIterator, rewind) {
618+
ZEND_PARSE_PARAMETERS_NONE();
619+
620+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
621+
if (!intern) {
622+
RETURN_THROWS();
623+
}
624+
625+
if (!intern->iter->funcs->rewind) {
626+
/* Allow calling rewind() if no iteration has happened yet,
627+
* even if the iterator does not support rewinding. */
628+
if (intern->iter->index != 0) {
629+
zend_throw_error(NULL, "Iterator does not support rewinding");
630+
RETURN_THROWS();
631+
}
632+
intern->iter->index = 0;
633+
return;
634+
}
635+
636+
intern->iter->funcs->rewind(intern->iter);
637+
intern->iter->index = 0;
638+
}
639+
482640
/* {{{ function tables */
483641
static const zend_function_entry zend_funcs_aggregate[] = {
484642
ZEND_ABSTRACT_ME(iterator, getIterator, arginfo_class_IteratorAggregate_getIterator)
@@ -519,11 +677,23 @@ static const zend_function_entry zend_funcs_stringable[] = {
519677
ZEND_ABSTRACT_ME(Stringable, __toString, arginfo_class_Stringable___toString)
520678
ZEND_FE_END
521679
};
680+
681+
static const zend_function_entry zend_funcs_internal_iterator[] = {
682+
ZEND_ME(InternalIterator, __construct, arginfo_class_InternalIterator___construct, ZEND_ACC_PRIVATE)
683+
ZEND_ME(InternalIterator, current, arginfo_class_InternalIterator_current, ZEND_ACC_PUBLIC)
684+
ZEND_ME(InternalIterator, next, arginfo_class_InternalIterator_next, ZEND_ACC_PUBLIC)
685+
ZEND_ME(InternalIterator, key, arginfo_class_InternalIterator_key, ZEND_ACC_PUBLIC)
686+
ZEND_ME(InternalIterator, valid, arginfo_class_InternalIterator_valid, ZEND_ACC_PUBLIC)
687+
ZEND_ME(InternalIterator, rewind, arginfo_class_InternalIterator_rewind, ZEND_ACC_PUBLIC)
688+
ZEND_FE_END
689+
};
522690
/* }}} */
523691

524692
/* {{{ zend_register_interfaces */
525693
ZEND_API void zend_register_interfaces(void)
526694
{
695+
zend_class_entry ce;
696+
527697
REGISTER_MAGIC_INTERFACE(traversable, Traversable);
528698

529699
REGISTER_MAGIC_INTERFACE(aggregate, IteratorAggregate);
@@ -534,7 +704,6 @@ ZEND_API void zend_register_interfaces(void)
534704

535705
REGISTER_MAGIC_INTERFACE(serializable, Serializable);
536706

537-
zend_class_entry ce;
538707
INIT_CLASS_ENTRY(ce, "ArrayAccess", zend_funcs_arrayaccess);
539708
zend_ce_arrayaccess = zend_register_internal_interface(&ce);
540709

@@ -543,5 +712,15 @@ ZEND_API void zend_register_interfaces(void)
543712

544713
INIT_CLASS_ENTRY(ce, "Stringable", zend_funcs_stringable);
545714
zend_ce_stringable = zend_register_internal_interface(&ce);
715+
716+
INIT_CLASS_ENTRY(ce, "InternalIterator", zend_funcs_internal_iterator);
717+
zend_ce_internal_iterator = zend_register_internal_class(&ce);
718+
zend_class_implements(zend_ce_internal_iterator, 1, zend_ce_iterator);
719+
zend_ce_internal_iterator->ce_flags |= ZEND_ACC_FINAL;
720+
zend_ce_internal_iterator->create_object = zend_internal_iterator_create;
721+
722+
memcpy(&zend_internal_iterator_handlers, zend_get_std_object_handlers(),
723+
sizeof(zend_object_handlers));
724+
zend_internal_iterator_handlers.free_obj = zend_internal_iterator_free;
546725
}
547726
/* }}} */

Zend/zend_interfaces.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ ZEND_API int zend_user_unserialize(zval *object, zend_class_entry *ce, const uns
7878
ZEND_API int zend_class_serialize_deny(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data);
7979
ZEND_API int zend_class_unserialize_deny(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data);
8080

81+
ZEND_API int zend_create_internal_iterator_zval(zval *return_value, zval *obj);
82+
8183
END_EXTERN_C()
8284

8385
#endif /* ZEND_INTERFACES_H */

Zend/zend_interfaces.stub.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,20 @@ interface Stringable
5454
{
5555
function __toString(): string;
5656
}
57+
58+
final class InternalIterator implements Iterator
59+
{
60+
private function __construct();
61+
62+
/** @return mixed */
63+
public function current();
64+
65+
/** @return mixed */
66+
public function key();
67+
68+
public function next(): void;
69+
70+
public function valid(): bool;
71+
72+
public function rewind(): void;
73+
}

Zend/zend_interfaces_arginfo.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,17 @@ ZEND_END_ARG_INFO()
3636

3737
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Stringable___toString, 0, 0, IS_STRING, 0)
3838
ZEND_END_ARG_INFO()
39+
40+
#define arginfo_class_InternalIterator___construct arginfo_class_IteratorAggregate_getIterator
41+
42+
#define arginfo_class_InternalIterator_current arginfo_class_IteratorAggregate_getIterator
43+
44+
#define arginfo_class_InternalIterator_key arginfo_class_IteratorAggregate_getIterator
45+
46+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_InternalIterator_next, 0, 0, IS_VOID, 0)
47+
ZEND_END_ARG_INFO()
48+
49+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_InternalIterator_valid, 0, 0, _IS_BOOL, 0)
50+
ZEND_END_ARG_INFO()
51+
52+
#define arginfo_class_InternalIterator_rewind arginfo_class_InternalIterator_next

Zend/zend_weakrefs.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -620,12 +620,22 @@ ZEND_METHOD(WeakMap, count)
620620
RETURN_LONG(count);
621621
}
622622

623+
ZEND_METHOD(WeakMap, getIterator)
624+
{
625+
if (zend_parse_parameters_none() == FAILURE) {
626+
return;
627+
}
628+
629+
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
630+
}
631+
623632
static const zend_function_entry zend_weakmap_methods[] = {
624633
ZEND_ME(WeakMap, offsetGet, arginfo_class_WeakMap_offsetGet, ZEND_ACC_PUBLIC)
625634
ZEND_ME(WeakMap, offsetSet, arginfo_class_WeakMap_offsetSet, ZEND_ACC_PUBLIC)
626635
ZEND_ME(WeakMap, offsetExists, arginfo_class_WeakMap_offsetExists, ZEND_ACC_PUBLIC)
627636
ZEND_ME(WeakMap, offsetUnset, arginfo_class_WeakMap_offsetUnset, ZEND_ACC_PUBLIC)
628637
ZEND_ME(WeakMap, count, arginfo_class_WeakMap_count, ZEND_ACC_PUBLIC)
638+
ZEND_ME(WeakMap, getIterator, arginfo_class_WeakMap_getIterator, ZEND_ACC_PUBLIC)
629639
ZEND_FE_END
630640
};
631641

@@ -654,17 +664,16 @@ void zend_register_weakref_ce(void) /* {{{ */
654664

655665
INIT_CLASS_ENTRY(ce, "WeakMap", zend_weakmap_methods);
656666
zend_ce_weakmap = zend_register_internal_class(&ce);
667+
zend_class_implements(
668+
zend_ce_weakmap, 3, zend_ce_arrayaccess, zend_ce_countable, zend_ce_aggregate);
669+
657670
zend_ce_weakmap->ce_flags |= ZEND_ACC_FINAL;
658671

659672
zend_ce_weakmap->create_object = zend_weakmap_create_object;
660673
zend_ce_weakmap->get_iterator = zend_weakmap_get_iterator;
661674
zend_ce_weakmap->serialize = zend_class_serialize_deny;
662675
zend_ce_weakmap->unserialize = zend_class_unserialize_deny;
663676

664-
/* Must happen after get_iterator is assigned. */
665-
zend_class_implements(
666-
zend_ce_weakmap, 3, zend_ce_arrayaccess, zend_ce_countable, zend_ce_traversable);
667-
668677
memcpy(&zend_weakmap_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
669678
zend_weakmap_handlers.offset = XtOffsetOf(zend_weakmap, std);
670679
zend_weakmap_handlers.free_obj = zend_weakmap_free_obj;

Zend/zend_weakrefs.stub.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public static function create(): WeakReference;
88
public function get(): ?object;
99
}
1010

11-
final class WeakMap implements ArrayAccess, Countable, Traversable {
11+
final class WeakMap implements ArrayAccess, Countable, IteratorAggregate {
1212
//public function __construct();
1313

1414
/**
@@ -30,4 +30,6 @@ public function offsetExists($object): bool;
3030
public function offsetUnset($object): void;
3131

3232
public function count(): int;
33+
34+
public function getIterator(): Iterator;
3335
}

Zend/zend_weakrefs_arginfo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ ZEND_END_ARG_INFO()
2828

2929
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_WeakMap_count, 0, 0, IS_LONG, 0)
3030
ZEND_END_ARG_INFO()
31+
32+
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_WeakMap_getIterator, 0, 0, Iterator, 0)
33+
ZEND_END_ARG_INFO()

ext/date/php_date.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ static const zend_function_entry date_funcs_period[] = {
226226
PHP_ME(DatePeriod, getEndDate, arginfo_class_DatePeriod_getEndDate, ZEND_ACC_PUBLIC)
227227
PHP_ME(DatePeriod, getDateInterval, arginfo_class_DatePeriod_getDateInterval, ZEND_ACC_PUBLIC)
228228
PHP_ME(DatePeriod, getRecurrences, arginfo_class_DatePeriod_getRecurrences, ZEND_ACC_PUBLIC)
229+
PHP_ME(DatePeriod, getIterator, arginfo_class_DatePeriod_getIterator, ZEND_ACC_PUBLIC)
229230
PHP_FE_END
230231
};
231232

@@ -1832,7 +1833,7 @@ static void date_register_classes(void) /* {{{ */
18321833
ce_period.create_object = date_object_new_period;
18331834
date_ce_period = zend_register_internal_class_ex(&ce_period, NULL);
18341835
date_ce_period->get_iterator = date_object_period_get_iterator;
1835-
zend_class_implements(date_ce_period, 1, zend_ce_traversable);
1836+
zend_class_implements(date_ce_period, 1, zend_ce_aggregate);
18361837
memcpy(&date_object_handlers_period, &std_object_handlers, sizeof(zend_object_handlers));
18371838
date_object_handlers_period.offset = XtOffsetOf(php_period_obj, std);
18381839
date_object_handlers_period.free_obj = date_object_free_storage_period;
@@ -4464,6 +4465,13 @@ PHP_METHOD(DatePeriod, getRecurrences)
44644465
}
44654466
/* }}} */
44664467

4468+
PHP_METHOD(DatePeriod, getIterator)
4469+
{
4470+
ZEND_PARSE_PARAMETERS_NONE();
4471+
4472+
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
4473+
}
4474+
44674475
static int check_id_allowed(char *id, zend_long what) /* {{{ */
44684476
{
44694477
if (what & PHP_DATE_TIMEZONE_GROUP_AFRICA && strncasecmp(id, "Africa/", 7) == 0) return 1;

ext/date/php_date.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ PHP_METHOD(DatePeriod, getStartDate);
108108
PHP_METHOD(DatePeriod, getEndDate);
109109
PHP_METHOD(DatePeriod, getDateInterval);
110110
PHP_METHOD(DatePeriod, getRecurrences);
111+
PHP_METHOD(DatePeriod, getIterator);
111112

112113
/* Options and Configuration */
113114
PHP_FUNCTION(date_default_timezone_set);

0 commit comments

Comments
 (0)