Skip to content

Commit 57b9f12

Browse files
committed
Introduce InternalIterator
1 parent 674210a commit 57b9f12

30 files changed

+450
-27
lines changed

Zend/zend_interfaces.c

Lines changed: 175 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,17 +290,15 @@ 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
/* Abstract class can implement Traversable only, in which case the extending class must
297297
* implement Iterator or IteratorAggregate. */
298298
if (class_type->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) {
299299
return SUCCESS;
300300
}
301+
301302
if (class_type->num_interfaces) {
302303
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
303304
for (i = 0; i < class_type->num_interfaces; i++) {
@@ -484,9 +485,169 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e
484485
}
485486
/* }}}*/
486487

488+
typedef struct {
489+
zend_object std;
490+
zend_object_iterator *iter;
491+
zend_bool rewind_called;
492+
} zend_internal_iterator;
493+
494+
static zend_object *zend_internal_iterator_create(zend_class_entry *ce) {
495+
zend_internal_iterator *intern = emalloc(sizeof(zend_internal_iterator));
496+
zend_object_std_init(&intern->std, ce);
497+
intern->std.handlers = &zend_internal_iterator_handlers;
498+
intern->iter = NULL;
499+
intern->rewind_called = 0;
500+
return &intern->std;
501+
}
502+
503+
ZEND_API int zend_create_internal_iterator_zval(zval *return_value, zval *obj) {
504+
zend_class_entry *scope = EG(current_execute_data)->func->common.scope;
505+
ZEND_ASSERT(scope->get_iterator != zend_user_it_get_new_iterator);
506+
zend_object_iterator *iter = scope->get_iterator(Z_OBJCE_P(obj), obj, /* by_ref */ 0);
507+
if (!iter) {
508+
return FAILURE;
509+
}
510+
511+
zend_internal_iterator *intern =
512+
(zend_internal_iterator *) zend_internal_iterator_create(zend_ce_internal_iterator);
513+
intern->iter = iter;
514+
ZVAL_OBJ(return_value, &intern->std);
515+
return SUCCESS;
516+
}
517+
518+
static void zend_internal_iterator_free(zend_object *obj) {
519+
zend_internal_iterator *intern = (zend_internal_iterator *) obj;
520+
if (intern->iter) {
521+
zend_iterator_dtor(intern->iter);
522+
}
523+
zend_object_std_dtor(&intern->std);
524+
}
525+
526+
static zend_internal_iterator *zend_internal_iterator_fetch(zval *This) {
527+
zend_internal_iterator *intern = (zend_internal_iterator *) Z_OBJ_P(This);
528+
if (!intern->iter) {
529+
zend_throw_error(NULL, "The InternalIterator object has not been properly initialized");
530+
return NULL;
531+
}
532+
return intern;
533+
}
534+
535+
/* Many iterators will now behave correctly if rewind() is not called, make sure it happens. */
536+
static int zend_internal_iterator_ensure_rewound(zend_internal_iterator *intern) {
537+
if (!intern->rewind_called) {
538+
zend_object_iterator *iter = intern->iter;
539+
intern->rewind_called = 1;
540+
if (iter->funcs->rewind) {
541+
iter->funcs->rewind(iter);
542+
if (UNEXPECTED(EG(exception))) {
543+
return FAILURE;
544+
}
545+
}
546+
}
547+
return SUCCESS;
548+
}
549+
550+
551+
ZEND_METHOD(InternalIterator, __construct) {
552+
zend_throw_error(NULL, "Cannot manually construct InternalIterator");
553+
}
554+
555+
ZEND_METHOD(InternalIterator, current) {
556+
ZEND_PARSE_PARAMETERS_NONE();
557+
558+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
559+
if (!intern) {
560+
RETURN_THROWS();
561+
}
562+
563+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
564+
RETURN_THROWS();
565+
}
566+
567+
zval *data = intern->iter->funcs->get_current_data(intern->iter);
568+
if (data) {
569+
ZVAL_COPY_DEREF(return_value, data);
570+
}
571+
}
572+
573+
ZEND_METHOD(InternalIterator, key) {
574+
ZEND_PARSE_PARAMETERS_NONE();
575+
576+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
577+
if (!intern) {
578+
RETURN_THROWS();
579+
}
580+
581+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
582+
RETURN_THROWS();
583+
}
584+
585+
if (intern->iter->funcs->get_current_key) {
586+
intern->iter->funcs->get_current_key(intern->iter, return_value);
587+
} else {
588+
RETURN_LONG(intern->iter->index);
589+
}
590+
}
591+
592+
ZEND_METHOD(InternalIterator, next) {
593+
ZEND_PARSE_PARAMETERS_NONE();
594+
595+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
596+
if (!intern) {
597+
RETURN_THROWS();
598+
}
599+
600+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
601+
RETURN_THROWS();
602+
}
603+
604+
intern->iter->funcs->move_forward(intern->iter);
605+
intern->iter->index++;
606+
}
607+
608+
ZEND_METHOD(InternalIterator, valid) {
609+
ZEND_PARSE_PARAMETERS_NONE();
610+
611+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
612+
if (!intern) {
613+
RETURN_THROWS();
614+
}
615+
616+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
617+
RETURN_THROWS();
618+
}
619+
620+
RETURN_BOOL(intern->iter->funcs->valid(intern->iter) == SUCCESS);
621+
}
622+
623+
ZEND_METHOD(InternalIterator, rewind) {
624+
ZEND_PARSE_PARAMETERS_NONE();
625+
626+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
627+
if (!intern) {
628+
RETURN_THROWS();
629+
}
630+
631+
if (!intern->iter->funcs->rewind) {
632+
/* Allow calling rewind() if no iteration has happened yet,
633+
* even if the iterator does not support rewinding. */
634+
if (intern->iter->index != 0) {
635+
zend_throw_error(NULL, "Iterator does not support rewinding");
636+
RETURN_THROWS();
637+
}
638+
intern->iter->index = 0;
639+
return;
640+
}
641+
642+
intern->iter->funcs->rewind(intern->iter);
643+
intern->iter->index = 0;
644+
}
645+
487646
/* {{{ zend_register_interfaces */
488647
ZEND_API void zend_register_interfaces(void)
489648
{
649+
zend_class_entry ce;
650+
490651
REGISTER_MAGIC_INTERFACE(traversable, Traversable);
491652

492653
REGISTER_MAGIC_INTERFACE(aggregate, IteratorAggregate);
@@ -497,7 +658,6 @@ ZEND_API void zend_register_interfaces(void)
497658

498659
REGISTER_MAGIC_INTERFACE(serializable, Serializable);
499660

500-
zend_class_entry ce;
501661
INIT_CLASS_ENTRY(ce, "ArrayAccess", class_ArrayAccess_methods);
502662
zend_ce_arrayaccess = zend_register_internal_interface(&ce);
503663

@@ -506,5 +666,15 @@ ZEND_API void zend_register_interfaces(void)
506666

507667
INIT_CLASS_ENTRY(ce, "Stringable", class_Stringable_methods);
508668
zend_ce_stringable = zend_register_internal_interface(&ce);
669+
670+
INIT_CLASS_ENTRY(ce, "InternalIterator", class_InternalIterator_methods);
671+
zend_ce_internal_iterator = zend_register_internal_class(&ce);
672+
zend_class_implements(zend_ce_internal_iterator, 1, zend_ce_iterator);
673+
zend_ce_internal_iterator->ce_flags |= ZEND_ACC_FINAL;
674+
zend_ce_internal_iterator->create_object = zend_internal_iterator_create;
675+
676+
memcpy(&zend_internal_iterator_handlers, zend_get_std_object_handlers(),
677+
sizeof(zend_object_handlers));
678+
zend_internal_iterator_handlers.free_obj = zend_internal_iterator_free;
509679
}
510680
/* }}} */

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
@@ -63,3 +63,20 @@ interface Stringable
6363
{
6464
public function __toString(): string;
6565
}
66+
67+
final class InternalIterator implements Iterator
68+
{
69+
private function __construct();
70+
71+
/** @return mixed */
72+
public function current();
73+
74+
/** @return mixed */
75+
public function key();
76+
77+
public function next(): void;
78+
79+
public function valid(): bool;
80+
81+
public function rewind(): void;
82+
}

