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 fbfdf0d0eea..9d8fef90341 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2233,6 +2233,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 2892fcc589a..c8fbf745228 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6817,7 +6817,8 @@ zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, } 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 ffff831fe3b..4d7a674c693 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2487,7 +2487,8 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa /* 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++;