Skip to content

Commit b2fe217

Browse files
committed
Introduce InternalIterator
1 parent 674210a commit b2fe217

30 files changed

+453
-30
lines changed

Zend/zend_interfaces.c

Lines changed: 178 additions & 8 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,20 +290,16 @@ 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' */
291-
uint32_t i;
292-
293-
if (class_type->get_iterator || (class_type->parent && class_type->parent->get_iterator)) {
294-
return SUCCESS;
295-
}
296293
/* Abstract class can implement Traversable only, in which case the extending class must
297294
* implement Iterator or IteratorAggregate. */
298295
if (class_type->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) {
299296
return SUCCESS;
300297
}
298+
299+
/* Check that class_type implements at least one of 'IteratorAggregate' or 'Iterator' */
301300
if (class_type->num_interfaces) {
302301
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
303-
for (i = 0; i < class_type->num_interfaces; i++) {
302+
for (uint32_t i = 0; i < class_type->num_interfaces; i++) {
304303
if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
305304
return SUCCESS;
306305
}
@@ -484,9 +483,169 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e
484483
}
485484
/* }}}*/
486485

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

492651
REGISTER_MAGIC_INTERFACE(aggregate, IteratorAggregate);
@@ -497,7 +656,6 @@ ZEND_API void zend_register_interfaces(void)
497656

498657
REGISTER_MAGIC_INTERFACE(serializable, Serializable);
499658

500-
zend_class_entry ce;
501659
INIT_CLASS_ENTRY(ce, "ArrayAccess", class_ArrayAccess_methods);
502660
zend_ce_arrayaccess = zend_register_internal_interface(&ce);
503661

@@ -506,5 +664,17 @@ ZEND_API void zend_register_interfaces(void)
506664

507665
INIT_CLASS_ENTRY(ce, "Stringable", class_Stringable_methods);
508666
zend_ce_stringable = zend_register_internal_interface(&ce);
667+
668+
INIT_CLASS_ENTRY(ce, "InternalIterator", class_InternalIterator_methods);
669+
zend_ce_internal_iterator = zend_register_internal_class(&ce);
670+
zend_class_implements(zend_ce_internal_iterator, 1, zend_ce_iterator);
671+
zend_ce_internal_iterator->ce_flags |= ZEND_ACC_FINAL;
672+
zend_ce_internal_iterator->create_object = zend_internal_iterator_create;
673+
zend_ce_internal_iterator->serialize = zend_class_serialize_deny;
674+
zend_ce_internal_iterator->unserialize = zend_class_unserialize_deny;
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
};

0 commit comments

Comments
 (0)