]> granicus.if.org Git - php/commitdiff
Resolve trait alias refers to earlier
authorNikita Popov <nikita.ppv@gmail.com>
Tue, 3 Mar 2020 13:21:33 +0000 (14:21 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Tue, 3 Mar 2020 13:23:31 +0000 (14:23 +0100)
Make sure all trait method references are converted to absolute
method references in advance. This regresses one error message
that I don't think is particularly valuable.

Zend/tests/traits/bug54441.phpt
Zend/zend_inheritance.c

index bb83d12299e441664337d42ddc866afa44cbe1e2..195ff09ff2a838f6bedc5058f77a3f9b5ad4370e 100644 (file)
@@ -16,4 +16,4 @@ class Boo {
 
 ?>
 --EXPECTF--
-Fatal error: The modifiers for the trait alias dontKnow() need to be changed in the same statement in which the alias is defined. Error in %s on line %d
+Fatal error: The modifiers of the trait method dontKnow() are changed, but this method does not exist. Error in %s on line %d
index eb79aab0985d8f69302bf543460dfb7f80d9cf47..df84efc389cc88cceced3bdaf18e591aef8d73cd 100644 (file)
@@ -1690,9 +1690,9 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z
                while (alias) {
                        /* Scope unset or equal to the function we compare to, and the alias applies to fn */
                        if (alias->alias != NULL
-                               && (!aliases[i] || fn->common.scope == aliases[i])
-                               && ZSTR_LEN(alias->trait_method.method_name) == ZSTR_LEN(fnname)
-                               && (zend_binary_strcasecmp(ZSTR_VAL(alias->trait_method.method_name), ZSTR_LEN(alias->trait_method.method_name), ZSTR_VAL(fnname), ZSTR_LEN(fnname)) == 0)) {
+                               && fn->common.scope == aliases[i]
+                               && zend_string_equals_ci(alias->trait_method.method_name, fnname)
+                       ) {
                                fn_copy = *fn;
 
                                /* if it is 0, no modifieres has been changed */
@@ -1703,15 +1703,6 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z
                                lcname = zend_string_tolower(alias->alias);
                                zend_add_trait_method(ce, alias->alias, lcname, &fn_copy, overridden);
                                zend_string_release_ex(lcname, 0);
-
-                               /* Record the trait from which this alias was resolved. */
-                               if (!aliases[i]) {
-                                       aliases[i] = fn->common.scope;
-                               }
-                               if (!alias->trait_method.class_name) {
-                                       /* TODO: try to avoid this assignment (it's necessary only for reflection) */
-                                       alias->trait_method.class_name = zend_string_copy(fn->common.scope->name);
-                               }
                        }
                        alias_ptr++;
                        alias = *alias_ptr;
@@ -1732,20 +1723,10 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z
                        while (alias) {
                                /* Scope unset or equal to the function we compare to, and the alias applies to fn */
                                if (alias->alias == NULL && alias->modifiers != 0
-                                       && (!aliases[i] || fn->common.scope == aliases[i])
-                                       && (ZSTR_LEN(alias->trait_method.method_name) == ZSTR_LEN(fnname))
-                                       && (zend_binary_strcasecmp(ZSTR_VAL(alias->trait_method.method_name), ZSTR_LEN(alias->trait_method.method_name), ZSTR_VAL(fnname), ZSTR_LEN(fnname)) == 0)) {
-
+                                       && fn->common.scope == aliases[i]
+                                       && zend_string_equals_ci(alias->trait_method.method_name, fnname)
+                               ) {
                                        fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK));
-
-                                       /** Record the trait from which this alias was resolved. */
-                                       if (!aliases[i]) {
-                                               aliases[i] = fn->common.scope;
-                                       }
-                                       if (!alias->trait_method.class_name) {
-                                               /* TODO: try to avoid this assignment (it's necessary only for reflection) */
-                                               alias->trait_method.class_name = zend_string_copy(fn->common.scope->name);
-                                       }
                                }
                                alias_ptr++;
                                alias = *alias_ptr;
@@ -1862,9 +1843,11 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
                aliases = ecalloc(i, sizeof(zend_class_entry*));
                i = 0;
                while (ce->trait_aliases[i]) {
-                       /** For all aliases with an explicit class name, resolve the class now. */
-                       if (ce->trait_aliases[i]->trait_method.class_name) {
-                               cur_method_ref = &ce->trait_aliases[i]->trait_method;
+                       zend_trait_alias *cur_alias = ce->trait_aliases[i];
+                       cur_method_ref = &ce->trait_aliases[i]->trait_method;
+                       lcname = zend_string_tolower(cur_method_ref->method_name);
+                       if (cur_method_ref->class_name) {
+                               /* For all aliases with an explicit class name, resolve the class now. */
                                trait = zend_fetch_class(cur_method_ref->class_name, ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD);
                                if (!trait) {
                                        zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(cur_method_ref->class_name));
@@ -1872,13 +1855,47 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
                                zend_check_trait_usage(ce, trait, traits);
                                aliases[i] = trait;
 
-                               /** And, ensure that the referenced method is resolvable, too. */
-                               lcname = zend_string_tolower(cur_method_ref->method_name);
+                               /* And, ensure that the referenced method is resolvable, too. */
                                if (!zend_hash_exists(&trait->function_table, lcname)) {
                                        zend_error_noreturn(E_COMPILE_ERROR, "An alias was defined for %s::%s but this method does not exist", ZSTR_VAL(trait->name), ZSTR_VAL(cur_method_ref->method_name));
                                }
-                               zend_string_release_ex(lcname, 0);
+                       } else {
+                               /* Find out which trait this method refers to. */
+                               trait = NULL;
+                               for (j = 0; j < ce->num_traits; j++) {
+                                       if (traits[j]) {
+                                               if (zend_hash_exists(&traits[j]->function_table, lcname)) {
+                                                       if (!trait) {
+                                                               trait = traits[j];
+                                                               continue;
+                                                       }
+
+                                                       // TODO: This is ambiguous! The first trait is assumed.
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                               /* Non-absolute method reference refers to method that does not exist. */
+                               if (!trait) {
+                                       if (cur_alias->alias) {
+                                               zend_error_noreturn(E_COMPILE_ERROR,
+                                                       "An alias (%s) was defined for method %s(), but this method does not exist",
+                                                       ZSTR_VAL(cur_alias->alias),
+                                                       ZSTR_VAL(cur_alias->trait_method.method_name));
+                                       } else {
+                                               zend_error_noreturn(E_COMPILE_ERROR,
+                                                       "The modifiers of the trait method %s() are changed, but this method does not exist. Error",
+                                                       ZSTR_VAL(cur_alias->trait_method.method_name));
+                                       }
+                               }
+
+                               aliases[i] = trait;
+
+                               /* TODO: try to avoid this assignment (it's necessary only for reflection) */
+                               cur_method_ref->class_name = zend_string_copy(trait->name);
                        }
+                       zend_string_release_ex(lcname, 0);
                        i++;
                }
        }
@@ -2066,56 +2083,6 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
 }
 /* }}} */
 
-static void zend_do_check_for_inconsistent_traits_aliasing(zend_class_entry *ce, zend_class_entry **aliases) /* {{{ */
-{
-       int i = 0;
-       zend_trait_alias* cur_alias;
-       zend_string* lc_method_name;
-
-       if (ce->trait_aliases) {
-               while (ce->trait_aliases[i]) {
-                       cur_alias = ce->trait_aliases[i];
-                       /** The trait for this alias has not been resolved, this means, this
-                               alias was not applied. Abort with an error. */
-                       if (!aliases[i]) {
-                               if (cur_alias->alias) {
-                                       /** Plain old inconsistency/typo/bug */
-                                       zend_error_noreturn(E_COMPILE_ERROR,
-                                                          "An alias (%s) was defined for method %s(), but this method does not exist",
-                                                          ZSTR_VAL(cur_alias->alias),
-                                                          ZSTR_VAL(cur_alias->trait_method.method_name));
-                               } else {
-                                       /** Here are two possible cases:
-                                               1) this is an attempt to modify the visibility
-                                                  of a method introduce as part of another alias.
-                                                  Since that seems to violate the DRY principle,
-                                                  we check against it and abort.
-                                               2) it is just a plain old inconsitency/typo/bug
-                                                  as in the case where alias is set. */
-
-                                       lc_method_name = zend_string_tolower(
-                                               cur_alias->trait_method.method_name);
-                                       if (zend_hash_exists(&ce->function_table,
-                                                                                lc_method_name)) {
-                                               zend_string_release_ex(lc_method_name, 0);
-                                               zend_error_noreturn(E_COMPILE_ERROR,
-                                                                  "The modifiers for the trait alias %s() need to be changed in the same statement in which the alias is defined. Error",
-                                                                  ZSTR_VAL(cur_alias->trait_method.method_name));
-                                       } else {
-                                               zend_string_release_ex(lc_method_name, 0);
-                                               zend_error_noreturn(E_COMPILE_ERROR,
-                                                                  "The modifiers of the trait method %s() are changed, but this method does not exist. Error",
-                                                                  ZSTR_VAL(cur_alias->trait_method.method_name));
-
-                                       }
-                               }
-                       }
-                       i++;
-               }
-       }
-}
-/* }}} */
-
 static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */
 {
        HashTable **exclude_tables;
@@ -2153,9 +2120,6 @@ static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */
        /* first care about all methods to be flattened into the class */
        zend_do_traits_method_binding(ce, traits, exclude_tables, aliases);
 
-       /* Aliases which have not been applied indicate typos/bugs. */
-       zend_do_check_for_inconsistent_traits_aliasing(ce, aliases);
-
        if (aliases) {
                efree(aliases);
        }