Skip to content

[RFC] Allow #[\Deprecated] on traits #19045

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ Constants listed in valid targets when used wrong (internal attribute)
<?php

#[Deprecated]
class Example {}
function demo(
#[Deprecated] $v
) {}

?>
--EXPECTF--
Fatal error: Attribute "Deprecated" cannot target class (allowed targets: function, method, class constant, constant) in %s on line %d
Fatal error: Attribute "Deprecated" cannot target parameter (allowed targets: class, function, method, class constant, constant) in %s on line %d
15 changes: 15 additions & 0 deletions Zend/tests/attributes/deprecated/traits/basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
#[\Deprecated]: Basic trait deprecation
--FILE--
<?php

#[\Deprecated]
trait DemoTrait {}

class DemoClass {
use DemoTrait;
}

?>
--EXPECTF--
Deprecated: Trait DemoTrait used by DemoClass is deprecated in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/attributes/deprecated/traits/class_not_trait.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
#[\Deprecated]: Using on a class
--FILE--
<?php

#[\Deprecated]
class Demo {}

?>
--EXPECTF--
Fatal error: Cannot apply #[Deprecated] to class Demo in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/attributes/deprecated/traits/enum_not_trait.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
#[\Deprecated]: Using on an enum
--FILE--
<?php

#[\Deprecated]
enum Demo {}

?>
--EXPECTF--
Fatal error: Cannot apply #[Deprecated] to enum Demo in %s on line %d
25 changes: 25 additions & 0 deletions Zend/tests/attributes/deprecated/traits/exception.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
#[\Deprecated]: Deprecation converted to ErrorException does not break
--FILE--
<?php

function my_error_handler(int $errno, string $errstr, ?string $errfile = null, ?int $errline = null) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
}

set_error_handler('my_error_handler');

#[\Deprecated]
trait DemoTrait {}

class DemoClass {
use DemoTrait;
}

?>
--EXPECTF--
Fatal error: Uncaught ErrorException: Trait DemoTrait used by DemoClass is deprecated in %s:%d
Stack trace:
#0 %s: my_error_handler(16384, 'Trait DemoTrait...', '%s', %d)
#1 {main}
thrown in %s on line %d
18 changes: 18 additions & 0 deletions Zend/tests/attributes/deprecated/traits/inheritance.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
#[\Deprecated]: Deprecated traits only apply to direct use, not inheritance
--FILE--
<?php

#[\Deprecated]
trait DemoTrait {}

class DemoClass {
use DemoTrait;
}

class ChildClass extends DemoClass {
}

?>
--EXPECTF--
Deprecated: Trait DemoTrait used by DemoClass is deprecated in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/attributes/deprecated/traits/interface_not_trait.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
#[\Deprecated]: Using on an interface
--FILE--
<?php

#[\Deprecated]
interface Demo {}

?>
--EXPECTF--
Fatal error: Cannot apply #[Deprecated] to interface Demo in %s on line %d
33 changes: 33 additions & 0 deletions Zend/tests/attributes/deprecated/traits/messages.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
#[\Deprecated]: Messages for deprecated traits
--FILE--
<?php

#[\Deprecated("please do not use")]
trait DemoTrait1 {}

#[\Deprecated("will be removed in 3.0", since: "2.7")]
trait DemoTrait2 {}

#[\Deprecated(message: "going away")]
trait DemoTrait3 {}

#[\Deprecated(since: "3.5")]
trait DemoTrait4 {}

class DemoClass {
use DemoTrait1;
use DemoTrait2;
use DemoTrait3;
use DemoTrait4;
}

?>
--EXPECTF--
Deprecated: Trait DemoTrait1 used by DemoClass is deprecated, please do not use in %s on line %d

Deprecated: Trait DemoTrait2 used by DemoClass is deprecated since 2.7, will be removed in 3.0 in %s on line %d

Deprecated: Trait DemoTrait3 used by DemoClass is deprecated, going away in %s on line %d

Deprecated: Trait DemoTrait4 used by DemoClass is deprecated since 3.5 in %s on line %d
29 changes: 29 additions & 0 deletions Zend/zend_attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,34 @@ static void validate_allow_dynamic_properties(
scope->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;
}

static void validate_deprecated(
zend_attribute *attr,
uint32_t target,
zend_class_entry *scope
) {
if (target != ZEND_ATTRIBUTE_TARGET_CLASS) {
// Being used for a method or something, validation does not apply
return;
}
if (scope->ce_flags & ZEND_ACC_TRAIT) {
scope->ce_flags |= ZEND_ACC_DEPRECATED;
return;
}

const char *type = "class";
if (scope->ce_flags & ZEND_ACC_INTERFACE) {
type = "interface";
} else if (scope->ce_flags & ZEND_ACC_ENUM) {
type = "enum";
}
zend_error_noreturn(
E_ERROR,
"Cannot apply #[Deprecated] to %s %s",
type,
ZSTR_VAL(scope->name)
);
}

