Skip to content

Commit 2c5fcd5

Browse files
committed
Resolve trait alias refers to earlier
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.
1 parent ef03236 commit 2c5fcd5

File tree

2 files changed

+49
-85
lines changed

2 files changed

+49
-85
lines changed

Zend/tests/traits/bug54441.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ class Boo {
1616

1717
?>
1818
--EXPECTF--
19-
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
19+
Fatal error: The modifiers of the trait method dontKnow() are changed, but this method does not exist. Error in %s on line %d

Zend/zend_inheritance.c

Lines changed: 48 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,9 +1690,9 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z
16901690
while (alias) {
16911691
/* Scope unset or equal to the function we compare to, and the alias applies to fn */
16921692
if (alias->alias != NULL
1693-
&& (!aliases[i] || fn->common.scope == aliases[i])
1694-
&& ZSTR_LEN(alias->trait_method.method_name) == ZSTR_LEN(fnname)
1695-
&& (zend_binary_strcasecmp(ZSTR_VAL(alias->trait_method.method_name), ZSTR_LEN(alias->trait_method.method_name), ZSTR_VAL(fnname), ZSTR_LEN(fnname)) == 0)) {
1693+
&& fn->common.scope == aliases[i]
1694+
&& zend_string_equals_ci(alias->trait_method.method_name, fnname)
1695+
) {
16961696
fn_copy = *fn;
16971697

16981698
/* 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
17031703
lcname = zend_string_tolower(alias->alias);
17041704
zend_add_trait_method(ce, alias->alias, lcname, &fn_copy, overridden);
17051705
zend_string_release_ex(lcname, 0);
1706-
1707-
/* Record the trait from which this alias was resolved. */
1708-
if (!aliases[i]) {
1709-
aliases[i] = fn->common.scope;
1710-
}
1711-
if (!alias->trait_method.class_name) {
1712-
/* TODO: try to avoid this assignment (it's necessary only for reflection) */
1713-
alias->trait_method.class_name = zend_string_copy(fn->common.scope->name);
1714-
}
17151706
}
17161707
alias_ptr++;
17171708
alias = *alias_ptr;
@@ -1732,20 +1723,10 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z
17321723
while (alias) {
17331724
/* Scope unset or equal to the function we compare to, and the alias applies to fn */
17341725
if (alias->alias == NULL && alias->modifiers != 0
1735-
&& (!aliases[i] || fn->common.scope == aliases[i])
1736-
&& (ZSTR_LEN(alias->trait_method.method_name) == ZSTR_LEN(fnname))
1737-
&& (zend_binary_strcasecmp(ZSTR_VAL(alias->trait_method.method_name), ZSTR_LEN(alias->trait_method.method_name), ZSTR_VAL(fnname), ZSTR_LEN(fnname)) == 0)) {
1738-
1726+
&& fn->common.scope == aliases[i]
1727+
&& zend_string_equals_ci(alias->trait_method.method_name, fnname)
1728+
) {
17391729
fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK));
1740-
1741-
/** Record the trait from which this alias was resolved. */
1742-
if (!aliases[i]) {
1743-
aliases[i] = fn->common.scope;
1744-
}
1745-
if (!alias->trait_method.class_name) {
1746-
/* TODO: try to avoid this assignment (it's necessary only for reflection) */
1747-
alias->trait_method.class_name = zend_string_copy(fn->common.scope->name);
1748-
}
17491730
}
17501731
alias_ptr++;
17511732
alias = *alias_ptr;
@@ -1862,23 +1843,59 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
18621843
aliases = ecalloc(i, sizeof(zend_class_entry*));
18631844
i = 0;
18641845
while (ce->trait_aliases[i]) {
1865-
/** For all aliases with an explicit class name, resolve the class now. */
1866-
if (ce->trait_aliases[i]->trait_method.class_name) {
1867-
cur_method_ref = &ce->trait_aliases[i]->trait_method;
1846+
zend_trait_alias *cur_alias = ce->trait_aliases[i];
1847+
cur_method_ref = &ce->trait_aliases[i]->trait_method;
1848+
lcname = zend_string_tolower(cur_method_ref->method_name);
1849+
if (cur_method_ref->class_name) {
1850+
/* For all aliases with an explicit class name, resolve the class now. */
18681851
trait = zend_fetch_class(cur_method_ref->class_name, ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD);
18691852
if (!trait) {
18701853
zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(cur_method_ref->class_name));
18711854
}
18721855
zend_check_trait_usage(ce, trait, traits);
18731856
aliases[i] = trait;
18741857

1875-
/** And, ensure that the referenced method is resolvable, too. */
1876-
lcname = zend_string_tolower(cur_method_ref->method_name);
1858+
/* And, ensure that the referenced method is resolvable, too. */
18771859
if (!zend_hash_exists(&trait->function_table, lcname)) {
18781860
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));
18791861
}
1880-
zend_string_release_ex(lcname, 0);
1862+
} else {
1863+
/* Find out which trait this method refers to. */
1864+
trait = NULL;
1865+
for (j = 0; j < ce->num_traits; j++) {
1866+
if (traits[j]) {
1867+
if (zend_hash_exists(&traits[j]->function_table, lcname)) {
1868+
if (!trait) {
1869+
trait = traits[j];
1870+
continue;
1871+
}
1872+
1873+
// TODO: This is ambiguous! The first trait is assumed.
1874+
break;
1875+
}
1876+
}
1877+
}
1878+
1879+
/* Non-absolute method reference refers to method that does not exist. */
1880+
if (!trait) {
1881+
if (cur_alias->alias) {
1882+
zend_error_noreturn(E_COMPILE_ERROR,
1883+
"An alias (%s) was defined for method %s(), but this method does not exist",
1884+
ZSTR_VAL(cur_alias->alias),
1885+
ZSTR_VAL(cur_alias->trait_method.method_name));
1886+
} else {
1887+
zend_error_noreturn(E_COMPILE_ERROR,
1888+
"The modifiers of the trait method %s() are changed, but this method does not exist. Error",
1889+
ZSTR_VAL(cur_alias->trait_method.method_name));
1890+
}
1891+
}
1892+
1893+
aliases[i] = trait;
1894+
1895+
/* TODO: try to avoid this assignment (it's necessary only for reflection) */
1896+
cur_method_ref->class_name = zend_string_copy(trait->name);
18811897
}
1898+
zend_string_release_ex(lcname, 0);
18821899
i++;
18831900
}
18841901
}
@@ -2066,56 +2083,6 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
20662083
}
20672084
/* }}} */
20682085