Zend/zend_interfaces_arginfo.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,27 @@ ZEND_END_ARG_INFO()
3737
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Stringable___toString, 0, 0, IS_STRING, 0)
3838
ZEND_END_ARG_INFO()
3939

40+
#define arginfo_class_InternalIterator___construct arginfo_class_IteratorAggregate_getIterator
4041

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
53+
54+
55+
ZEND_METHOD(InternalIterator, __construct);
56+
ZEND_METHOD(InternalIterator, current);
57+
ZEND_METHOD(InternalIterator, key);
58+
ZEND_METHOD(InternalIterator, next);
59+
ZEND_METHOD(InternalIterator, valid);
60+
ZEND_METHOD(InternalIterator, rewind);
4161

4262

4363
static const zend_function_entry class_Traversable_methods[] = {
@@ -87,3 +107,14 @@ static const zend_function_entry class_Stringable_methods[] = {
87107
ZEND_ABSTRACT_ME_WITH_FLAGS(Stringable, __toString, arginfo_class_Stringable___toString, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT)
88108
ZEND_FE_END
89109
};
110+
111+
112+
static const zend_function_entry class_InternalIterator_methods[] = {
113+
ZEND_ME(InternalIterator, __construct, arginfo_class_InternalIterator___construct, ZEND_ACC_PRIVATE)
114+
ZEND_ME(InternalIterator, current, arginfo_class_InternalIterator_current, ZEND_ACC_PUBLIC)
115+
ZEND_ME(InternalIterator, key, arginfo_class_InternalIterator_key, ZEND_ACC_PUBLIC)
116+
ZEND_ME(InternalIterator, next, arginfo_class_InternalIterator_next, ZEND_ACC_PUBLIC)
117+
ZEND_ME(InternalIterator, valid, arginfo_class_InternalIterator_valid, ZEND_ACC_PUBLIC)
118+
ZEND_ME(InternalIterator, rewind, arginfo_class_InternalIterator_rewind, ZEND_ACC_PUBLIC)
119+
ZEND_FE_END
120+
};

Zend/zend_weakrefs.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,15 @@ ZEND_METHOD(WeakMap, count)
613613
RETURN_LONG(count);
614614
}
615615

