diff --git a/Zend/tests/return_types/036.phpt b/Zend/tests/return_types/036.phpt index 2f0847f21b3..178873e816b 100644 --- a/Zend/tests/return_types/036.phpt +++ b/Zend/tests/return_types/036.phpt @@ -8,4 +8,4 @@ class Foo { } ?> --EXPECTF-- -Fatal error: Declaration of Foo::__toString(): bool must be compatible with Stringable::__toString(): string in %s on line %d +Fatal error: Foo::__toString(): Return type must be string when declared in %s on line %d diff --git a/Zend/tests/stringable_trait.phpt b/Zend/tests/stringable_trait.phpt index 32aaa9fc609..623a2069eda 100644 --- a/Zend/tests/stringable_trait.phpt +++ b/Zend/tests/stringable_trait.phpt @@ -8,13 +8,31 @@ trait T { return "ok"; } } +trait T2 { + use T; +} class C { use T; } +class C2 { + use T2; +} var_dump(new C instanceof Stringable); +var_dump(new C2 instanceof Stringable); + +// The traits themselves should not implement Stringable -- traits cannot implement interfaces. +$rc = new ReflectionClass(T::class); +var_dump($rc->getInterfaceNames()); +$rc = new ReflectionClass(T2::class); +var_dump($rc->getInterfaceNames()); ?> --EXPECT-- bool(true) +bool(true) +array(0) { +} +array(0) { +} diff --git a/Zend/tests/stringable_trait_invalid.phpt b/Zend/tests/stringable_trait_invalid.phpt index cdd6980b88a..e2492e9f7d3 100644 --- a/Zend/tests/stringable_trait_invalid.phpt +++ b/Zend/tests/stringable_trait_invalid.phpt @@ -9,12 +9,6 @@ trait T { } } -class C { - use T; -} - -var_dump(new C instanceof Stringable); - ?> --EXPECTF-- -Fatal error: Declaration of T::__toString(): int must be compatible with Stringable::__toString(): string in %s on line %d +Fatal error: T::__toString(): Return type must be string when declared in %s on line %d diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5f003c9e25d..66d29f747a6 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2433,6 +2433,11 @@ static void zend_check_magic_method_return_type(const zend_class_entry *ce, cons return; } + if (ZEND_TYPE_PURE_MASK(fptr->common.arg_info[-1].type) & MAY_BE_NEVER) { + /* It is always legal to specify the never type. */ + return; + } + bool is_complex_type = ZEND_TYPE_IS_COMPLEX(fptr->common.arg_info[-1].type); uint32_t extra_types = ZEND_TYPE_PURE_MASK(fptr->common.arg_info[-1].type) & ~return_type; if (extra_types & MAY_BE_STATIC) { @@ -2541,6 +2546,7 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_STRING); } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d700a0fd557..f3fa1f3df48 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7070,7 +7070,8 @@ static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string } zend_add_magic_method(ce, (zend_function *) op_array, lcname); - if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) { + if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME) + && !(ce->ce_flags & ZEND_ACC_TRAIT)) { add_stringable_interface(ce); } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 894bf6bd01a..b30bb523d36 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2902,7 +2902,8 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string /* Normally Stringable is added during compilation. However, if it is imported from a trait, * we need to explicilty add the interface here. */ - if (ce->__tostring && !zend_class_implements_interface(ce, zend_ce_stringable)) { + if (ce->__tostring && !(ce->ce_flags & ZEND_ACC_TRAIT) + && !zend_class_implements_interface(ce, zend_ce_stringable)) { ZEND_ASSERT(ce->__tostring->common.fn_flags & ZEND_ACC_TRAIT_CLONE); ce->ce_flags |= ZEND_ACC_RESOLVED_INTERFACES; ce->num_interfaces++;