ZEND_METHOD(Attribute, __construct)
{
zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL;
Expand Down Expand Up @@ -545,6 +573,7 @@ void zend_register_attribute_ce(void)

zend_ce_deprecated = register_class_Deprecated();
attr = zend_mark_internal_attribute(zend_ce_deprecated);
attr->validator = validate_deprecated;

zend_ce_nodiscard = register_class_NoDiscard();
attr = zend_mark_internal_attribute(zend_ce_nodiscard);
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_attributes.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public function __construct() {}
/**
* @strict-properties
*/
#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION|Attribute::TARGET_CLASS_CONSTANT|Attribute::TARGET_CONSTANT)]
#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION|Attribute::TARGET_CLASS_CONSTANT|Attribute::TARGET_CONSTANT|Attribute::TARGET_CLASS)]
final class Deprecated
{
public readonly ?string $message;
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_attributes_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ typedef struct _zend_oparray_context {
/* or IS_CONSTANT_VISITED_MARK | | | */
#define ZEND_CLASS_CONST_IS_CASE (1 << 6) /* | | | X */
/* | | | */
/* deprecation flag | | | */
#define ZEND_ACC_DEPRECATED (1 << 11) /* X | X | | X */
/* | | | */
/* Property Flags (unused: 13...) | | | */
/* =========== | | | */
/* | | | */
Expand All @@ -267,7 +270,7 @@ typedef struct _zend_oparray_context {
#define ZEND_ACC_PROTECTED_SET (1 << 11) /* | | X | */
#define ZEND_ACC_PRIVATE_SET (1 << 12) /* | | X | */
/* | | | */
/* Class Flags (unused: 30,31) | | | */
/* Class Flags (unused: 31) | | | */
/* =========== | | | */
/* | | | */
/* Special class types | | | */
Expand All @@ -285,7 +288,7 @@ typedef struct _zend_oparray_context {
/* | | | */
/* Class has magic methods __get/__set/__unset/ | | | */
/* __isset that use guards | | | */
#define ZEND_ACC_USE_GUARDS (1 << 11) /* X | | | */
#define ZEND_ACC_USE_GUARDS (1 << 30) /* X | | | */
/* | | | */
/* Class constants updated | | | */
#define ZEND_ACC_CONSTANTS_UPDATED (1 << 12) /* X | | | */
Expand Down Expand Up @@ -336,9 +339,6 @@ typedef struct _zend_oparray_context {
/* Function Flags (unused: 30) | | | */
/* ============== | | | */
/* | | | */
/* deprecation flag | | | */
#define ZEND_ACC_DEPRECATED (1 << 11) /* | X | | X */
/* | | | */
/* Function returning by reference | | | */
#define ZEND_ACC_RETURN_REFERENCE (1 << 12) /* | X | | */
/* | | | */
Expand Down
21 changes: 21 additions & 0 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -2006,6 +2006,27 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_constant(const zend_consta
zend_string_release(message_suffix);
}

ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_trait(
zend_class_entry *trait,
const zend_string *used_by
) {
zend_string *message_suffix = ZSTR_EMPTY_ALLOC();

if (get_deprecation_suffix_from_attribute(trait->attributes, trait, &message_suffix) == FAILURE) {
return;
}

int code = trait->type == ZEND_INTERNAL_CLASS ? E_DEPRECATED : E_USER_DEPRECATED;

zend_error_unchecked(code, "Trait %s used by %s is deprecated%S",
ZSTR_VAL(trait->name),
ZSTR_VAL(used_by),
message_suffix
);

zend_string_release(message_suffix);
}

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");
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_functi
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_function *fbc);
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_FASTCALL zend_deprecated_trait(zend_class_entry *trait, const zend_string *used_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);
Expand Down
7 changes: 7 additions & 0 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -3507,6 +3507,13 @@ 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(trait->ce_flags & ZEND_ACC_DEPRECATED)) {
zend_deprecated_trait(trait, ce->name);
if (UNEXPECTED(EG(exception))) {
free_alloca(traits_and_interfaces, use_heap);
return NULL;
}
}
for (j = 0; j < i; j++) {
if (traits_and_interfaces[j] == trait) {
/* skip duplications */
Expand Down