616+
ZEND_METHOD(WeakMap, getIterator)
617+
{
618+
if (zend_parse_parameters_none() == FAILURE) {
619+
return;
620+
}
621+
622+
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
623+
}
624+
616625
void zend_register_weakref_ce(void) /* {{{ */
617626
{
618627
zend_class_entry ce;
@@ -638,17 +647,16 @@ void zend_register_weakref_ce(void) /* {{{ */
638647

639648
INIT_CLASS_ENTRY(ce, "WeakMap", class_WeakMap_methods);
640649
zend_ce_weakmap = zend_register_internal_class(&ce);
650+
zend_class_implements(
651+
zend_ce_weakmap, 3, zend_ce_arrayaccess, zend_ce_countable, zend_ce_aggregate);
652+
641653
zend_ce_weakmap->ce_flags |= ZEND_ACC_FINAL;
642654

643655
zend_ce_weakmap->create_object = zend_weakmap_create_object;
644656
zend_ce_weakmap->get_iterator = zend_weakmap_get_iterator;
645657
zend_ce_weakmap->serialize = zend_class_serialize_deny;
646658
zend_ce_weakmap->unserialize = zend_class_unserialize_deny;
647659

648-
/* Must happen after get_iterator is assigned. */
649-
zend_class_implements(
650-
zend_ce_weakmap, 3, zend_ce_arrayaccess, zend_ce_countable, zend_ce_traversable);
651-
652660
memcpy(&zend_weakmap_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
653661
zend_weakmap_handlers.offset = XtOffsetOf(zend_weakmap, std);
654662
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
@@ -11,7 +11,7 @@ public static function create(): WeakReference {}
1111
public function get(): ?object {}
1212
}
1313

14-
final class WeakMap implements ArrayAccess, Countable, Traversable
14+
final class WeakMap implements ArrayAccess, Countable, IteratorAggregate
1515
{
1616
/**
1717
* @param object $object
@@ -32,4 +32,6 @@ public function offsetExists($object): bool {}
3232
public function offsetUnset($object): void {}
3333

3434
public function count(): int {}
35+
36+
public function getIterator(): Iterator {}
3537
}

Zend/zend_weakrefs_arginfo.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ ZEND_END_ARG_INFO()
2929
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_WeakMap_count, 0, 0, IS_LONG, 0)
3030
ZEND_END_ARG_INFO()
3131

32+
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_WeakMap_getIterator, 0, 0, Iterator, 0)
33+
ZEND_END_ARG_INFO()
34+
3235

3336
ZEND_METHOD(WeakReference, __construct);
3437
ZEND_METHOD(WeakReference, create);
@@ -38,6 +41,7 @@ ZEND_METHOD(WeakMap, offsetSet);
3841
ZEND_METHOD(WeakMap, offsetExists);
3942
ZEND_METHOD(WeakMap, offsetUnset);
4043
ZEND_METHOD(WeakMap, count);
44+
ZEND_METHOD(WeakMap, getIterator);
4145

4246

4347
static const zend_function_entry class_WeakReference_methods[] = {
@@ -54,5 +58,6 @@ static const zend_function_entry class_WeakMap_methods[] = {
5458
ZEND_ME(WeakMap, offsetExists, arginfo_class_WeakMap_offsetExists, ZEND_ACC_PUBLIC)
5559
ZEND_ME(WeakMap, offsetUnset, arginfo_class_WeakMap_offsetUnset, ZEND_ACC_PUBLIC)
5660
ZEND_ME(WeakMap, count, arginfo_class_WeakMap_count, ZEND_ACC_PUBLIC)
61+
ZEND_ME(WeakMap, getIterator, arginfo_class_WeakMap_getIterator, ZEND_ACC_PUBLIC)
5762
ZEND_FE_END
5863
};

ext/date/php_date.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1692,7 +1692,7 @@ static void date_register_classes(void) /* {{{ */
16921692
ce_period.create_object = date_object_new_period;
16931693
date_ce_period = zend_register_internal_class_ex(&ce_period, NULL);
16941694
date_ce_period->get_iterator = date_object_period_get_iterator;
1695-
zend_class_implements(date_ce_period, 1, zend_ce_traversable);
1695+
zend_class_implements(date_ce_period, 1, zend_ce_aggregate);
16961696
memcpy(&date_object_handlers_period, &std_object_handlers, sizeof(zend_object_handlers));
16971697
date_object_handlers_period.offset = XtOffsetOf(php_period_obj, std);
16981698
date_object_handlers_period.free_obj = date_object_free_storage_period;
@@ -4372,6 +4372,13 @@ PHP_METHOD(DatePeriod, getRecurrences)
43724372
}
43734373
/* }}} */
43744374

4375+
PHP_METHOD(DatePeriod, getIterator)
4376+
{
4377+
ZEND_PARSE_PARAMETERS_NONE();
4378+
4379+
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
4380+
}
4381+
43754382
static int check_id_allowed(char *id, zend_long what) /* {{{ */
43764383
{
43774384
if (what & PHP_DATE_TIMEZONE_GROUP_AFRICA && strncasecmp(id, "Africa/", 7) == 0) return 1;

0 commit comments

Comments
 (0)