Skip to content

Commit 8e09b87

Browse files
committed
Introduce InternalIterator
1 parent b9390bf commit 8e09b87

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 */
@@ -246,20 +249,16 @@ ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *c
246249
/* {{{ zend_implement_traversable */
247250
static int zend_implement_traversable(zend_class_entry *interface, zend_class_entry *class_type)
248251
{
249-
/* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
250-
uint32_t i;
251-
252-
if (class_type->get_iterator || (class_type->parent && class_type->parent->get_iterator)) {
253-
return SUCCESS;
254-
}
255252
/* Abstract class can implement Traversable only, in which case the extending class must
256253
* implement Iterator or IteratorAggregate. */
257254
if (class_type->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) {
258255
return SUCCESS;
259256
}
257+
258+
/* Check that class_type implements at least one of 'IteratorAggregate' or 'Iterator' */
260259
if (class_type->num_interfaces) {
261260
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
262-
for (i = 0; i < class_type->num_interfaces; i++) {
261+
for (uint32_t i = 0; i < class_type->num_interfaces; i++) {
263262
if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
264263
return SUCCESS;
265264
}
@@ -441,9 +440,169 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e
441440
}
442441
/* }}}*/
443442

443+
typedef struct {
444+
zend_object std;
445+
zend_object_iterator *iter;
446+
zend_bool rewind_called;
447+
} zend_internal_iterator;
448+
449+
static zend_object *zend_internal_iterator_create(zend_class_entry *ce) {
450+
zend_internal_iterator *intern = emalloc(sizeof(zend_internal_iterator));
451+
zend_object_std_init(&intern->std, ce);
452+
intern->std.handlers = &zend_internal_iterator_handlers;
453+
intern->iter = NULL;
454+
intern->rewind_called = 0;
455+
return &intern->std;
456+
}
457+
458+
ZEND_API int zend_create_internal_iterator_zval(zval *return_value, zval *obj) {
459+
zend_class_entry *scope = EG(current_execute_data)->func->common.scope;
460+
ZEND_ASSERT(scope->get_iterator != zend_user_it_get_new_iterator);
461+
zend_object_iterator *iter = scope->get_iterator(Z_OBJCE_P(obj), obj, /* by_ref */ 0);
462+
if (!iter) {
463+
return FAILURE;
464+
}
465+
466+
zend_internal_iterator *intern =
467+
(zend_internal_iterator *) zend_internal_iterator_create(zend_ce_internal_iterator);
468+
intern->iter = iter;
469+
ZVAL_OBJ(return_value, &intern->std);
470+
return SUCCESS;
471+
}
472+
473+
static void zend_internal_iterator_free(zend_object *obj) {
474+
zend_internal_iterator *intern = (zend_internal_iterator *) obj;
475+
if (intern->iter) {
476+
zend_iterator_dtor(intern->iter);
477+
}
478+
zend_object_std_dtor(&intern->std);
479+
}
480+
481+
static zend_internal_iterator *zend_internal_iterator_fetch(zval *This) {
482+
zend_internal_iterator *intern = (zend_internal_iterator *) Z_OBJ_P(This);
483+
if (!intern->iter) {
484+
zend_throw_error(NULL, "The InternalIterator object has not been properly initialized");
485+
return NULL;
486+
}
487+
return intern;
488+
}
489+
490+
/* Many iterators will not behave correctly if rewind() is not called, make sure it happens. */
491+
static int zend_internal_iterator_ensure_rewound(zend_internal_iterator *intern) {
492+
if (!intern->rewind_called) {
493+
zend_object_iterator *iter = intern->iter;
494+
intern->rewind_called = 1;
495+
if (iter->funcs->rewind) {
496+
iter->funcs->rewind(iter);
497+
if (UNEXPECTED(EG(exception))) {
498+
return FAILURE;
499+
}
500+
}
501+
}
502+
return SUCCESS;
503+
}
504+
505+
506+
ZEND_METHOD(InternalIterator, __construct) {
507+
zend_throw_error(NULL, "Cannot manually construct InternalIterator");
508+
}
509+
510+
ZEND_METHOD(InternalIterator, current) {
511+
ZEND_PARSE_PARAMETERS_NONE();
512+
513+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
514+
if (!intern) {
515+
RETURN_THROWS();
516+
}
517+
518+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
519+
RETURN_THROWS();
520+
}
521+
522+
zval *data = intern->iter->funcs->get_current_data(intern->iter);
523+
if (data) {
524+
ZVAL_COPY_DEREF(return_value, data);
525+
}
526+
}
527+
528+
ZEND_METHOD(InternalIterator, key) {
529+
ZEND_PARSE_PARAMETERS_NONE();
530+
531+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
532+
if (!intern) {
533+
RETURN_THROWS();
534+
}
535+
536+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
537+
RETURN_THROWS();
538+
}
539+
540+
if (intern->iter->funcs->get_current_key) {
541+
intern->iter->funcs->get_current_key(intern->iter, return_value);
542+
} else {
543+
RETURN_LONG(intern->iter->index);
544+
}
545+
}
546+
547+
ZEND_METHOD(InternalIterator, next) {
548+
ZEND_PARSE_PARAMETERS_NONE();
549+
550+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
551+
if (!intern) {
552+
RETURN_THROWS();
553+
}
554+
555+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
556+
RETURN_THROWS();
557+
}
558+
559+
intern->iter->funcs->move_forward(intern->iter);
560+
intern->iter->index++;
561+
}
562+
563+
ZEND_METHOD(InternalIterator, valid) {
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+
RETURN_BOOL(intern->iter->funcs->valid(intern->iter) == SUCCESS);
576+
}
577+
578+
ZEND_METHOD(InternalIterator, rewind) {
579+
ZEND_PARSE_PARAMETERS_NONE();
580+
581+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
582+
if (!intern) {
583+
RETURN_THROWS();
584+
}
585+
586+
if (!intern->iter->funcs->rewind) {
587+
/* Allow calling rewind() if no iteration has happened yet,
588+
* even if the iterator does not support rewinding. */
589+
if (intern->iter->index != 0) {
590+
zend_throw_error(NULL, "Iterator does not support rewinding");
591+
RETURN_THROWS();
592+
}
593+
intern->iter->index = 0;
594+
return;
595+
}
596+
597+
intern->iter->funcs->rewind(intern->iter);
598+
intern->iter->index = 0;
599+
}
600+
444601
/* {{{ zend_register_interfaces */
445602
ZEND_API void zend_register_interfaces(void)
446603
{
604+
zend_class_entry ce;
605+
447606
REGISTER_MAGIC_INTERFACE(traversable, Traversable);
448607

449608
REGISTER_MAGIC_INTERFACE(aggregate, IteratorAggregate);
@@ -454,7 +613,6 @@ ZEND_API void zend_register_interfaces(void)
454613

455614
REGISTER_MAGIC_INTERFACE(serializable, Serializable);
456615

457-
zend_class_entry ce;
458616
INIT_CLASS_ENTRY(ce, "ArrayAccess", class_ArrayAccess_methods);
459617
zend_ce_arrayaccess = zend_register_internal_interface(&ce);
460618

@@ -463,5 +621,17 @@ ZEND_API void zend_register_interfaces(void)
463621

464622
INIT_CLASS_ENTRY(ce, "Stringable", class_Stringable_methods);
465623
zend_ce_stringable = zend_register_internal_interface(&ce);
624+
625+
INIT_CLASS_ENTRY(ce, "InternalIterator", class_InternalIterator_methods);
626+
zend_ce_internal_iterator = zend_register_internal_class(&ce);
627+
zend_class_implements(zend_ce_internal_iterator, 1, zend_ce_iterator);
628+
zend_ce_internal_iterator->ce_flags |= ZEND_ACC_FINAL;
629+
zend_ce_internal_iterator->create_object = zend_internal_iterator_create;
630+
zend_ce_internal_iterator->serialize = zend_class_serialize_deny;
631+
zend_ce_internal_iterator->unserialize = zend_class_unserialize_deny;
632+
633+
memcpy(&zend_internal_iterator_handlers, zend_get_std_object_handlers(),
634+
sizeof(zend_object_handlers));
635+
zend_internal_iterator_handlers.free_obj = zend_internal_iterator_free;
466636
}
467637
/* }}} */

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
@@ -609,6 +609,15 @@ ZEND_METHOD(WeakMap, count)
609609
RETURN_LONG(count);
610610
}
611611

