Skip to content

Commit 7f2f41f

Browse files
committed
Introduce InternalIterator
1 parent a96e770 commit 7f2f41f

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 */
@@ -279,20 +282,16 @@ ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *c
279282
/* {{{ zend_implement_traversable */
280283
static int zend_implement_traversable(zend_class_entry *interface, zend_class_entry *class_type)
281284
{
282-
/* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
283-
uint32_t i;
284-
285-
if (class_type->get_iterator || (class_type->parent && class_type->parent->get_iterator)) {
286-
return SUCCESS;
287-
}
288285
/* Abstract class can implement Traversable only, in which case the extending class must
289286
* implement Iterator or IteratorAggregate. */
290287
if (class_type->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) {
291288
return SUCCESS;
292289
}
290+
291+
/* Check that class_type implements at least one of 'IteratorAggregate' or 'Iterator' */
293292
if (class_type->num_interfaces) {
294293
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
295-
for (i = 0; i < class_type->num_interfaces; i++) {
294+
for (uint32_t i = 0; i < class_type->num_interfaces; i++) {
296295
if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
297296
return SUCCESS;
298297
}
@@ -476,9 +475,169 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e
476475
}
477476
/* }}}*/
478477

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

484643
REGISTER_MAGIC_INTERFACE(aggregate, IteratorAggregate);
@@ -489,7 +648,6 @@ ZEND_API void zend_register_interfaces(void)
489648

490649
REGISTER_MAGIC_INTERFACE(serializable, Serializable);
491650

492-
zend_class_entry ce;
493651
INIT_CLASS_ENTRY(ce, "ArrayAccess", class_ArrayAccess_methods);
494652
zend_ce_arrayaccess = zend_register_internal_interface(&ce);
495653

@@ -498,5 +656,17 @@ ZEND_API void zend_register_interfaces(void)
498656

499657
INIT_CLASS_ENTRY(ce, "Stringable", class_Stringable_methods);
500658
zend_ce_stringable = zend_register_internal_interface(&ce);
659+
660+
INIT_CLASS_ENTRY(ce, "InternalIterator", class_InternalIterator_methods);
661+
zend_ce_internal_iterator = zend_register_internal_class(&ce);
662+
zend_class_implements(zend_ce_internal_iterator, 1, zend_ce_iterator);
663+
zend_ce_internal_iterator->ce_flags |= ZEND_ACC_FINAL;
664+
zend_ce_internal_iterator->create_object = zend_internal_iterator_create;
665+
zend_ce_internal_iterator->serialize = zend_class_serialize_deny;
666+
zend_ce_internal_iterator->unserialize = zend_class_unserialize_deny;
667+
668+
memcpy(&zend_internal_iterator_handlers, zend_get_std_object_handlers(),
669+
sizeof(zend_object_handlers));
670+
zend_internal_iterator_handlers.free_obj = zend_internal_iterator_free;
501671
}
502672
/* }}} */

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)