Skip to content

Commit 4ff5616

Browse files
committed
Introduce InternalIterator
1 parent 819a872 commit 4ff5616

39 files changed

+493
-102
lines changed

Zend/tests/bug48667_1.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ abstract class A implements Iterator, IteratorAggregate { }
77

88
?>
99
--EXPECTF--
10-
Fatal error: Class A cannot implement both IteratorAggregate and Iterator at the same time in %s on line %d
10+
Fatal error: Class A cannot implement both Iterator and IteratorAggregate at the same time in %s on line %d

Zend/zend_inheritance.c

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,6 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */
135135
ce->create_object = parent->create_object;
136136

137137
/* Inherit special functions if needed */
138-
if (EXPECTED(!ce->get_iterator)) {
139-
ce->get_iterator = parent->get_iterator;
140-
}
141-
if (parent->iterator_funcs_ptr) {
142-
/* Must be initialized through iface->interface_gets_implemented() */
143-
ZEND_ASSERT(ce->iterator_funcs_ptr);
144-
}
145138
if (EXPECTED(!ce->__get)) {
146139
ce->__get = parent->__get;
147140
}
@@ -1182,19 +1175,6 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
11821175
ce->parent = parent_ce;
11831176
ce->ce_flags |= ZEND_ACC_RESOLVED_PARENT;
11841177

1185-
/* Inherit interfaces */
1186-
if (parent_ce->num_interfaces) {
1187-
if (!ce->num_interfaces) {
1188-
zend_do_inherit_interfaces(ce, parent_ce);
1189-
} else {
1190-
uint32_t i;
1191-
1192-
for (i = 0; i < parent_ce->num_interfaces; i++) {
1193-
do_implement_interface(ce, parent_ce->interfaces[i]);
1194-
}
1195-
}
1196-
}
1197-
11981178
/* Inherit properties */
11991179
if (parent_ce->default_properties_count) {
12001180
zval *src, *dst, *end;
@@ -1372,6 +1352,14 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
13721352
}
13731353
}
13741354

1355+
/* Inherit interfaces */
1356+
if (parent_ce->num_interfaces) {
1357+
/* If ce has interfaces itself, zend_do_implement_interfaces() will handle this later. */
1358+
if (!ce->num_interfaces) {
1359+
zend_do_inherit_interfaces(ce, parent_ce);
1360+
}
1361+
}
1362+
13751363
do_inherit_parent_constructor(ce);
13761364