612+
ZEND_METHOD(WeakMap, getIterator)
613+
{
614+
if (zend_parse_parameters_none() == FAILURE) {
615+
return;
616+
}
617+
618+
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
619+
}
620+
612621
void zend_register_weakref_ce(void) /* {{{ */
613622
{
614623
zend_class_entry ce;
@@ -634,17 +643,16 @@ void zend_register_weakref_ce(void) /* {{{ */
634643

635644
INIT_CLASS_ENTRY(ce, "WeakMap", class_WeakMap_methods);
636645
zend_ce_weakmap = zend_register_internal_class(&ce);
646+
zend_class_implements(
647+
zend_ce_weakmap, 3, zend_ce_arrayaccess, zend_ce_countable, zend_ce_aggregate);
648+
637649
zend_ce_weakmap->ce_flags |= ZEND_ACC_FINAL;
638650

639651
zend_ce_weakmap->create_object = zend_weakmap_create_object;
640652
zend_ce_weakmap->get_iterator = zend_weakmap_get_iterator;
641653
zend_ce_weakmap->serialize = zend_class_serialize_deny;
642654
zend_ce_weakmap->unserialize = zend_class_unserialize_deny;
643655

644-
/* Must happen after get_iterator is assigned. */
645-
zend_class_implements(
646-
zend_ce_weakmap, 3, zend_ce_arrayaccess, zend_ce_countable, zend_ce_traversable);
647-
648656
memcpy(&zend_weakmap_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
649657
zend_weakmap_handlers.offset = XtOffsetOf(zend_weakmap, std);
650658
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)