Skip to content

Commit 0721ddc

Browse files
committed
Adapt patch to current master
1 parent 56d1cc7 commit 0721ddc

9 files changed

+237
-6
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
#[Deprecated] attribute
3+
--FILE--
4+
<?php
5+
6+
error_reporting(E_ALL | E_DEPRECATED);
7+
ini_set('display_errors', true);
8+
9+
#[Deprecated]
10+
function test() {
11+
}
12+
13+
#[Deprecated("use test() instead")]
14+
function test2() {
15+
}
16+
17+
class Clazz {
18+
#[Deprecated]
19+
function test() {
20+
}
21+
22+
#[Deprecated("use test() instead")]
23+
function test2() {
24+
}
25+
}
26+
27+
test();
28+
test2();
29+
call_user_func("test");
30+
31+
$cls = new Clazz();
32+
$cls->test();
33+
$cls->test2();
34+
35+
call_user_func([$cls, "test"]);
36+
--EXPECTF--
37+
Deprecated: Function test() is deprecated in %s
38+
39+
Deprecated: Function test2() is deprecated, use test() instead in %s
40+
41+
Deprecated: Function test() is deprecated in %s
42+
43+
Deprecated: Method Clazz::test() is deprecated in %s
44+
45+
Deprecated: Method Clazz::test2() is deprecated, use test() instead in %s
46+
47+
Deprecated: Method Clazz::test() is deprecated in %s
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
#[Deprecated] attribute
3+
--FILE--
4+
<?php
5+
6+
#[Deprecated()]
7+
function test() {
8+
}
9+
10+
#[Deprecated("use test() instead")]
11+
function test2() {
12+
}
13+
14+
$reflection = new ReflectionFunction('test');
15+
var_dump($reflection->getAttributes()[0]->newInstance());
16+
17+
$reflection = new ReflectionFunction('test2');
18+
var_dump($reflection->getAttributes()[0]->newInstance());
19+
20+
--EXPECTF--
21+
object(Deprecated)#3 (1) {
22+
["message"]=>
23+
NULL
24+
}
25+
object(Deprecated)#2 (1) {
26+
["message"]=>
27+
string(18) "use test() instead"
28+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
#[Deprecated] attribute with wrong type
3+
--FILE--
4+
<?php
5+
6+
#[Deprecated(1234)]
7+
function test() {
8+
}
9+
10+
11+
--EXPECTF--
12+
Fatal error: Deprecated::__construct: Argument #1 ($message) must be of type string, int given in %s

Zend/zend_attributes.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ ZEND_API zend_class_entry *zend_ce_return_type_will_change_attribute;
2929
ZEND_API zend_class_entry *zend_ce_allow_dynamic_properties;
3030
ZEND_API zend_class_entry *zend_ce_sensitive_parameter;
3131
ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value;
32+
ZEND_API zend_class_entry *zend_ce_deprecated_attribute;
3233

3334
static zend_object_handlers attributes_object_handlers_sensitive_parameter_value;
3435

@@ -79,6 +80,32 @@ static void validate_allow_dynamic_properties(
7980
scope->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;
8081
}
8182

83+
void validate_deprecated_attribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope)
84+
{
85+
// TODO: More proper signature validation: Too many args, incorrect arg names.
86+
if (attr->argc == 1) {
87+
zval message;
88+
89+
/* As this is run in the middle of compilation, fetch the attribute value without
90+
* specifying a scope. The class is not fully linked yet, and we may seen an
91+
* inconsistent state. */
92+
if (FAILURE == zend_get_attribute_value(&message, attr, 0, NULL)) {
93+
return;
94+
}
95+
96+
if (Z_TYPE(message) != IS_STRING) {
97+
zval_ptr_dtor(&message);
98+
99+
zend_error_noreturn(E_COMPILE_ERROR,
100+
"Deprecated::__construct: Argument #1 ($message) must be of type string, %s given",
101+
zend_zval_type_name(&message)
102+
);
103+
}
104+
105+
zval_ptr_dtor(&message);
106+
}
107+
}
108+
82109
ZEND_METHOD(Attribute, __construct)
83110
{
84111
zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL;
@@ -136,6 +163,22 @@ static HashTable *attributes_sensitive_parameter_value_get_properties_for(zend_o
136163
return NULL;
137164
}
138165

166+
ZEND_METHOD(Deprecated, __construct)
167+
{
168+
zend_string *message = NULL;
169+
170+
ZEND_PARSE_PARAMETERS_START(0, 1)
171+
Z_PARAM_OPTIONAL
172+
Z_PARAM_STR_OR_NULL(message)
173+
ZEND_PARSE_PARAMETERS_END();
174+
175+
if (message) {
176+
ZVAL_STR(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), message);
177+
} else {
178+
ZVAL_NULL(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0));
179+
}
180+
}
181+
139182
static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
140183
{
141184
if (attributes) {
@@ -371,6 +414,10 @@ void zend_register_attribute_ce(void)
371414
/* This is not an actual attribute, thus the zend_mark_internal_attribute() call is missing. */
372415
zend_ce_sensitive_parameter_value = register_class_SensitiveParameterValue();
373416
zend_ce_sensitive_parameter_value->default_object_handlers = &attributes_object_handlers_sensitive_parameter_value;
417+
418+
zend_ce_deprecated_attribute = register_class_Deprecated();
419+
attr = zend_mark_internal_attribiute(zend_ce_deprecated_properties);
420+
attr->validator = validate_deprecated_attribute;
374421
}
375422

376423
void zend_attributes_shutdown(void)

Zend/zend_attributes.stub.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,12 @@ public function getValue(): mixed {}
8686

8787
public function __debugInfo(): array {}
8888
}
89+
90+
/**
91+
* @strict-properties
92+
*/
93+
#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION)]
94+
final class Deprecated
95+
{
96+
public function __construct(?string $message = null) {}
97+
}

