Traits refactoring

This commit is contained in:
Dmitry Stogov 2012-12-25 10:23:08 +04:00
parent 3f0dcc08e0
commit 3f8c729e69
18 changed files with 339 additions and 427 deletions

View File

@ -16,4 +16,4 @@ class C implements IFoo {
}
--EXPECTF--
Fatal error: Declaration of C::oneArgument() must be compatible with IFoo::oneArgument($a) in %s on line %d
Fatal error: Declaration of TFoo::oneArgument() must be compatible with IFoo::oneArgument($a) in %s on line %d

View File

@ -23,4 +23,4 @@ $o = new CBroken;
$o->foo(1);
--EXPECTF--
Fatal error: Declaration of TBroken1::foo($a) must be compatible with TBroken2::foo($a, $b = 0) in %s on line %d
Fatal error: Declaration of TBroken2::foo($a, $b = 0) must be compatible with TBroken1::foo($a) in %s on line %d

View File

@ -23,4 +23,4 @@ $o = new CBroken;
$o->foo(1);
--EXPECTF--
Fatal error: Declaration of TBroken1::foo($a, $b = 0) must be compatible with TBroken2::foo($a) in %s on line %d
Fatal error: Declaration of TBroken2::foo($a) must be compatible with TBroken1::foo($a, $b = 0) in %s on line %d

View File