2069-
static void zend_do_check_for_inconsistent_traits_aliasing(zend_class_entry *ce, zend_class_entry **aliases) /* {{{ */
2070-
{
2071-
int i = 0;
2072-
zend_trait_alias* cur_alias;
2073-
zend_string* lc_method_name;
2074-
2075-
if (ce->trait_aliases) {
2076-
while (ce->trait_aliases[i]) {
2077-
cur_alias = ce->trait_aliases[i];
2078-
/** The trait for this alias has not been resolved, this means, this
2079-
alias was not applied. Abort with an error. */
2080-
if (!aliases[i]) {
2081-
if (cur_alias->alias) {
2082-
/** Plain old inconsistency/typo/bug */
2083-
zend_error_noreturn(E_COMPILE_ERROR,
2084-
"An alias (%s) was defined for method %s(), but this method does not exist",
2085-
ZSTR_VAL(cur_alias->alias),
2086-
ZSTR_VAL(cur_alias->trait_method.method_name));
2087-
} else {
2088-
/** Here are two possible cases:
2089-
1) this is an attempt to modify the visibility
2090-
of a method introduce as part of another alias.
2091-
Since that seems to violate the DRY principle,
2092-
we check against it and abort.
2093-
2) it is just a plain old inconsitency/typo/bug
2094-
as in the case where alias is set. */
2095-
2096-
lc_method_name = zend_string_tolower(
2097-
cur_alias->trait_method.method_name);
2098-
if (zend_hash_exists(&ce->function_table,
2099-
lc_method_name)) {
2100-
zend_string_release_ex(lc_method_name, 0);
2101-
zend_error_noreturn(E_COMPILE_ERROR,
2102-
"The modifiers for the trait alias %s() need to be changed in the same statement in which the alias is defined. Error",
2103-
ZSTR_VAL(cur_alias->trait_method.method_name));
2104-
} else {
2105-
zend_string_release_ex(lc_method_name, 0);
2106-
zend_error_noreturn(E_COMPILE_ERROR,
2107-
"The modifiers of the trait method %s() are changed, but this method does not exist. Error",
2108-
ZSTR_VAL(cur_alias->trait_method.method_name));
2109-
2110-
}
2111-
}
2112-
}
2113-
i++;
2114-
}
2115-
}
2116-
}
2117-
/* }}} */
2118-
21192086
static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */
21202087
{
21212088
HashTable **exclude_tables;
@@ -2153,9 +2120,6 @@ static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */
21532120
/* first care about all methods to be flattened into the class */
21542121
zend_do_traits_method_binding(ce, traits, exclude_tables, aliases);
21552122

2156-
/* Aliases which have not been applied indicate typos/bugs. */
2157-
zend_do_check_for_inconsistent_traits_aliasing(ce, aliases);
2158-
21592123
if (aliases) {
21602124
efree(aliases);
21612125
}

0 commit comments

Comments
 (0)