Zend/zend_compile.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7435,6 +7435,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel)
74357435
zend_ast *return_type_ast = decl->child[3];
74367436
bool is_method = decl->kind == ZEND_AST_METHOD;
74377437
zend_string *lcname;
7438+
zend_attribute *deprecated;
74387439

74397440
zend_class_entry *orig_class_entry = CG(active_class_entry);
74407441
zend_op_array *orig_op_array = CG(active_op_array);
@@ -7520,6 +7521,12 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel)
75207521
zend_compile_closure_uses(uses_ast);
75217522
}
75227523

7524+
deprecated = zend_get_attribute_str(op_array->attributes, "deprecated", sizeof("deprecated")-1);
7525+
7526+
if (deprecated && stmt_ast != NULL) {
7527+
op_array->fn_flags |= ZEND_ACC_DEPRECATED;
7528+
}
7529+
75237530
if (ast->kind == ZEND_AST_ARROW_FUNC) {
75247531
bool needs_return = true;
75257532
if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {

Zend/zend_execute.c

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "zend_observer.h"
4444
#include "zend_system_id.h"
4545
#include "zend_call_stack.h"
46+
#include "zend_attributes.h"
4647
#include "Optimizer/zend_func_info.h"
4748

4849
/* Virtual current working directory support */
@@ -1719,13 +1720,45 @@ ZEND_API ZEND_COLD void zend_wrong_string_offset_error(void)
17191720

17201721
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc)
17211722
{
1722-
if (fbc->common.scope) {
1723-
zend_error(E_DEPRECATED, "Method %s::%s() is deprecated",
1724-
ZSTR_VAL(fbc->common.scope->name),
1725-
ZSTR_VAL(fbc->common.function_name)
1726-
);
1723+
zend_attribute *deprecated;
1724+
zend_string *message_suffix = NULL;
1725+
1726+
if (fbc->common.attributes != NULL) {
1727+
deprecated = zend_get_attribute_str(fbc->common.attributes, "deprecated", sizeof("deprecated")-1);
1728+
1729+
if (deprecated->argc >= 1) {
1730+
zval message;
1731+
1732+
if (FAILURE != zend_get_attribute_value(&message, deprecated, 0, fbc->common.scope)) {
1733+
message_suffix = Z_STR(message);
1734+
}
1735+
}
1736+
}
1737+
1738+
if (message_suffix != NULL) {
1739+
if (fbc->common.scope) {
1740+
zend_error(E_DEPRECATED, "Method %s::%s() is deprecated, %s",
1741+
ZSTR_VAL(fbc->common.scope->name),
1742+
ZSTR_VAL(fbc->common.function_name),
1743+
ZSTR_VAL(message_suffix)
1744+
);
1745+
} else {
1746+
zend_error(E_DEPRECATED, "Function %s() is deprecated, %s",
1747+
ZSTR_VAL(fbc->common.function_name),
1748+
ZSTR_VAL(message_suffix)
1749+
);
1750+
}
1751+
1752+
zend_string_release(message_suffix);
17271753
} else {
1728-
zend_error(E_DEPRECATED, "Function %s() is deprecated", ZSTR_VAL(fbc->common.function_name));
1754+
if (fbc->common.scope) {
1755+
zend_error(E_DEPRECATED, "Method %s::%s() is deprecated",
1756+
ZSTR_VAL(fbc->common.scope->name),
1757+
ZSTR_VAL(fbc->common.function_name)
1758+
);
1759+
} else {
1760+
zend_error(E_DEPRECATED, "Function %s() is deprecated", ZSTR_VAL(fbc->common.function_name));
1761+
}
17291762
}
17301763
}
17311764

Zend/zend_vm_def.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4023,6 +4023,10 @@ ZEND_VM_HOT_HANDLER(130, ZEND_DO_UCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
40234023
zend_function *fbc = call->func;
40244024
zval *ret;
40254025

4026+
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
4027+
zend_deprecated_function(fbc);
4028+
}
4029+
40264030
SAVE_OPLINE();
40274031
EX(call) = call->prev_execute_data;
40284032

@@ -4057,6 +4061,10 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER))
40574061
ret = EX_VAR(opline->result.var);
40584062
}
40594063

4064+
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
4065+
zend_deprecated_function(fbc);
4066+
}
4067+
40604068
call->prev_execute_data = execute_data;
40614069
execute_data = call;
40624070
i_init_func_execute_data(&fbc->op_array, ret, 0 EXECUTE_DATA_CC);
@@ -4155,6 +4163,10 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
41554163
ret = EX_VAR(opline->result.var);
41564164
}
41574165

4166+
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
4167+
zend_deprecated_function(fbc);
4168+
}
4169+
41584170
call->prev_execute_data = execute_data;
41594171
execute_data = call;
41604172
i_init_func_execute_data(&fbc->op_array, ret, 1 EXECUTE_DATA_CC);

Zend/zend_vm_execute.h

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)