@ -22,4 +22,4 @@ class TraitsTest1 {
?>
--EXPECTF--
Fatal error: Declaration of THelloB::hello() must be compatible with THelloA::hello($a) in %s on line %d
Fatal error: Declaration of THelloA::hello($a) must be compatible with THelloB::hello() in %s on line %d

View File

@ -23,4 +23,4 @@ class TraitsTest1 {
?>
--EXPECTF--
Fatal error: Declaration of THelloA::hello($a) must be compatible with THelloB::hello() in %s on line %d
Fatal error: Declaration of THelloB::hello() must be compatible with THelloA::hello($a) in %s on line %d

View File

@ -10,13 +10,9 @@ trait c {
public function test() { return 2; }
}
trait b {
public function test() { return 1; }
}
class bar {
use foo, c { c::test insteadof foo, b; }
use foo, c { c::test insteadof foo, b; }
use foo, c { c::test insteadof foo; }
use foo, c { c::test insteadof foo; }
}
$x = new bar;

View File

@ -35,4 +35,4 @@ $o->sayHello(array());
--EXPECTF--
World!
Fatal error: Declaration of MyHelloWorld::sayHello() must be compatible with Base::sayHello(array $a) in %s on line %d
Fatal error: Declaration of SayWorld::sayHello(Base $d) must be compatible with Base::sayHello(array $a) in %s on line %d

View File

@ -0,0 +1,30 @@
--TEST--
Aliasing leading to conflict should result in error message
--FILE--
<?php
error_reporting(E_ALL);
trait Hello {
public function hello() {
echo 'Hello';
}
}
trait World {
public function world() {
echo ' World!';
}
}
class MyClass {
use Hello, World { world as hello; }
}
$o = new MyClass();
$o->hello();
$o->world();
?>
--EXPECTF--
Fatal error: Trait method hello has not been applied, because there are collisions with other trait methods on MyClass in %s on line %d

View File

@ -0,0 +1,17 @@
--TEST--
Invalid conflict resolution (unused trait as lhs of "insteadof")
--FILE--
<?php
trait T1 {
function foo() {echo "T1\n";}
}
trait T2 {
function foo() {echo "T2\n";}
}
class C {
use T1 {
T2::foo insteadof T1;
}
}
--EXPECTF--
Fatal error: Trait T2 is not used in %s on line %d

View File

@ -0,0 +1,17 @@
--TEST--
Invalid conflict resolution (unused trait as rhs of "insteadof")
--FILE--
<?php
trait T1 {
function foo() {echo "T1\n";}
}
trait T2 {
function foo() {echo "T2\n";}
}
class C {
use T1 {
T1::foo insteadof T2;
}
}
--EXPECTF--
Fatal error: Trait T2 is not used in %s on line %d

View File

@ -0,0 +1,17 @@
--TEST--
Invalid conflict resolution (unused trait as lhs of "as")
--FILE--
<?php
trait T1 {
function foo() {echo "T1\n";}
}
trait T2 {
function foo() {echo "T2\n";}
}
class C {
use T1 {
T2::foo as private;
}
}
--EXPECTF--
Fatal error: Trait T2 is not used in %s on line %d

View File

@ -0,0 +1,15 @@
--TEST--
abstract alias
--FILE--
<?php
trait T1 {
function foo() {}
}
class C1 {
use T1 {
T1::foo as abstract;
}
}
?>
--EXPECTF--
Fatal error: Cannot use 'abstarct' as method modifier in %s on line %d

View File

@ -0,0 +1,15 @@
--TEST--
final alias
--FILE--
<?php
trait T1 {
function foo() {}
}
class C1 {
use T1 {
T1::foo as final;
}
}
?>
--EXPECTF--
Fatal error: Cannot use 'final' as method modifier in %s on line %d

View File

@ -437,7 +437,7 @@ struct _zend_trait_precedence {
zend_class_entry** exclude_from_classes;
union _zend_function* function;
union _zend_function* function; /* FIXME: kept in 5.4 for BC, not used */
};
typedef struct _zend_trait_precedence zend_trait_precedence;
@ -455,7 +455,7 @@ struct _zend_trait_alias {
*/
zend_uint modifiers;
union _zend_function* function;
union _zend_function* function; /* FIXME: kept in 5.4 for BC, not used */
};
typedef struct _zend_trait_alias zend_trait_alias;

View File

@ -3527,8 +3527,7 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS && ce->type == ZEND_INTERNAL_CLASS) {
ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
} else if (!(ce->ce_flags & ZEND_ACC_IMPLEMENT_INTERFACES)
&& !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) {
} else if (!(ce->ce_flags & (ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
/* The verification will be done in runtime by ZEND_VERIFY_ABSTRACT_CLASS */
zend_verify_abstract_class(ce TSRMLS_CC);
}
@ -3625,7 +3624,6 @@ ZEND_API void zend_do_implement_trait(zend_class_entry *ce, zend_class_entry *tr
}
}
ce->traits[ce->num_traits++] = trait;
trait->refcount++;
}
}
/* }}} */
@ -3637,92 +3635,8 @@ static zend_bool zend_traits_method_compatibility_check(zend_function *fn, zend_
return zend_do_perform_implementation_check(fn, other_fn TSRMLS_CC)
&& zend_do_perform_implementation_check(other_fn, fn TSRMLS_CC)
&& ((fn_flags & ZEND_ACC_FINAL) == (other_flags & ZEND_ACC_FINAL)) /* equal final qualifier */
&& ((fn_flags & ZEND_ACC_STATIC)== (other_flags & ZEND_ACC_STATIC)); /* equal static qualifier */
}
/* }}} */
static int zend_traits_merge_functions(zend_function *fn TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
{
size_t current;
size_t i;
size_t count;
HashTable* resulting_table;
HashTable** function_tables;
zend_class_entry *ce;
size_t collision = 0;
size_t abstract_solved = 0;
zend_function* other_trait_fn;
current = va_arg(args, size_t); /* index of current trait */
count = va_arg(args, size_t);
resulting_table = va_arg(args, HashTable*);
function_tables = va_arg(args, HashTable**);
ce = va_arg(args, zend_class_entry*);
for (i = 0; i < count; i++) {
if (i == current) {
continue; /* just skip this, cause its the table this function is applied on */
}
if (zend_hash_quick_find(function_tables[i], hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void **)&other_trait_fn) == SUCCESS) {
/* if it is an abstract method, there is no collision */
if (other_trait_fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
/* Make sure they are compatible */
/* In case both are abstract, just check prototype, but need to do that in both directions */
if (!zend_traits_method_compatibility_check(fn, other_trait_fn TSRMLS_CC)) {
zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
zend_get_function_declaration(fn TSRMLS_CC),
zend_get_function_declaration(other_trait_fn TSRMLS_CC));
}
/* we can savely free and remove it from other table */
zend_function_dtor(other_trait_fn);
zend_hash_quick_del(function_tables[i], hash_key->arKey, hash_key->nKeyLength, hash_key->h);
} else {
/* if it is not an abstract method, there is still no collision */
/* if fn is an abstract method */
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
/* Make sure they are compatible.
Here, we already know other_trait_fn cannot be abstract, full check ok. */
if (!zend_traits_method_compatibility_check(fn, other_trait_fn TSRMLS_CC)) {
zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
zend_get_function_declaration(fn TSRMLS_CC),
zend_get_function_declaration(other_trait_fn TSRMLS_CC));
}
/* just mark as solved, will be added if its own trait is processed */
abstract_solved = 1;
} else {
/* but else, we have a collision of non-abstract methods */
collision++;
zend_function_dtor(other_trait_fn);
zend_hash_quick_del(function_tables[i], hash_key->arKey, hash_key->nKeyLength, hash_key->h);
}
}
}
}
if (collision) {
zend_function* class_fn;
/* make sure method is not already overridden in class */
if (zend_hash_quick_find(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void **)&class_fn) == FAILURE
|| class_fn->common.scope != ce) {
zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because there are collisions with other trait methods on %s", fn->common.function_name, ce->name);
}
zend_function_dtor(fn);
} else if (abstract_solved) {
zend_function_dtor(fn);
} else {
/* Add it to result function table */
if (zend_hash_quick_add(resulting_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, fn, sizeof(zend_function), NULL)==FAILURE) {
zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because failure occurred during updating resulting trait method table", fn->common.function_name);
}
}
return ZEND_HASH_APPLY_REMOVE;
&& ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) ==
(other_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC))); /* equal final and static qualifier */
}
/* }}} */
@ -3767,66 +3681,84 @@ static void zend_add_magic_methods(zend_class_entry* ce, const char* mname, uint
}
/* }}} */
static int zend_traits_merge_functions_to_class(zend_function *fn TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
static void zend_add_trait_method(zend_class_entry *ce, const char *name, const char *arKey, uint nKeyLength, zend_function *fn, HashTable **overriden TSRMLS_DC) /* {{{ */
{
zend_class_entry *ce = va_arg(args, zend_class_entry*);
zend_function* existing_fn = NULL;
zend_function fn_copy, *fn_copy_p;
zend_function* prototype = NULL; /* is used to determine the prototype according to the inheritance chain */
zend_function *existing_fn = NULL;
ulong h = zend_hash_func(arKey, nKeyLength);
if (zend_hash_quick_find(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void**) &existing_fn) == FAILURE ||
existing_fn->common.scope != ce) {
/* not found or inherited from other class or interface */
zend_function* parent_function;
if (ce->parent && zend_hash_quick_find(&ce->parent->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void**) &parent_function) != FAILURE) {
prototype = parent_function; /* ->common.fn_flags |= ZEND_ACC_ABSTRACT; */
/* we got that method in the parent class, and are going to override it,
except, if the trait is just asking to have an abstract method implemented. */
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
/* then we clean up an skip this method */
zend_function_dtor(fn);
return ZEND_HASH_APPLY_REMOVE;
if (zend_hash_quick_find(&ce->function_table, arKey, nKeyLength, h, (void**) &existing_fn) == SUCCESS) {
if (existing_fn->common.scope == ce) {
/* members from the current class override trait methods */
/* use temporary *overriden HashTable to detect hidden conflict */
if (*overriden) {
if (zend_hash_quick_find(*overriden, arKey, nKeyLength, h, (void**) &existing_fn) == SUCCESS) {
if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
/* Make sure the trait method is compatible with previosly declared abstarct method */
if (!zend_traits_method_compatibility_check(fn, existing_fn TSRMLS_CC)) {
zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
zend_get_function_declaration(fn TSRMLS_CC),
zend_get_function_declaration(existing_fn TSRMLS_CC));
}
} else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
/* Make sure the abstract declaration is compatible with previous declaration */
if (!zend_traits_method_compatibility_check(existing_fn, fn TSRMLS_CC)) {
zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
zend_get_function_declaration(fn TSRMLS_CC),
zend_get_function_declaration(existing_fn TSRMLS_CC));
}
return;
}
}
} else {
ALLOC_HASHTABLE(*overriden);
zend_hash_init_ex(*overriden, 2, NULL, NULL, 0, 0);
}
zend_hash_quick_update(*overriden, arKey, nKeyLength, h, fn, sizeof(zend_function), (void**)&fn);
return;
} else if (existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
/* Make sure the trait method is compatible with previosly declared abstarct method */
if (!zend_traits_method_compatibility_check(fn, existing_fn TSRMLS_CC)) {
zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
zend_get_function_declaration(fn TSRMLS_CC),
zend_get_function_declaration(existing_fn TSRMLS_CC));
}
} else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
/* Make sure the abstract declaration is compatible with previous declaration */
if (!zend_traits_method_compatibility_check(existing_fn, fn TSRMLS_CC)) {
zend_error(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s",
zend_get_function_declaration(fn TSRMLS_CC),
zend_get_function_declaration(existing_fn TSRMLS_CC));
}
return;
} else if ((existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
/* two trais can't define the same non-abstarct method */
#if 1
zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because there are collisions with other trait methods on %s",
name, ce->name);
#else /* TODO: better errot message */
zend_error(E_COMPILE_ERROR, "Trait method %s::%s has not been applied as %s::%s, because of collision with %s::%s",
fn->common.scope->name, fn->common.function_name,
ce->name, name,
existing_fn->common.scope->name, existing_fn->common.function_name);
#endif
} else {
/* inherited members are overridden by members inserted by traits */
/* check whether the trait method fullfills the inheritance requirements */
do_inheritance_check_on_method(fn, existing_fn TSRMLS_CC);
}
}
function_add_ref(fn);
zend_hash_quick_update(&ce->function_table, arKey, nKeyLength, h, fn, sizeof(zend_function), (void**)&fn);
zend_add_magic_methods(ce, arKey, nKeyLength, fn TSRMLS_CC);
}
/* }}} */
static int zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce TSRMLS_DC) /* {{{ */
{
if ((fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
fn->common.scope = ce;
fn->common.prototype = prototype;
if (prototype
&& (prototype->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT
|| prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) {
fn->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT;
} else if (fn->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT) {
/* remove ZEND_ACC_IMPLEMENTED_ABSTRACT flag, think it shouldn't be copied to class */
fn->common.fn_flags = fn->common.fn_flags - ZEND_ACC_IMPLEMENTED_ABSTRACT;
}
/* check whether the trait method fullfills the inheritance requirements */
if (prototype) {
do_inheritance_check_on_method(fn, prototype TSRMLS_CC);
}
/* one more thing: make sure we properly implement an abstract method */
if (existing_fn && existing_fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
prototype = fn->common.prototype;
do_inheritance_check_on_method(fn, existing_fn TSRMLS_CC);
fn->common.prototype = prototype;
}
/* delete inherited fn if the function to be added is not abstract */
if (existing_fn
&& existing_fn->common.scope != ce
&& (fn->common.fn_flags & ZEND_ACC_ABSTRACT) == 0) {
/* it is just a reference which was added to the subclass while doing
the inheritance, so we can deleted now, and will add the overriding
method afterwards.
Except, if we try to add an abstract function, then we should not
delete the inherited one */
zend_hash_quick_del(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h);
}
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
@ -3834,78 +3766,56 @@ static int zend_traits_merge_functions_to_class(zend_function *fn TSRMLS_DC, int
if (fn->op_array.static_variables) {
ce->ce_flags |= ZEND_HAS_STATIC_IN_METHODS;
}
fn_copy = *fn;
function_add_ref(&fn_copy);
if (zend_hash_quick_update(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, &fn_copy, sizeof(zend_function), (void**)&fn_copy_p)==FAILURE) {
zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because failure occurred during updating class method table", hash_key->arKey);
}
zend_add_magic_methods(ce, hash_key->arKey, hash_key->nKeyLength, fn_copy_p TSRMLS_CC);
zend_function_dtor(fn);
} else {
zend_function_dtor(fn);
}
return ZEND_HASH_APPLY_REMOVE;
return ZEND_HASH_APPLY_KEEP;
}
/* }}} */
static int zend_traits_copy_functions(zend_function *fn TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
{
HashTable* target;
zend_trait_alias** aliases;
HashTable* exclude_table;
char* lcname;
unsigned int fnname_len;
zend_function fn_copy;
void* dummy;
size_t i = 0;
zend_class_entry *ce;
HashTable **overriden;
zend_trait_alias *alias, **alias_ptr;
HashTable *exclude_table;
char *lcname;
unsigned int fnname_len;
zend_function fn_copy;
void *dummy;
target = va_arg(args, HashTable*);
aliases = va_arg(args, zend_trait_alias**);
ce = va_arg(args, zend_class_entry*);
overriden = va_arg(args, HashTable**);
exclude_table = va_arg(args, HashTable*);
fnname_len = strlen(fn->common.function_name);
/* apply aliases which are qualified with a class name, there should not be any ambiguity */
if (aliases) {
while (aliases[i]) {
if (ce->trait_aliases) {
alias_ptr = ce->trait_aliases;
alias = *alias_ptr;
while (alias) {
/* Scope unset or equal to the function we compare to, and the alias applies to fn */
if (aliases[i]->alias != NULL
&& (!aliases[i]->trait_method->ce || fn->common.scope == aliases[i]->trait_method->ce)
&& aliases[i]->trait_method->mname_len == fnname_len
&& (zend_binary_strcasecmp(aliases[i]->trait_method->method_name, aliases[i]->trait_method->mname_len, fn->common.function_name, fnname_len) == 0)) {
if (alias->alias != NULL
&& (!alias->trait_method->ce || fn->common.scope == alias->trait_method->ce)
&& alias->trait_method->mname_len == fnname_len
&& (zend_binary_strcasecmp(alias->trait_method->method_name, alias->trait_method->mname_len, fn->common.function_name, fnname_len) == 0)) {
fn_copy = *fn;
function_add_ref(&fn_copy);
/* this function_name is never destroyed, because ZEND_ACC_ALIAS
flag is set */
fn_copy.common.function_name = aliases[i]->alias;
fn_copy.common.fn_flags |= ZEND_ACC_ALIAS;
/* if it is 0, no modifieres has been changed */
if (aliases[i]->modifiers) {
fn_copy.common.fn_flags = aliases[i]->modifiers | ZEND_ACC_ALIAS;
if (!(aliases[i]->modifiers & ZEND_ACC_PPP_MASK)) {
fn_copy.common.fn_flags |= ZEND_ACC_PUBLIC;
}
fn_copy.common.fn_flags |= fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK);
if (alias->modifiers) {
fn_copy.common.fn_flags = alias->modifiers | (fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK));
}
lcname = zend_str_tolower_dup(aliases[i]->alias, aliases[i]->alias_len);
if (zend_hash_add(target, lcname, aliases[i]->alias_len+1, &fn_copy, sizeof(zend_function), NULL)==FAILURE) {
zend_error(E_COMPILE_ERROR, "Failed to add aliased trait method (%s) to the trait table. There is probably already a trait method with the same name", fn_copy.common.function_name);
}
lcname = zend_str_tolower_dup(alias->alias, alias->alias_len);
zend_add_trait_method(ce, alias->alias, lcname, alias->alias_len+1, &fn_copy, overriden TSRMLS_DC);
efree(lcname);
/** Record the trait from which this alias was resolved. */
if (!aliases[i]->trait_method->ce) {
aliases[i]->trait_method->ce = fn->common.scope;
/* Record the trait from which this alias was resolved. */
if (!alias->trait_method->ce) {
alias->trait_method->ce = fn->common.scope;
}
}
i++;
alias_ptr++;
alias = *alias_ptr;
}
}
@ -3914,38 +3824,31 @@ static int zend_traits_copy_functions(zend_function *fn TSRMLS_DC, int num_args,
if (exclude_table == NULL || zend_hash_find(exclude_table, lcname, fnname_len, &dummy) == FAILURE) {
/* is not in hashtable, thus, function is not to be excluded */
fn_copy = *fn;
function_add_ref(&fn_copy);
fn_copy.common.fn_flags |= ZEND_ACC_ALIAS;
/* apply aliases which are not qualified by a class name, or which have not
* alias name, just setting visibility */
if (aliases) {
i = 0;
while (aliases[i]) {
/* apply aliases which have not alias name, just setting visibility */
if (ce->trait_aliases) {
alias_ptr = ce->trait_aliases;
alias = *alias_ptr;
while (alias) {
/* Scope unset or equal to the function we compare to, and the alias applies to fn */
if (aliases[i]->alias == NULL && aliases[i]->modifiers != 0
&& (!aliases[i]->trait_method->ce || fn->common.scope == aliases[i]->trait_method->ce)
&& (aliases[i]->trait_method->mname_len == fnname_len)
&& (zend_binary_strcasecmp(aliases[i]->trait_method->method_name, aliases[i]->trait_method->mname_len, fn->common.function_name, fnname_len) == 0)) {
fn_copy.common.fn_flags = aliases[i]->modifiers | ZEND_ACC_ALIAS;
if (alias->alias == NULL && alias->modifiers != 0
&& (!alias->trait_method->ce || fn->common.scope == alias->trait_method->ce)
&& (alias->trait_method->mname_len == fnname_len)
&& (zend_binary_strcasecmp(alias->trait_method->method_name, alias->trait_method->mname_len, fn->common.function_name, fnname_len) == 0)) {
if (!(aliases[i]->modifiers & ZEND_ACC_PPP_MASK)) {
fn_copy.common.fn_flags |= ZEND_ACC_PUBLIC;
}
fn_copy.common.fn_flags |= fn->common.fn_flags ^ (fn->common.fn_flags & ZEND_ACC_PPP_MASK);
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]->trait_method->ce) {
aliases[i]->trait_method->ce = fn->common.scope;
if (!alias->trait_method->ce) {
alias->trait_method->ce = fn->common.scope;
}
}
i++;
alias_ptr++;
alias = *alias_ptr;
}
}
if (zend_hash_add(target, lcname, fnname_len+1, &fn_copy, sizeof(zend_function), NULL)==FAILURE) {
zend_error(E_COMPILE_ERROR, "Failed to add trait method (%s) to the trait table. There is probably already a trait method with the same name", fn_copy.common.function_name);
}
zend_add_trait_method(ce, fn->common.function_name, lcname, fnname_len+1, &fn_copy, overriden TSRMLS_DC);
}
efree(lcname);
@ -3954,10 +3857,16 @@ static int zend_traits_copy_functions(zend_function *fn TSRMLS_DC, int num_args,
}
/* }}} */
/* Copies function table entries to target function table with applied aliasing */
static void zend_traits_copy_trait_function_table(HashTable *target, HashTable *source, zend_trait_alias** aliases, HashTable* exclude_table TSRMLS_DC) /* {{{ */
static void zend_check_trait_usage(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC) /* {{{ */
{
zend_hash_apply_with_arguments(source TSRMLS_CC, (apply_func_args_t)zend_traits_copy_functions, 3, target, aliases, exclude_table);
zend_uint i;
for (i = 0; i < ce->num_traits; i++) {
if (ce->traits[i] == trait) {
return;
}
}
zend_error(E_COMPILE_ERROR, "Trait %s is not used", trait->name);
}
/* }}} */
@ -3980,6 +3889,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /*
ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) {
zend_error(E_COMPILE_ERROR, "Could not find trait %s", cur_method_ref->class_name);
}
zend_check_trait_usage(ce, cur_precedence->trait_method->ce TSRMLS_CC);
/** Ensure that the prefered method is actually available. */
lcname = zend_str_tolower_dup(cur_method_ref->method_name,
@ -4008,8 +3918,9 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /*
if (!(cur_precedence->exclude_from_classes[j] = zend_fetch_class(class_name, name_length, ZEND_FETCH_CLASS_TRAIT |ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) {
zend_error(E_COMPILE_ERROR, "Could not find trait %s", class_name);
}
}
zend_check_trait_usage(ce, cur_precedence->exclude_from_classes[j] TSRMLS_CC);
/* make sure that the trait method is not from a class mentioned in
exclude_from_classes, for consistency */
if (cur_precedence->trait_method->ce == cur_precedence->exclude_from_classes[i]) {
@ -4038,6 +3949,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce TSRMLS_DC) /*
if (!(cur_method_ref->ce = zend_fetch_class(cur_method_ref->class_name, cur_method_ref->cname_len, ZEND_FETCH_CLASS_TRAIT|ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC))) {
zend_error(E_COMPILE_ERROR, "Could not find trait %s", cur_method_ref->class_name);
}
zend_check_trait_usage(ce, cur_method_ref->ce TSRMLS_CC);
/** And, ensure that the referenced method is resolvable, too. */
lcname = zend_str_tolower_dup(cur_method_ref->method_name,
@ -4087,70 +3999,45 @@ static void zend_traits_compile_exclude_table(HashTable* exclude_table, zend_tra
static void zend_do_traits_method_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */
{
HashTable** function_tables;
HashTable* resulting_table;
HashTable exclude_table;
size_t i;
zend_uint i;
HashTable *overriden = NULL;
/* prepare copies of trait function tables for combination */
function_tables = emalloc(sizeof(HashTable*) * ce->num_traits);
resulting_table = (HashTable *)emalloc(sizeof(HashTable));
/* TODO: revisit this start size, may be its not optimal */
zend_hash_init_ex(resulting_table, 10, NULL, NULL, 0, 0);
for (i = 0; i < ce->num_traits; i++) {
function_tables[i] = (HashTable *)emalloc(sizeof(HashTable));
zend_hash_init_ex(function_tables[i], ce->traits[i]->function_table.nNumOfElements, NULL, NULL, 1, 0);
if (ce->trait_precedences) {
HashTable exclude_table;
/* TODO: revisit this start size, may be its not optimal */
zend_hash_init_ex(&exclude_table, 2, NULL, NULL, 0, 0);
zend_traits_compile_exclude_table(&exclude_table, ce->trait_precedences, ce->traits[i]);
/* copies functions, applies defined aliasing, and excludes unused trait methods */
zend_traits_copy_trait_function_table(function_tables[i], &ce->traits[i]->function_table, ce->trait_aliases, &exclude_table TSRMLS_CC);
zend_hash_apply_with_arguments(&ce->traits[i]->function_table TSRMLS_CC, (apply_func_args_t)zend_traits_copy_functions, 3, ce, &overriden, &exclude_table);
zend_hash_destroy(&exclude_table);
} else {
zend_traits_copy_trait_function_table(function_tables[i], &ce->traits[i]->function_table, ce->trait_aliases, NULL TSRMLS_CC);
zend_hash_apply_with_arguments(&ce->traits[i]->function_table TSRMLS_CC, (apply_func_args_t)zend_traits_copy_functions, 3, ce, &overriden, NULL);
}
}
/* now merge trait methods */
for (i = 0; i < ce->num_traits; i++) {
zend_hash_apply_with_arguments(function_tables[i] TSRMLS_CC, (apply_func_args_t)zend_traits_merge_functions, 5, i, ce->num_traits, resulting_table, function_tables, ce);
}
/* Now the resulting_table contains all trait methods we would have to
* add to the class in the following step the methods are inserted into the method table
* if there is already a method with the same name it is replaced iff ce != fn.scope
* --> all inherited methods are overridden, methods defined in the class are left untouched
*/
zend_hash_apply_with_arguments(resulting_table TSRMLS_CC, (apply_func_args_t)zend_traits_merge_functions_to_class, 1, ce);
/* free temporary function tables */
for (i = 0; i < ce->num_traits; i++) {
/* zend_hash_destroy(function_tables[i]); */
zend_hash_graceful_destroy(function_tables[i]);
efree(function_tables[i]);
}
efree(function_tables);
zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t)zend_fixup_trait_method, ce TSRMLS_CC);
/* free temporary resulting table */
/* zend_hash_destroy(resulting_table); */
zend_hash_graceful_destroy(resulting_table);
efree(resulting_table);
if (overriden) {
zend_hash_destroy(overriden);
FREE_HASHTABLE(overriden);
}
}
/* }}} */
static zend_class_entry* find_first_definition(zend_class_entry *ce, size_t current_trait, const char* prop_name, int prop_name_length, ulong prop_hash, zend_class_entry *coliding_ce) /* {{{ */
{
size_t i;
zend_property_info *coliding_prop;
for (i = 0; (i < current_trait) && (i < ce->num_traits); i++) {
if (zend_hash_quick_find(&ce->traits[i]->properties_info, prop_name, prop_name_length+1, prop_hash, (void **) &coliding_prop) == SUCCESS) {
return ce->traits[i];
if (coliding_ce == ce) {
for (i = 0; i < current_trait; i++) {
if (zend_hash_quick_exists(&ce->traits[i]->properties_info, prop_name, prop_name_length+1, prop_hash)) {
return ce->traits[i];
}
}
}
@ -4158,54 +4045,6 @@ static zend_class_entry* find_first_definition(zend_class_entry *ce, size_t curr
}
/* }}} */
static void zend_traits_register_private_property(zend_class_entry *ce, const char *name, int name_len, zend_property_info *old_info, zval *property TSRMLS_DC) /* {{{ */
{
char *priv_name;
int priv_name_length;
const char *interned_name;
zend_property_info property_info;
ulong h = zend_get_hash_value(name, name_len+1);
property_info = *old_info;
if (old_info->flags & ZEND_ACC_STATIC) {
property_info.offset = ce->default_static_members_count++;
ce->default_static_members_table = perealloc(ce->default_static_members_table, sizeof(zval*) * ce->default_static_members_count, ce->type == ZEND_INTERNAL_CLASS);
ce->default_static_members_table[property_info.offset] = property;
if (ce->type == ZEND_USER_CLASS) {
ce->static_members_table = ce->default_static_members_table;
}
} else {
property_info.offset = ce->default_properties_count++;
ce->default_properties_table = perealloc(ce->default_properties_table, sizeof(zval*) * ce->default_properties_count, ce->type == ZEND_INTERNAL_CLASS);
ce->default_properties_table[property_info.offset] = property;
}
zend_mangle_property_name(&priv_name, &priv_name_length, ce->name, ce->name_length, name, name_len, ce->type & ZEND_INTERNAL_CLASS);
property_info.name = priv_name;
property_info.name_length = priv_name_length;
interned_name = zend_new_interned_string(property_info.name, property_info.name_length+1, 0 TSRMLS_CC);
if (interned_name != property_info.name) {
if (ce->type == ZEND_USER_CLASS) {
efree((char*)property_info.name);
} else {
free((char*)property_info.name);
}
property_info.name = interned_name;
}
property_info.h = zend_get_hash_value(property_info.name, property_info.name_length+1);
property_info.ce = ce;
if (property_info.doc_comment) {
property_info.doc_comment = estrndup(property_info.doc_comment, property_info.doc_comment_len);
}
zend_hash_quick_update(&ce->properties_info, name, name_len+1, h, &property_info, sizeof(zend_property_info), NULL);
}
/* }}} */
static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {{{ */
{
size_t i;
@ -4219,7 +4058,8 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {
zend_bool not_compatible;
zval* prop_value;
char* doc_comment;
zend_uint flags;
/* In the following steps the properties are inserted into the property table
* for that, a very strict approach is applied:
* - check for compatibility, if not compatible with any property in class -> fatal
@ -4232,7 +4072,8 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {
/* first get the unmangeld name if necessary,
* then check whether the property is already there
*/
if ((property_info->flags & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
flags = property_info->flags;
if ((flags & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
prop_hash = property_info->h;
prop_name = property_info->name;
prop_name_length = property_info->name_length;
@ -4246,61 +4087,50 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {
/* next: check for conflicts with current class */
if (zend_hash_quick_find(&ce->properties_info, prop_name, prop_name_length+1, prop_hash, (void **) &coliding_prop) == SUCCESS) {
if (coliding_prop->flags & ZEND_ACC_SHADOW) {
/* this one is inherited, lets look it up in its own class */
zend_hash_quick_find(&coliding_prop->ce->properties_info, prop_name, prop_name_length+1, prop_hash, (void **) &coliding_prop);
if (coliding_prop->flags & ZEND_ACC_PRIVATE) {
/* private property, make the property_info.offset indenpended */
if (property_info->flags & ZEND_ACC_STATIC) {
prop_value = ce->traits[i]->default_static_members_table[property_info->offset];
if (coliding_prop->flags & ZEND_ACC_SHADOW) {
zend_hash_quick_del(&ce->properties_info, prop_name, prop_name_length+1, prop_hash);
flags |= ZEND_ACC_CHANGED;
} else {
if ((coliding_prop->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))
== (flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))) {
/* flags are identical, now the value needs to be checked */
if (flags & ZEND_ACC_STATIC) {
not_compatible = (FAILURE == compare_function(&compare_result,
ce->default_static_members_table[coliding_prop->offset],
ce->traits[i]->default_static_members_table[property_info->offset] TSRMLS_CC))
|| (Z_LVAL(compare_result) != 0);
} else {
prop_value = ce->traits[i]->default_properties_table[property_info->offset];
not_compatible = (FAILURE == compare_function(&compare_result,
ce->default_properties_table[coliding_prop->offset],
ce->traits[i]->default_properties_table[property_info->offset] TSRMLS_CC))
|| (Z_LVAL(compare_result) != 0);
}
Z_ADDREF_P(prop_value);
zend_traits_register_private_property(ce, prop_name, prop_name_length, property_info, prop_value TSRMLS_CC);
continue;
}
}
if ((coliding_prop->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))
== (property_info->flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))) {
/* flags are identical, now the value needs to be checked */
if (property_info->flags & ZEND_ACC_STATIC) {
not_compatible = (FAILURE == compare_function(&compare_result,
ce->default_static_members_table[coliding_prop->offset],
ce->traits[i]->default_static_members_table[property_info->offset] TSRMLS_CC))
|| (Z_LVAL(compare_result) != 0);
} else {
not_compatible = (FAILURE == compare_function(&compare_result,
ce->default_properties_table[coliding_prop->offset],
ce->traits[i]->default_properties_table[property_info->offset] TSRMLS_CC))
|| (Z_LVAL(compare_result) != 0);
/* the flags are not identical, thus, we assume properties are not compatible */
not_compatible = 1;
}
} else {
/* the flags are not identical, thus, we assume properties are not compatible */
not_compatible = 1;
}
if (not_compatible) {
zend_error(E_COMPILE_ERROR,
if (not_compatible) {
zend_error(E_COMPILE_ERROR,
"%s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed",
find_first_definition(ce, i, prop_name, prop_name_length, prop_hash, coliding_prop->ce)->name,
property_info->ce->name,
prop_name,
ce->name);
} else {
zend_error(E_STRICT,
} else {
zend_error(E_STRICT,
"%s and %s define the same property ($%s) in the composition of %s. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed",
find_first_definition(ce, i, prop_name, prop_name_length, prop_hash, coliding_prop->ce)->name,
property_info->ce->name,
prop_name,
ce->name);
continue;
}
}
}
/* property not found, so lets add it */
if (property_info->flags & ZEND_ACC_STATIC) {
if (flags & ZEND_ACC_STATIC) {
prop_value = ce->traits[i]->default_static_members_table[property_info->offset];
} else {
prop_value = ce->traits[i]->default_properties_table[property_info->offset];
@ -4309,7 +4139,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce TSRMLS_DC) /* {
doc_comment = property_info->doc_comment ? estrndup(property_info->doc_comment, property_info->doc_comment_len) : NULL;
zend_declare_property_ex(ce, prop_name, prop_name_length,
prop_value, property_info->flags,
prop_value, flags,
doc_comment, property_info->doc_comment_len TSRMLS_CC);
}
}
@ -4433,20 +4263,6 @@ ZEND_API int do_bind_function(const zend_op_array *op_array, zend_op *opline, Ha
}
/* }}} */
void zend_add_trait_precedence(znode *precedence_znode TSRMLS_DC) /* {{{ */
{
zend_class_entry *ce = CG(active_class_entry);
zend_add_to_list(&ce->trait_precedences, precedence_znode->u.op.ptr TSRMLS_CC);
}
/* }}} */
void zend_add_trait_alias(znode *alias_znode TSRMLS_DC) /* {{{ */
{
zend_class_entry *ce = CG(active_class_entry);
zend_add_to_list(&ce->trait_aliases, alias_znode->u.op.ptr TSRMLS_CC);
}
/* }}} */
void zend_prepare_reference(znode *result, znode *class_name, znode *method_name TSRMLS_DC) /* {{{ */
{
zend_trait_method_reference *method_ref = emalloc(sizeof(zend_trait_method_reference));
@ -4471,18 +4287,25 @@ void zend_prepare_reference(znode *result, znode *class_name, znode *method_name
}
/* }}} */
void zend_prepare_trait_alias(znode *result, znode *method_reference, znode *modifiers, znode *alias TSRMLS_DC) /* {{{ */
void zend_add_trait_alias(znode *method_reference, znode *modifiers, znode *alias TSRMLS_DC) /* {{{ */
{
zend_trait_alias *trait_alias = emalloc(sizeof(zend_trait_alias));
zend_class_entry *ce = CG(active_class_entry);
zend_trait_alias *trait_alias;
trait_alias->trait_method = (zend_trait_method_reference*)method_reference->u.op.ptr;
trait_alias->modifiers = Z_LVAL(modifiers->u.constant);
if (Z_LVAL(modifiers->u.constant) == ZEND_ACC_STATIC) {
zend_error(E_COMPILE_ERROR, "Cannot use 'static' as method modifier");
return;
} else if (Z_LVAL(modifiers->u.constant) == ZEND_ACC_ABSTRACT) {
zend_error(E_COMPILE_ERROR, "Cannot use 'abstarct' as method modifier");
return;
} else if (Z_LVAL(modifiers->u.constant) == ZEND_ACC_FINAL) {
zend_error(E_COMPILE_ERROR, "Cannot use 'final' as method modifier");
return;
}
trait_alias = emalloc(sizeof(zend_trait_alias));
trait_alias->trait_method = (zend_trait_method_reference*)method_reference->u.op.ptr;
trait_alias->modifiers = Z_LVAL(modifiers->u.constant);
if (alias) {
trait_alias->alias = Z_STRVAL(alias->u.constant);
trait_alias->alias_len = Z_STRLEN(alias->u.constant);
@ -4491,12 +4314,13 @@ void zend_prepare_trait_alias(znode *result, znode *method_reference, znode *mod
}
trait_alias->function = NULL;
result->u.op.ptr = trait_alias;
zend_add_to_list(&ce->trait_aliases, trait_alias TSRMLS_CC);
}
/* }}} */
void zend_prepare_trait_precedence(znode *result, znode *method_reference, znode *trait_list TSRMLS_DC) /* {{{ */
void zend_add_trait_precedence(znode *method_reference, znode *trait_list TSRMLS_DC) /* {{{ */
{
zend_class_entry *ce = CG(active_class_entry);
zend_trait_precedence *trait_precedence = emalloc(sizeof(zend_trait_precedence));
trait_precedence->trait_method = (zend_trait_method_reference*)method_reference->u.op.ptr;
@ -4504,7 +4328,7 @@ void zend_prepare_trait_precedence(znode *result, znode *method_reference, znode
trait_precedence->function = NULL;
result->u.op.ptr = trait_precedence;
zend_add_to_list(&ce->trait_precedences, trait_precedence TSRMLS_CC);
}
/* }}} */
@ -4539,7 +4363,7 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const ze
}
return NULL;
} else {
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES))) {
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
zend_verify_abstract_class(ce TSRMLS_CC);
}
return ce;
@ -5109,7 +4933,7 @@ void zend_do_end_class_declaration(const znode *class_token, const znode *parent
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))
&& ((parent_token->op_type != IS_UNUSED) || (ce->num_interfaces > 0))) {
zend_verify_abstract_class(ce TSRMLS_CC);
if (ce->num_interfaces) {
if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) {
do_verify_abstract_class(TSRMLS_C);
}
}
@ -5159,9 +4983,10 @@ void zend_do_implements_interface(znode *interface_name TSRMLS_DC) /* {{{ */
}
/* }}} */
void zend_do_implements_trait(znode *trait_name TSRMLS_DC) /* {{{ */
void zend_do_use_trait(znode *trait_name TSRMLS_DC) /* {{{ */
{
zend_op *opline;
if ((CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE)) {
zend_error(E_COMPILE_ERROR,
"Cannot use traits inside of interfaces. %s is used in %s",
@ -5891,7 +5716,11 @@ void zend_add_to_list(void *result, void *item TSRMLS_DC) /* {{{ */
void** list = *(void**)result;
size_t n = 0;
while (list && list[n]) { n++; }
if (list) {
while (list[n]) {
n++;
}
}
list = erealloc(list, sizeof(void*) * (n+2));

View File

@ -207,8 +207,6 @@ typedef struct _zend_try_catch_element {
#define ZEND_ACC_RETURN_REFERENCE 0x4000000
#define ZEND_ACC_DONE_PASS_TWO 0x8000000
#define ZEND_ACC_ALIAS 0x10000000
char *zend_visibility_string(zend_uint fn_flags);
@ -509,16 +507,13 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
void zend_do_implements_interface(znode *interface_znode TSRMLS_DC);
/* Trait related functions */
void zend_add_trait_precedence(znode *precedence_znode TSRMLS_DC);
void zend_add_trait_alias(znode *alias_znode TSRMLS_DC);
void zend_do_use_trait(znode *trait_znode TSRMLS_DC);
void zend_prepare_reference(znode *result, znode *class_name, znode *method_name TSRMLS_DC);
void zend_add_trait_precedence(znode *method_reference, znode *trait_list TSRMLS_DC);
void zend_add_trait_alias(znode *method_reference, znode *modifiers, znode *alias TSRMLS_DC);
void zend_do_implements_trait(znode *interface_znode /*, znode* aliases */ TSRMLS_DC);
ZEND_API void zend_do_implement_trait(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC);
ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC);
void zend_prepare_trait_precedence(znode *result, znode *method_reference, znode *trait_list TSRMLS_DC);
void zend_prepare_reference(znode *result, znode *class_name, znode *method_name TSRMLS_DC);
void zend_prepare_trait_alias(znode *result, znode *method_reference, znode *modifiers, znode *alias TSRMLS_DC);
ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC);
void zend_do_early_binding(TSRMLS_D);

View File

@ -585,8 +585,8 @@ trait_use_statement:
;
trait_list:
fully_qualified_class_name { zend_do_implements_trait(&$1 TSRMLS_CC); }
| trait_list ',' fully_qualified_class_name { zend_do_implements_trait(&$3 TSRMLS_CC); }
fully_qualified_class_name { zend_do_use_trait(&$1 TSRMLS_CC); }
| trait_list ',' fully_qualified_class_name { zend_do_use_trait(&$3 TSRMLS_CC); }
;
trait_adaptations:
@ -605,12 +605,12 @@ non_empty_trait_adaptation_list:
;
trait_adaptation_statement:
trait_precedence ';' { zend_add_trait_precedence(&$1 TSRMLS_CC); }
| trait_alias ';' { zend_add_trait_alias(&$1 TSRMLS_CC); }
trait_precedence ';'
| trait_alias ';'
;
trait_precedence:
trait_method_reference_fully_qualified T_INSTEADOF trait_reference_list { zend_prepare_trait_precedence(&$$, &$1, &$3 TSRMLS_CC); }
trait_method_reference_fully_qualified T_INSTEADOF trait_reference_list { zend_add_trait_precedence(&$1, &$3 TSRMLS_CC); }
;
trait_reference_list:
@ -628,8 +628,8 @@ trait_method_reference_fully_qualified:
;
trait_alias:
trait_method_reference T_AS trait_modifiers T_STRING { zend_prepare_trait_alias(&$$, &$1, &$3, &$4 TSRMLS_CC); }
| trait_method_reference T_AS member_modifier { zend_prepare_trait_alias(&$$, &$1, &$3, NULL TSRMLS_CC); }
trait_method_reference T_AS trait_modifiers T_STRING { zend_add_trait_alias(&$1, &$3, &$4 TSRMLS_CC); }
| trait_method_reference T_AS member_modifier { zend_add_trait_alias(&$1, &$3, NULL TSRMLS_CC); }
;
trait_modifiers:

View File

@ -215,12 +215,6 @@ ZEND_API int zend_cleanup_class_data(zend_class_entry **pce TSRMLS_DC)
void _destroy_zend_class_traits_info(zend_class_entry *ce)
{
if (ce->num_traits > 0 && ce->traits) {
size_t i;
for (i = 0; i < ce->num_traits; i++) {
if (ce->traits[i]) {
destroy_zend_class(&ce->traits[i]);
}
}
efree(ce->traits);
}
@ -267,15 +261,6 @@ void _destroy_zend_class_traits_info(zend_class_entry *ce)
}
}
static int zend_clear_trait_method_name(zend_op_array *op_array TSRMLS_DC)
{
if (op_array->function_name && (op_array->fn_flags & ZEND_ACC_ALIAS) == 0) {
efree(op_array->function_name);
op_array->function_name = NULL;
}
return 0;
}
ZEND_API void destroy_zend_class(zend_class_entry **pce)
{
zend_class_entry *ce = *pce;
@ -307,10 +292,6 @@ ZEND_API void destroy_zend_class(zend_class_entry **pce)
}
zend_hash_destroy(&ce->properties_info);
str_efree(ce->name);
if ((ce->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
TSRMLS_FETCH();
zend_hash_apply(&ce->function_table, (apply_func_t)zend_clear_trait_method_name TSRMLS_CC);
}
zend_hash_destroy(&ce->function_table);
zend_hash_destroy(&ce->constants_table);
if (ce->num_interfaces > 0 && ce->interfaces) {
@ -400,7 +381,7 @@ ZEND_API void destroy_op_array(zend_op_array *op_array TSRMLS_DC)
}
efree(op_array->opcodes);
if (op_array->function_name && (op_array->fn_flags & ZEND_ACC_ALIAS) == 0) {
if (op_array->function_name) {
efree((char*)op_array->function_name);
}
if (op_array->doc_comment) {