summaryrefslogtreecommitdiff
path: root/Zend/zend_interfaces.c
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-03-04 10:10:36 +0100
committerNikita Popov <nikita.ppv@gmail.com>2020-03-04 10:29:21 +0100
commitc05a9c3dcd587418906d9d25f83872e027f7dd43 (patch)
tree7c4ab9a194c44cf75c504634a8b576875ec3f9ab /Zend/zend_interfaces.c
parenta527c60ede98d51785091ceb380242b0ffeab5ad (diff)
downloadphp-git-c05a9c3dcd587418906d9d25f83872e027f7dd43.tar.gz
Implement interfaces after all methods available
The place where interface implementation handlers is called is currently ill-defined: If the class implements interfaces itself, the handlers for both the parent interfaces and the new interfaces will be called after all methods are registered (post trait use). If the class does not implement interfaces, then the parent interface handlers are called early during inheritance (before methods are inherited). This commit moves the calls to always occur after all methods are available. For userland classes this will be post trait import, at the time where interfaces get implemented (whether the class itself defines additional interfaces or not). For internal classes it will be at the end of inheritance, as internal class declarations do not have proper finalization. This allows us to simplify the logic for implementing the magic Iterator / IteratorAggregate interfaces. In particularly we can now also automatically detect whether an extension of IteratorAggregate can safely reuse a custom get_iterator handler, or whether it needs to switch to the userland mechanism. The Iterator case continues to rely on ZEND_ACC_REUSE_GET_ITERATOR for this purpose, as a wholesale replacement is not possible there.
Diffstat (limited to 'Zend/zend_interfaces.c')
-rw-r--r--Zend/zend_interfaces.c136
1 files changed, 48 insertions, 88 deletions
diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c
index fe403b3d7a..cf8b24d76a 100644
--- a/Zend/zend_interfaces.c
+++ b/Zend/zend_interfaces.c
@@ -313,59 +313,39 @@ static int zend_implement_traversable(zend_class_entry *interface, zend_class_en
/* {{{ zend_implement_aggregate */
static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entry *class_type)
{
- uint32_t i;
- int t = -1;
- zend_class_iterator_funcs *funcs_ptr;
+ if (zend_class_implements_interface(class_type, zend_ce_iterator)) {
+ zend_error_noreturn(E_ERROR,
+ "Class %s cannot implement both Iterator and IteratorAggregate at the same time",
+ ZSTR_VAL(class_type->name));
+ }
- if (class_type->get_iterator) {
- if (class_type->type == ZEND_INTERNAL_CLASS) {
- /* inheritance ensures the class has necessary userland methods */
+ zend_function *zf = zend_hash_str_find_ptr(
+ &class_type->function_table, "getiterator", sizeof("getiterator") - 1);
+ if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_new_iterator) {
+ /* get_iterator was explicitly assigned for an internal class. */
+ if (!class_type->parent || class_type->parent->get_iterator != class_type->get_iterator) {
+ ZEND_ASSERT(class_type->type == ZEND_INTERNAL_CLASS);
return SUCCESS;
- } else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
- /* c-level get_iterator cannot be changed (exception being only Traversable is implemented) */
- if (class_type->num_interfaces) {
- ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_RESOLVED_INTERFACES);
- for (i = 0; i < class_type->num_interfaces; i++) {
- if (class_type->interfaces[i] == zend_ce_iterator) {
- zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
- ZSTR_VAL(class_type->name),
- ZSTR_VAL(interface->name),
- ZSTR_VAL(zend_ce_iterator->name));
- return FAILURE;
- }
- if (class_type->interfaces[i] == zend_ce_traversable) {
- t = i;
- }
- }
- }
- if (t == -1) {
- return FAILURE;
- }
}
- }
- if (class_type->parent
- && (class_type->parent->ce_flags & ZEND_ACC_REUSE_GET_ITERATOR)) {
- class_type->get_iterator = class_type->parent->get_iterator;
- class_type->ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR;
- } else {
- class_type->get_iterator = zend_user_it_get_new_iterator;
- }
- funcs_ptr = class_type->iterator_funcs_ptr;
- if (class_type->type == ZEND_INTERNAL_CLASS) {
- if (!funcs_ptr) {
- funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs));
- class_type->iterator_funcs_ptr = funcs_ptr;
- }
- funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(&class_type->function_table, "getiterator", sizeof("getiterator") - 1);
- } else {
- if (!funcs_ptr) {
- funcs_ptr = zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
- class_type->iterator_funcs_ptr = funcs_ptr;
- memset(funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
- } else {
- funcs_ptr->zf_new_iterator = NULL;
+
+ /* The getIterator() method has not been overwritten, use inherited get_iterator(). */
+ if (zf->common.scope != class_type) {
+ return SUCCESS;
}
+
+ /* getIterator() has been overwritten, switch to zend_user_it_get_new_iterator. */
}
+
+ ZEND_ASSERT(!class_type->iterator_funcs_ptr && "Iterator funcs already set?");
+ zend_class_iterator_funcs *funcs_ptr = class_type->type == ZEND_INTERNAL_CLASS
+ ? pemalloc(sizeof(zend_class_iterator_funcs), 1)
+ : zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
+ class_type->get_iterator = zend_user_it_get_new_iterator;
+ class_type->iterator_funcs_ptr = funcs_ptr;
+
+ memset(funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
+ funcs_ptr->zf_new_iterator = zf;
+
return SUCCESS;
}
/* }}} */
@@ -373,55 +353,35 @@ static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entr
/* {{{ zend_implement_iterator */
static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry *class_type)
{
- zend_class_iterator_funcs *funcs_ptr;
+ if (zend_class_implements_interface(class_type, zend_ce_aggregate)) {
+ zend_error_noreturn(E_ERROR,
+ "Class %s cannot implement both Iterator and IteratorAggregate at the same time",
+ ZSTR_VAL(class_type->name));
+ }
if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_iterator) {
- if (class_type->type == ZEND_INTERNAL_CLASS) {
- /* inheritance ensures the class has the necessary userland methods */
+ if (!class_type->parent || class_type->parent->get_iterator != class_type->get_iterator) {
+ /* get_iterator was explicitly assigned for an internal class. */
+ ZEND_ASSERT(class_type->type == ZEND_INTERNAL_CLASS);
return SUCCESS;
- } else {
- /* c-level get_iterator cannot be changed */
- if (class_type->get_iterator == zend_user_it_get_new_iterator) {
- zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
- ZSTR_VAL(class_type->name),
- ZSTR_VAL(interface->name),
- ZSTR_VAL(zend_ce_aggregate->name));
- }
- return FAILURE;
}
+ /* Otherwise get_iterator was inherited from the parent by default. */
}
- if (class_type->parent
- && (class_type->parent->ce_flags & ZEND_ACC_REUSE_GET_ITERATOR)) {
- class_type->get_iterator = class_type->parent->get_iterator;
+
+ if (class_type->parent && (class_type->parent->ce_flags & ZEND_ACC_REUSE_GET_ITERATOR)) {
+ /* Keep the inherited get_iterator handler. */
class_type->ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR;
} else {
class_type->get_iterator = zend_user_it_get_iterator;
}
- funcs_ptr = class_type->iterator_funcs_ptr;
- if (class_type->type == ZEND_INTERNAL_CLASS) {
- if (!funcs_ptr) {
- funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs));
- class_type->iterator_funcs_ptr = funcs_ptr;
- } else {
- funcs_ptr->zf_rewind = zend_hash_str_find_ptr(&class_type->function_table, "rewind", sizeof("rewind") - 1);
- funcs_ptr->zf_valid = zend_hash_str_find_ptr(&class_type->function_table, "valid", sizeof("valid") - 1);
- funcs_ptr->zf_key = zend_hash_str_find_ptr(&class_type->function_table, "key", sizeof("key") - 1);
- funcs_ptr->zf_current = zend_hash_str_find_ptr(&class_type->function_table, "current", sizeof("current") - 1);
- funcs_ptr->zf_next = zend_hash_str_find_ptr(&class_type->function_table, "next", sizeof("next") - 1);
- }
- } else {
- if (!funcs_ptr) {
- funcs_ptr = zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
- class_type->iterator_funcs_ptr = funcs_ptr;
- memset(funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
- } else {
- funcs_ptr->zf_valid = NULL;
- funcs_ptr->zf_current = NULL;
- funcs_ptr->zf_key = NULL;
- funcs_ptr->zf_next = NULL;
- funcs_ptr->zf_rewind = NULL;
- }
- }
+
+ ZEND_ASSERT(!class_type->iterator_funcs_ptr && "Iterator funcs already set?");
+ zend_class_iterator_funcs *funcs_ptr = class_type->type == ZEND_INTERNAL_CLASS
+ ? pemalloc(sizeof(zend_class_iterator_funcs), 1)
+ : zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
+ memset(funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
+ class_type->iterator_funcs_ptr = funcs_ptr;
+
return SUCCESS;
}
/* }}} */