diff --git a/Zend/tests/traits/bug61998.phpt b/Zend/tests/traits/bug61998.phpt new file mode 100644 index 000000000000..3528a75cbc9f --- /dev/null +++ b/Zend/tests/traits/bug61998.phpt @@ -0,0 +1,49 @@ +--TEST-- +Bug #61998 (Crash when declare trait after class if user defined the same method as aliased one) +--FILE-- +func(); +$class2->func(); +$class3->func(); +?> +==DONE== +--EXPECT-- +From Class1::func +From Class2::func +From trait T +==DONE== diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 602b6004100b..7ecad103ed2a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3832,11 +3832,21 @@ static int zend_traits_merge_functions_to_class(zend_function *fn TSRMLS_DC, int } 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); + /* User defined a function with the same name, add a reference to the original + trait function to avoid function name memory releasing problem */ + char *tmp_fn_name = emalloc(hash_key->nKeyLength + 1); + tmp_fn_name[0] = '\0'; + snprintf(tmp_fn_name + 1, hash_key->nKeyLength, "%s", hash_key->arKey); + fn_copy = *fn; + function_add_ref(&fn_copy); + + if (zend_hash_add(&ce->function_table, tmp_fn_name, hash_key->nKeyLength + 1, &fn_copy, sizeof(zend_function), NULL)==FAILURE) { + zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because failure occured during updating class method table", hash_key->arKey); + } + efree(tmp_fn_name); } + zend_function_dtor(fn); return ZEND_HASH_APPLY_REMOVE; } @@ -3869,11 +3879,9 @@ static int zend_traits_copy_functions(zend_function *fn TSRMLS_DC, int num_args, && (zend_binary_strcasecmp(aliases[i]->trait_method->method_name, aliases[i]->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 its refcount - greater than 1, classes are always destoyed in reverse order - and trait is declared early than this class */ + fn_copy.common.function_name = aliases[i]->alias; - + /* if it is 0, no modifieres has been changed */ if (aliases[i]->modifiers) { fn_copy.common.fn_flags = aliases[i]->modifiers; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 1cf65cee164e..31a0c08bc796 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3659,7 +3659,10 @@ static int _addmethod_va(zend_function *mptr TSRMLS_DC, int num_args, va_list ar long filter = va_arg(args, long); zval *obj = va_arg(args, zval *); - _addmethod(mptr, ce, retval, filter, obj TSRMLS_CC); + if (hash_key->nKeyLength != 0 && hash_key->arKey[0] != '\0') { + _addmethod(mptr, ce, retval, filter, obj TSRMLS_CC); + } + return ZEND_HASH_APPLY_KEEP; } /* }}} */ diff --git a/ext/reflection/tests/ReflectionClass_getMethods_004.phpt b/ext/reflection/tests/ReflectionClass_getMethods_004.phpt new file mode 100644 index 000000000000..571735e24511 --- /dev/null +++ b/ext/reflection/tests/ReflectionClass_getMethods_004.phpt @@ -0,0 +1,87 @@ +--TEST-- +ReflectionClass::getMethods() with trait +--FILE-- +getMethods()); + +echo "Class2:\n"; +$rc2 = new ReflectionClass("Class2"); +var_dump($rc2->getMethods()); + +echo "Class3:\n"; +$rc3 = new ReflectionClass("Class3"); +var_dump($rc3->getMethods()); +--EXPECTF-- +Class1: +array(2) { + [0]=> + &object(ReflectionMethod)#%d (2) { + ["name"]=> + string(4) "func" + ["class"]=> + string(6) "Class1" + } + [1]=> + &object(ReflectionMethod)#%d (2) { + ["name"]=> + string(7) "newFunc" + ["class"]=> + string(6) "Class1" + } +} +Class2: +array(2) { + [0]=> + &object(ReflectionMethod)#%d (2) { + ["name"]=> + string(8) "newFunc2" + ["class"]=> + string(6) "Class2" + } + [1]=> + &object(ReflectionMethod)#%d (2) { + ["name"]=> + string(4) "func" + ["class"]=> + string(6) "Class2" + } +} +Class3: +array(1) { + [0]=> + &object(ReflectionMethod)#%d (2) { + ["name"]=> + string(4) "func" + ["class"]=> + string(6) "Class3" + } +}