From 81d287f6cfd275cb49602a95c5375070ac2c4838 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sun, 10 May 2026 08:37:38 -0700 Subject: [PATCH 1/3] [RFC] Allow `#[\Deprecated]` on interfaces --- .../validator_Deprecated.phpt | 38 ------------------- .../with_Deprecated.phpt | 8 ++++ .../deprecated/interfaces/basic.phpt | 34 +++++++++++++++++ .../extending_deprecated_interface.phpt | 31 +++++++++++++++ .../interfaces/implements_child.phpt | 15 ++++++++ .../implements_interface_and_child.phpt | 17 +++++++++ .../inherited_interface_implementation.phpt | 15 ++++++++ ...rited_manual_interface_implementation.phpt | 17 +++++++++ .../interfaces/throwing_error_handler.phpt | 23 +++++++++++ .../deprecated/interfaces/type_okay.phpt | 35 +++++++++++++++++ Zend/zend_attributes.c | 2 +- Zend/zend_execute.c | 37 ++++++++++++++++++ Zend/zend_execute.h | 2 + Zend/zend_inheritance.c | 11 ++++++ 14 files changed, 246 insertions(+), 39 deletions(-) create mode 100644 Zend/tests/attributes/deprecated/interfaces/basic.phpt create mode 100644 Zend/tests/attributes/deprecated/interfaces/extending_deprecated_interface.phpt create mode 100644 Zend/tests/attributes/deprecated/interfaces/implements_child.phpt create mode 100644 Zend/tests/attributes/deprecated/interfaces/implements_interface_and_child.phpt create mode 100644 Zend/tests/attributes/deprecated/interfaces/inherited_interface_implementation.phpt create mode 100644 Zend/tests/attributes/deprecated/interfaces/inherited_manual_interface_implementation.phpt create mode 100644 Zend/tests/attributes/deprecated/interfaces/throwing_error_handler.phpt create mode 100644 Zend/tests/attributes/deprecated/interfaces/type_okay.phpt diff --git a/Zend/tests/attributes/delayed_target_validation/validator_Deprecated.phpt b/Zend/tests/attributes/delayed_target_validation/validator_Deprecated.phpt index f5fedb4ee68e..1d34d8b06123 100644 --- a/Zend/tests/attributes/delayed_target_validation/validator_Deprecated.phpt +++ b/Zend/tests/attributes/delayed_target_validation/validator_Deprecated.phpt @@ -3,10 +3,6 @@ --FILE-- --EXPECTF-- ******************** -Interface [ interface DemoInterface ] { - @@ %s %d-%d - - - Constants [0] { - } - - - Static properties [0] { - } - - - Static methods [0] { - } - - - Properties [0] { - } - - - Methods [0] { - } -} - -array(2) { - [0]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(23) "DelayedTargetValidation" - } - [1]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(10) "Deprecated" - } -} -Error: Cannot apply #[\Deprecated] to interface DemoInterface -******************** Class [ class DemoClass ] { @@ %s %d-%d diff --git a/Zend/tests/attributes/delayed_target_validation/with_Deprecated.phpt b/Zend/tests/attributes/delayed_target_validation/with_Deprecated.phpt index e103f9d97389..b7a80c6d59b2 100644 --- a/Zend/tests/attributes/delayed_target_validation/with_Deprecated.phpt +++ b/Zend/tests/attributes/delayed_target_validation/with_Deprecated.phpt @@ -48,6 +48,12 @@ class WithDeprecatedTrait { use DeprecatedTrait; } +#[DelayedTargetValidation] +#[Deprecated] // Does something here +interface DeprecatedInterface {} + +class ImplementsDeprecatedInterface implements DeprecatedInterface {} + #[DelayedTargetValidation] #[Deprecated] // Does something here function demoFn() { @@ -70,6 +76,8 @@ var_dump(GLOBAL_CONST); ?> --EXPECTF-- Deprecated: Trait DeprecatedTrait used by WithDeprecatedTrait is deprecated in %s on line %d + +Deprecated: Interface DeprecatedInterface implemented by ImplementsDeprecatedInterface is deprecated in %s on line %d Got: example Deprecated: Method DemoClass::printVal() is deprecated in %s on line %d diff --git a/Zend/tests/attributes/deprecated/interfaces/basic.phpt b/Zend/tests/attributes/deprecated/interfaces/basic.phpt new file mode 100644 index 000000000000..68a5b16aced0 --- /dev/null +++ b/Zend/tests/attributes/deprecated/interfaces/basic.phpt @@ -0,0 +1,34 @@ +--TEST-- +#[\Deprecated]: Basic interface deprecation +--FILE-- + +--EXPECTF-- +Deprecated: Interface DemoInterface1 implemented by DemoClass is deprecated, please do not use in %s on line %d + +Deprecated: Interface DemoInterface2 implemented by DemoClass is deprecated since 2.7, will be removed in 3.0 in %s on line %d + +Deprecated: Interface DemoInterface3 implemented by DemoClass is deprecated, going away in %s on line %d + +Deprecated: Interface DemoInterface4 implemented by DemoClass is deprecated since 3.5 in %s on line %d diff --git a/Zend/tests/attributes/deprecated/interfaces/extending_deprecated_interface.phpt b/Zend/tests/attributes/deprecated/interfaces/extending_deprecated_interface.phpt new file mode 100644 index 000000000000..9da7c22d7f8c --- /dev/null +++ b/Zend/tests/attributes/deprecated/interfaces/extending_deprecated_interface.phpt @@ -0,0 +1,31 @@ +--TEST-- +#[\Deprecated]: Interfaces extending deprecated interfaces trigger warnings +--FILE-- + +--EXPECTF-- +Deprecated: Interface DemoInterface1 extended by ExtendsDemo1 is deprecated, please do not use in %s on line %d + +Deprecated: Interface DemoInterface2 extended by ExtendsDemo2 is deprecated since 2.7, will be removed in 3.0 in %s on line %d + +Deprecated: Interface DemoInterface3 extended by ExtendsDemo3 is deprecated, going away in %s on line %d + +Deprecated: Interface DemoInterface4 extended by ExtendsDemo4 is deprecated since 3.5 in %s on line %d diff --git a/Zend/tests/attributes/deprecated/interfaces/implements_child.phpt b/Zend/tests/attributes/deprecated/interfaces/implements_child.phpt new file mode 100644 index 000000000000..9f02087501b9 --- /dev/null +++ b/Zend/tests/attributes/deprecated/interfaces/implements_child.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Deprecated]: Implementing a child of a deprecated interface +--FILE-- + +--EXPECTF-- +Deprecated: Interface BaseInterface extended by ChildInterface is deprecated, please do not use in %s on line %d diff --git a/Zend/tests/attributes/deprecated/interfaces/implements_interface_and_child.phpt b/Zend/tests/attributes/deprecated/interfaces/implements_interface_and_child.phpt new file mode 100644 index 000000000000..a9b9edeb7a3d --- /dev/null +++ b/Zend/tests/attributes/deprecated/interfaces/implements_interface_and_child.phpt @@ -0,0 +1,17 @@ +--TEST-- +#[\Deprecated]: Implementing a deprecated interface and a child of it +--FILE-- + +--EXPECTF-- +Deprecated: Interface BaseInterface extended by ChildInterface is deprecated, please do not use in %s on line %d + +Deprecated: Interface BaseInterface implemented by DemoClass is deprecated, please do not use in %s on line %d diff --git a/Zend/tests/attributes/deprecated/interfaces/inherited_interface_implementation.phpt b/Zend/tests/attributes/deprecated/interfaces/inherited_interface_implementation.phpt new file mode 100644 index 000000000000..2334582756b2 --- /dev/null +++ b/Zend/tests/attributes/deprecated/interfaces/inherited_interface_implementation.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Deprecated]: Parent class implements deprecated interface, child class inherits +--FILE-- + +--EXPECTF-- +Deprecated: Interface DemoInterface implemented by Base is deprecated, please do not use in %s on line %d diff --git a/Zend/tests/attributes/deprecated/interfaces/inherited_manual_interface_implementation.phpt b/Zend/tests/attributes/deprecated/interfaces/inherited_manual_interface_implementation.phpt new file mode 100644 index 000000000000..b7540175e69e --- /dev/null +++ b/Zend/tests/attributes/deprecated/interfaces/inherited_manual_interface_implementation.phpt @@ -0,0 +1,17 @@ +--TEST-- +#[\Deprecated]: Parent class implements deprecated interface, child class inherits and also implements +--FILE-- + +--EXPECTF-- +Deprecated: Interface DemoInterface implemented by Base is deprecated, please do not use in %s on line %d + +Deprecated: Interface DemoInterface implemented by Child is deprecated, please do not use in %s on line %d diff --git a/Zend/tests/attributes/deprecated/interfaces/throwing_error_handler.phpt b/Zend/tests/attributes/deprecated/interfaces/throwing_error_handler.phpt new file mode 100644 index 000000000000..75c0af92abe8 --- /dev/null +++ b/Zend/tests/attributes/deprecated/interfaces/throwing_error_handler.phpt @@ -0,0 +1,23 @@ +--TEST-- +#[\Deprecated]: Deprecation converted to ErrorException does not break +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ErrorException: Interface DemoInterface implemented by DemoClass is deprecated in %s:%d +Stack trace: +#0 %s(%d): my_error_handler(16384, 'Interface DemoI...', '/usr/src/php/Ze...', 12) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/deprecated/interfaces/type_okay.phpt b/Zend/tests/attributes/deprecated/interfaces/type_okay.phpt new file mode 100644 index 000000000000..d9e558ff63c2 --- /dev/null +++ b/Zend/tests/attributes/deprecated/interfaces/type_okay.phpt @@ -0,0 +1,35 @@ +--TEST-- +#[\Deprecated]: Instanceof, type checking, etc. for deprecated interfaces are okay +--FILE-- + +--EXPECTF-- +Deprecated: Interface DemoInterface implemented by ImplementsDemo is deprecated, please do not use in %s on line %d +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 71f0d788e921..67b313984cfc 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -118,7 +118,7 @@ static zend_string *validate_deprecated( /* Being used for a method or something, validation does not apply */ return NULL; } - if (!(scope->ce_flags & ZEND_ACC_TRAIT)) { + if (!(scope->ce_flags & (ZEND_ACC_TRAIT|ZEND_ACC_INTERFACE))) { const char *type = zend_get_object_type_case(scope, false); return zend_strpprintf(0, "Cannot apply #[\\Deprecated] to %s %s", type, ZSTR_VAL(scope->name)); } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4253037fda52..c3b476efd632 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2032,6 +2032,43 @@ ZEND_API ZEND_COLD void zend_use_of_deprecated_trait( zend_string_release(message_suffix); } +static ZEND_COLD void zend_deprecated_interface_action( + zend_class_entry *interface, + const zend_string *by, + const char *action +) { + zend_string *message_suffix = ZSTR_EMPTY_ALLOC(); + + if (get_deprecation_suffix_from_attribute(interface->attributes, interface, &message_suffix) == FAILURE) { + return; + } + + int code = interface->type == ZEND_INTERNAL_CLASS ? E_DEPRECATED : E_USER_DEPRECATED; + + zend_error_unchecked(code, "Interface %s %s by %s is deprecated%S", + ZSTR_VAL(interface->name), + action, + ZSTR_VAL(by), + message_suffix + ); + + zend_string_release(message_suffix); +} + +ZEND_API ZEND_COLD void zend_implementation_of_deprecated_interface( + zend_class_entry *interface, + const zend_string *implemented_by +) { + zend_deprecated_interface_action(interface, implemented_by, "implemented"); +} + +ZEND_API ZEND_COLD void zend_extending_deprecated_interface( + zend_class_entry *interface, + const zend_string *extended_by +) { + zend_deprecated_interface_action(interface, extended_by, "extended"); +} + ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void) { zend_error(E_DEPRECATED, "Automatic conversion of false to array is deprecated"); diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index ba48b19bcfe1..39f4f2e64509 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -66,6 +66,8 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_functio ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_class_constant *c, const zend_string *constant_name); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_constant(const zend_constant *c, const zend_string *constant_name); ZEND_API ZEND_COLD void zend_use_of_deprecated_trait(zend_class_entry *trait, const zend_string *used_by); +ZEND_API ZEND_COLD void zend_implementation_of_deprecated_interface(zend_class_entry *interface, const zend_string *implemented_by); +ZEND_API ZEND_COLD void zend_extending_deprecated_interface(zend_class_entry *interface, const zend_string *extended_by); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void); ZEND_COLD void ZEND_FASTCALL zend_param_must_be_ref(const zend_function *func, uint32_t arg_num); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_use_resource_as_offset(const zval *dim); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index d70d08f5f1c4..7ed3ccc41fc3 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -3557,6 +3557,17 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string free_alloca(traits_and_interfaces, use_heap); return NULL; } + if (UNEXPECTED(iface->ce_flags & ZEND_ACC_DEPRECATED)) { + if (ce->ce_flags & ZEND_ACC_INTERFACE) { + zend_extending_deprecated_interface(iface, ce->name); + } else { + zend_implementation_of_deprecated_interface(iface, ce->name); + } + if (UNEXPECTED(EG(exception))) { + free_alloca(traits_and_interfaces, use_heap); + return NULL; + } + } traits_and_interfaces[ce->num_traits + i] = iface; if (iface) { UPDATE_IS_CACHEABLE(iface); From 99348433712396ae47efe2f3eaf13906866fd67f Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sun, 10 May 2026 08:53:54 -0700 Subject: [PATCH 2/3] Test fixes --- .../attributes/deprecated/error_on_interface.phpt | 11 ----------- .../deprecated/interfaces/throwing_error_handler.phpt | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 Zend/tests/attributes/deprecated/error_on_interface.phpt diff --git a/Zend/tests/attributes/deprecated/error_on_interface.phpt b/Zend/tests/attributes/deprecated/error_on_interface.phpt deleted file mode 100644 index 595181a7cecd..000000000000 --- a/Zend/tests/attributes/deprecated/error_on_interface.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -#[\Deprecated]: Using on an interface ---FILE-- - ---EXPECTF-- -Fatal error: Cannot apply #[\Deprecated] to interface Demo in %s on line %d diff --git a/Zend/tests/attributes/deprecated/interfaces/throwing_error_handler.phpt b/Zend/tests/attributes/deprecated/interfaces/throwing_error_handler.phpt index 75c0af92abe8..2cb6df2a69c7 100644 --- a/Zend/tests/attributes/deprecated/interfaces/throwing_error_handler.phpt +++ b/Zend/tests/attributes/deprecated/interfaces/throwing_error_handler.phpt @@ -18,6 +18,6 @@ class DemoClass implements DemoInterface {} --EXPECTF-- Fatal error: Uncaught ErrorException: Interface DemoInterface implemented by DemoClass is deprecated in %s:%d Stack trace: -#0 %s(%d): my_error_handler(16384, 'Interface DemoI...', '/usr/src/php/Ze...', 12) +#0 %s(%d): my_error_handler(16384, 'Interface DemoI...', '%s', 12) #1 {main} thrown in %s on line %d From a5010099d2b1d300d7f76add94b06b64f6923e97 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sun, 10 May 2026 09:19:17 -0700 Subject: [PATCH 3/3] Tool comparison; `interface` is a macro on windows??? --- .../interfaces/tool_comparison.phpt | 25 +++++++++++++++++++ Zend/zend_execute.c | 16 ++++++------ Zend/zend_execute.h | 4 +-- 3 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 Zend/tests/attributes/deprecated/interfaces/tool_comparison.phpt diff --git a/Zend/tests/attributes/deprecated/interfaces/tool_comparison.phpt b/Zend/tests/attributes/deprecated/interfaces/tool_comparison.phpt new file mode 100644 index 000000000000..33f94cc251e8 --- /dev/null +++ b/Zend/tests/attributes/deprecated/interfaces/tool_comparison.phpt @@ -0,0 +1,25 @@ +--TEST-- +#[\Deprecated]: Interface deprecation RFC example +--FILE-- + +--EXPECTF-- +Deprecated: Interface BaseInterface extended by ChildInterface is deprecated in %s on line %d + +Deprecated: Interface BaseInterface implemented by ImplementsBase is deprecated in %s on line %d + +Deprecated: Interface BaseInterface implemented by ImplementsBoth is deprecated in %s on line %d diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index c3b476efd632..3741a8d72c42 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2033,20 +2033,20 @@ ZEND_API ZEND_COLD void zend_use_of_deprecated_trait( } static ZEND_COLD void zend_deprecated_interface_action( - zend_class_entry *interface, + zend_class_entry *iface, const zend_string *by, const char *action ) { zend_string *message_suffix = ZSTR_EMPTY_ALLOC(); - if (get_deprecation_suffix_from_attribute(interface->attributes, interface, &message_suffix) == FAILURE) { + if (get_deprecation_suffix_from_attribute(iface->attributes, iface, &message_suffix) == FAILURE) { return; } - int code = interface->type == ZEND_INTERNAL_CLASS ? E_DEPRECATED : E_USER_DEPRECATED; + int code = iface->type == ZEND_INTERNAL_CLASS ? E_DEPRECATED : E_USER_DEPRECATED; zend_error_unchecked(code, "Interface %s %s by %s is deprecated%S", - ZSTR_VAL(interface->name), + ZSTR_VAL(iface->name), action, ZSTR_VAL(by), message_suffix @@ -2056,17 +2056,17 @@ static ZEND_COLD void zend_deprecated_interface_action( } ZEND_API ZEND_COLD void zend_implementation_of_deprecated_interface( - zend_class_entry *interface, + zend_class_entry *iface, const zend_string *implemented_by ) { - zend_deprecated_interface_action(interface, implemented_by, "implemented"); + zend_deprecated_interface_action(iface, implemented_by, "implemented"); } ZEND_API ZEND_COLD void zend_extending_deprecated_interface( - zend_class_entry *interface, + zend_class_entry *iface, const zend_string *extended_by ) { - zend_deprecated_interface_action(interface, extended_by, "extended"); + zend_deprecated_interface_action(iface, extended_by, "extended"); } ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void) diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 39f4f2e64509..ae6bceb1eb73 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -66,8 +66,8 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_functio ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_class_constant *c, const zend_string *constant_name); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_constant(const zend_constant *c, const zend_string *constant_name); ZEND_API ZEND_COLD void zend_use_of_deprecated_trait(zend_class_entry *trait, const zend_string *used_by); -ZEND_API ZEND_COLD void zend_implementation_of_deprecated_interface(zend_class_entry *interface, const zend_string *implemented_by); -ZEND_API ZEND_COLD void zend_extending_deprecated_interface(zend_class_entry *interface, const zend_string *extended_by); +ZEND_API ZEND_COLD void zend_implementation_of_deprecated_interface(zend_class_entry *iface, const zend_string *implemented_by); +ZEND_API ZEND_COLD void zend_extending_deprecated_interface(zend_class_entry *iface, const zend_string *extended_by); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void); ZEND_COLD void ZEND_FASTCALL zend_param_must_be_ref(const zend_function *func, uint32_t arg_num); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_use_resource_as_offset(const zval *dim);