13771365
if (ce->type == ZEND_INTERNAL_CLASS) {
@@ -1530,8 +1518,12 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry
15301518
ce->interfaces = interfaces;
15311519
ce->ce_flags |= ZEND_ACC_RESOLVED_INTERFACES;
15321520

1533-
i = num_parent_interfaces;
1534-
for (; i < ce->num_interfaces; i++) {
1521+
for (i = 0; i < num_parent_interfaces; i++) {
1522+
do_implement_interface(ce, ce->interfaces[i]);
1523+
}
1524+
/* Note that new interfaces can be added during this loop due to interface inheritance.
1525+
* Use num_interfaces rather than ce->num_interfaces to not re-process the new ones. */
1526+
for (; i < num_interfaces; i++) {
15351527
do_interface_implementation(ce, ce->interfaces[i]);
15361528
}
15371529
}

Zend/zend_interfaces.c

Lines changed: 206 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ ZEND_API zend_class_entry *zend_ce_iterator;
2828
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;
31+
ZEND_API zend_class_entry *zend_ce_internal_iterator;
32+
33+
static zend_object_handlers zend_internal_iterator_handlers;
3134

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

292-
if (class_type->get_iterator || (class_type->parent && class_type->parent->get_iterator)) {
293-
return SUCCESS;
294-
}
295295
if (class_type->num_interfaces) {
296296
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
297297
for (i = 0; i < class_type->num_interfaces; i++) {
@@ -312,59 +312,38 @@ static int zend_implement_traversable(zend_class_entry *interface, zend_class_en
312312
/* {{{ zend_implement_aggregate */
313313
static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entry *class_type)
314314
{
315-
uint32_t i;
316-
int t = -1;
317-
zend_class_iterator_funcs *funcs_ptr;
315+
if (zend_class_implements_interface(class_type, zend_ce_iterator)) {
316+
zend_error_noreturn(E_ERROR,
317+
"Class %s cannot implement both Iterator and IteratorAggregate at the same time",
318+
ZSTR_VAL(class_type->name));
319+
}
318320

319321
if (class_type->get_iterator) {
320-
if (class_type->type == ZEND_INTERNAL_CLASS) {
321-
/* inheritance ensures the class has necessary userland methods */
322-
return SUCCESS;
323-
} else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
324-
/* c-level get_iterator cannot be changed (exception being only Traversable is implemented) */
325-
if (class_type->num_interfaces) {
326-
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
327-
for (i = 0; i < class_type->num_interfaces; i++) {
328-
if (class_type->interfaces[i] == zend_ce_iterator) {
329-
zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
330-
ZSTR_VAL(class_type->name),
331-
ZSTR_VAL(interface->name),
332-
ZSTR_VAL(zend_ce_iterator->name));
333-
return FAILURE;
334-
}
335-
if (class_type->interfaces[i] == zend_ce_traversable) {
336-
t = i;
337-
}
338-
}
339-
}
340-
if (t == -1) {
341-
return FAILURE;
342-
}
343-
}
322+
/* An internal class with a custom get_iterator implementation. */
323+
ZEND_ASSERT(class_type->type == ZEND_INTERNAL_CLASS);
324+
return SUCCESS;
344325
}
345-
if (class_type->parent
346-
&& (class_type->parent->ce_flags & ZEND_ACC_REUSE_GET_ITERATOR)) {
326+
327+
zend_function *zf = zend_hash_str_find_ptr(
328+
&class_type->function_table, "getiterator", sizeof("getiterator") - 1);
329+
if (class_type->parent && class_type->parent->get_iterator != zend_user_it_get_new_iterator
330+
&& zf->common.scope != class_type) {
331+
/* We have a custom get_iterator on the parent class, and the getIterator() method
332+
* has not been overridden in this class: Inherit the parent get_iterator.
333+
* Otherwise fall back to a normal call of the overridden getIterator(). */
347334
class_type->get_iterator = class_type->parent->get_iterator;
348-
class_type->ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR;
349-
} else {
350-
class_type->get_iterator = zend_user_it_get_new_iterator;
351-
}
352-
funcs_ptr = class_type->iterator_funcs_ptr;
353-
if (class_type->type == ZEND_INTERNAL_CLASS) {
354-
if (!funcs_ptr) {
355-
funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs));
356-
class_type->iterator_funcs_ptr = funcs_ptr;
357-
}
358-
funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(&class_type->function_table, "getiterator", sizeof("getiterator") - 1);
359-
} else {
360-
if (!funcs_ptr) {
361-
funcs_ptr = zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
362-
class_type->iterator_funcs_ptr = funcs_ptr;
363-
memset(funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
364-
} else {
365-
funcs_ptr->zf_new_iterator = NULL;
366-
}
335+
return SUCCESS;
367336
}
337+
338+
ZEND_ASSERT(!class_type->iterator_funcs_ptr && "Iterator funcs already set?");
339+
zend_class_iterator_funcs *funcs_ptr = class_type->type == ZEND_INTERNAL_CLASS
340+
? pemalloc(sizeof(zend_class_iterator_funcs), 1)
341+
: zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
342+
class_type->get_iterator = zend_user_it_get_new_iterator;
343+
class_type->iterator_funcs_ptr = funcs_ptr;
344+
memset(funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
345+
funcs_ptr->zf_new_iterator = zf;
346+
368347
return SUCCESS;
369348
}
370349
/* }}} */
@@ -381,12 +360,9 @@ static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry
381360
} else {
382361
/* c-level get_iterator cannot be changed */
383362
if (class_type->get_iterator == zend_user_it_get_new_iterator) {
384-
zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
385-
ZSTR_VAL(class_type->name),
386-
ZSTR_VAL(interface->name),
387-
ZSTR_VAL(zend_ce_aggregate->name));
363+
zend_error_noreturn(E_ERROR, "Class %s cannot implement both Iterator and IteratorAggregate at the same time",
364+
ZSTR_VAL(class_type->name));
388365
}
389-
return FAILURE;
390366
}
391367
}
392368
if (class_type->parent
@@ -532,6 +508,157 @@ static int zend_implement_countable(zend_class_entry *interface, zend_class_entr
532508
}
533509
/* }}}*/
534510

511+
typedef struct {
512+
zend_object std;
513+
zend_object_iterator *iter;
514+
zend_bool rewind_called;
515+
} zend_internal_iterator;
516+
517+
static zend_object *zend_internal_iterator_create(zend_class_entry *ce) {
518+
zend_internal_iterator *intern = emalloc(sizeof(zend_internal_iterator));
519+
zend_object_std_init(&intern->std, ce);
520+
intern->std.handlers = &zend_internal_iterator_handlers;
521+
intern->iter = NULL;
522+
intern->rewind_called = 0;
523+
return &intern->std;
524+
}
525+
526+
ZEND_API int zend_create_internal_iterator_zval(zval *return_value, zval *obj) {
527+
zend_class_entry *ce = Z_OBJCE_P(obj);
528+
ZEND_ASSERT(ce->get_iterator != zend_user_it_get_new_iterator);
529+
zend_object_iterator *iter = ce->get_iterator(ce, obj, /* by_ref */ 0);
530+
if (!iter) {
531+
return FAILURE;
532+
}
533+
534+
zend_internal_iterator *intern =
535+
(zend_internal_iterator *) zend_internal_iterator_create(zend_ce_internal_iterator);
536+
intern->iter = iter;
537+
ZVAL_OBJ(return_value, &intern->std);
538+
return SUCCESS;
539+
}
540+
541+
static void zend_internal_iterator_free(zend_object *obj) {
542+
zend_internal_iterator *intern = (zend_internal_iterator *) obj;
543+
if (intern->iter) {
544+
zend_iterator_dtor(intern->iter);
545+
}
546+
zend_object_std_dtor(&intern->std);
547+
}
548+
549+
static zend_internal_iterator *zend_internal_iterator_fetch(zval *This) {
550+
zend_internal_iterator *intern = (zend_internal_iterator *) Z_OBJ_P(This);
551+
if (!intern->iter) {
552+
zend_throw_error(NULL, "The InternalIterator object has not been properly initialized");
553+
return NULL;
554+
}
555+
return intern;
556+
}
557+
558+
/* Many iterators will now behave correctly if rewind() is not called, make sure it happens. */
559+
static int zend_internal_iterator_ensure_rewound(zend_internal_iterator *intern) {
560+
if (!intern->rewind_called) {
561+
zend_object_iterator *iter = intern->iter;
562+
intern->rewind_called = 1;
563+
if (iter->funcs->rewind) {
564+
iter->funcs->rewind(iter);
565+
if (UNEXPECTED(EG(exception))) {
566+
return FAILURE;
567+
}
568+
}
569+
}
570+
return SUCCESS;
571+
}
572+
573+
574+
ZEND_METHOD(InternalIterator, __construct) {
575+
zend_throw_error(NULL, "Cannot manually construct InternalIterator");
576+
}
577+
578+
ZEND_METHOD(InternalIterator, current) {
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 (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
587+
RETURN_THROWS();
588+
}
589+
590+
zval *data = intern->iter->funcs->get_current_data(intern->iter);
591+
if (data) {
592+
ZVAL_COPY_DEREF(return_value, data);
593+
}
594+
}
595+
596+
ZEND_METHOD(InternalIterator, key) {
597+
ZEND_PARSE_PARAMETERS_NONE();
598+
599+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
600+
if (!intern) {
601+
RETURN_THROWS();
602+
}
603+
604+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
605+
RETURN_THROWS();
606+
}
607+
608+
if (intern->iter->funcs->get_current_key) {
609+
intern->iter->funcs->get_current_key(intern->iter, return_value);
610+
} else {
611+
RETURN_LONG(intern->iter->index);
612+
}
613+
}
614+
615+
ZEND_METHOD(InternalIterator, next) {
616+
ZEND_PARSE_PARAMETERS_NONE();
617+
618+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
619+
if (!intern) {
620+
RETURN_THROWS();
621+
}
622+
623+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
624+
RETURN_THROWS();
625+
}
626+
627+
intern->iter->funcs->move_forward(intern->iter);
628+
intern->iter->index++;
629+
}
630+
631+
ZEND_METHOD(InternalIterator, valid) {
632+
ZEND_PARSE_PARAMETERS_NONE();
633+
634+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
635+
if (!intern) {
636+
RETURN_THROWS();
637+
}
638+
639+
if (zend_internal_iterator_ensure_rewound(intern) == FAILURE) {
640+
RETURN_THROWS();
641+
}
642+
643+
RETURN_BOOL(intern->iter->funcs->valid(intern->iter) == SUCCESS);
644+
}
645+
646+
ZEND_METHOD(InternalIterator, rewind) {
647+
ZEND_PARSE_PARAMETERS_NONE();
648+
649+
zend_internal_iterator *intern = zend_internal_iterator_fetch(ZEND_THIS);
650+
if (!intern) {
651+
RETURN_THROWS();
652+
}
653+
654+
if (!intern->iter->funcs->rewind) {
655+
zend_throw_error(NULL, "Iterator does not support rewinding");
656+
RETURN_THROWS();
657+
}
658+
659+
intern->iter->funcs->rewind(intern->iter);
660+
}
661+
535662
/* {{{ function tables */
536663
static const zend_function_entry zend_funcs_aggregate[] = {
537664
ZEND_ABSTRACT_ME(iterator, getIterator, arginfo_class_IteratorAggregate_getIterator)
@@ -567,11 +694,23 @@ static const zend_function_entry zend_funcs_countable[] = {
567694
ZEND_ABSTRACT_ME(Countable, count, arginfo_class_Countable_count)
568695
ZEND_FE_END
569696
};
697+
698+
static const zend_function_entry zend_funcs_internal_iterator[] = {
699+
ZEND_ME(InternalIterator, __construct, arginfo_class_InternalIterator___construct, ZEND_ACC_PRIVATE)
700+
ZEND_ME(InternalIterator, current, arginfo_class_InternalIterator_current, ZEND_ACC_PUBLIC)
701+
ZEND_ME(InternalIterator, next, arginfo_class_InternalIterator_next, ZEND_ACC_PUBLIC)
702+
ZEND_ME(InternalIterator, key, arginfo_class_InternalIterator_key, ZEND_ACC_PUBLIC)
703+
ZEND_ME(InternalIterator, valid, arginfo_class_InternalIterator_valid, ZEND_ACC_PUBLIC)
704+
ZEND_ME(InternalIterator, rewind, arginfo_class_InternalIterator_rewind, ZEND_ACC_PUBLIC)
705+
ZEND_FE_END
706+
};
570707
/* }}} */
571708

572709
/* {{{ zend_register_interfaces */
573710
ZEND_API void zend_register_interfaces(void)
574711
{
712+
zend_class_entry ce;
713+
575714
REGISTER_MAGIC_INTERFACE(traversable, Traversable);
576715

577716
REGISTER_MAGIC_INTERFACE(aggregate, IteratorAggregate);
@@ -585,5 +724,15 @@ ZEND_API void zend_register_interfaces(void)
585724
REGISTER_MAGIC_INTERFACE(serializable, Serializable);
586725

587726
REGISTER_MAGIC_INTERFACE(countable, Countable);
727+
728+
INIT_CLASS_ENTRY(ce, "InternalIterator", zend_funcs_internal_iterator);
729+
zend_ce_internal_iterator = zend_register_internal_class(&ce);
730+
zend_class_implements(zend_ce_internal_iterator, 1, zend_ce_iterator);
731+
zend_ce_internal_iterator->ce_flags |= ZEND_ACC_FINAL;
732+
zend_ce_internal_iterator->create_object = zend_internal_iterator_create;
733+
734+
memcpy(&zend_internal_iterator_handlers, zend_get_std_object_handlers(),
735+
sizeof(zend_object_handlers));
736+
zend_internal_iterator_handlers.free_obj = zend_internal_iterator_free;
588737
}
589738
/* }}} */

0 commit comments

Comments
 (0)