Skip to content

Commit fdd088f

Browse files
kooldeviluuu1994
authored andcommitted
Properly deal with internal attributes used on promoted properties.
Closes GH-9661
1 parent 8e49d7f commit fdd088f

File tree

7 files changed

+169
-30
lines changed

7 files changed

+169
-30
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ PHP NEWS
66
. Fixed bug GH-9823 (Don’t reset func in zend_closure_internal_handler).
77
(Florian Sowade)
88
. Fixed potential NULL pointer dereference Windows shm*() functions. (cmb)
9+
. Fix target validation for internal attributes with constructor property
10+
promotion. (kooldev)
911

1012
- FPM:
1113
. Fixed bug GH-9754 (SaltStack (using Python subprocess) hangs when running

Zend/zend_compile.c

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6589,8 +6589,9 @@ static bool zend_is_valid_default_value(zend_type type, zval *value)
65896589
return 0;
65906590
}
65916591

6592-
static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint32_t offset, uint32_t target) /* {{{ */
6593-
{
6592+
static void zend_compile_attributes(
6593+
HashTable **attributes, zend_ast *ast, uint32_t offset, uint32_t target, uint32_t promoted
6594+
) /* {{{ */ {
65946595
zend_attribute *attr;
65956596
zend_internal_attribute *config;
65966597

@@ -6616,8 +6617,20 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
66166617
}
66176618

66186619
zend_string *name = zend_resolve_class_name_ast(el->child[0]);
6620+
zend_string *lcname = zend_string_tolower_ex(name, false);
66196621
zend_ast_list *args = el->child[1] ? zend_ast_get_list(el->child[1]) : NULL;
66206622

6623+
config = zend_internal_attribute_get(lcname);
6624+
zend_string_release(lcname);
6625+
6626+
/* Exclude internal attributes that do not match on promoted properties. */
6627+
if (config && !(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
6628+
if (promoted & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL)) {
6629+
zend_string_release(name);
6630+
continue;
6631+
}
6632+
}
6633+
66216634
uint32_t flags = (CG(active_op_array)->fn_flags & ZEND_ACC_STRICT_TYPES)
66226635
? ZEND_ATTRIBUTE_STRICT_TYPES : 0;
66236636
attr = zend_add_attribute(
@@ -6662,31 +6675,33 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
66626675
}
66636676
}
66646677

6665-
/* Validate attributes in a secondary loop (needed to detect repeated attributes). */
6666-
ZEND_HASH_PACKED_FOREACH_PTR(*attributes, attr) {
6667-
if (attr->offset != offset || NULL == (config = zend_internal_attribute_get(attr->lcname))) {
6668-
continue;
6669-
}
6678+
if (*attributes != NULL) {
6679+
/* Validate attributes in a secondary loop (needed to detect repeated attributes). */
6680+
ZEND_HASH_PACKED_FOREACH_PTR(*attributes, attr) {
6681+
if (attr->offset != offset || NULL == (config = zend_internal_attribute_get(attr->lcname))) {
6682+
continue;
6683+
}
66706684

6671-
if (!(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
6672-
zend_string *location = zend_get_attribute_target_names(target);
6673-
zend_string *allowed = zend_get_attribute_target_names(config->flags);
6685+
if (!(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
6686+
zend_string *location = zend_get_attribute_target_names(target);
6687+
zend_string *allowed = zend_get_attribute_target_names(config->flags);
66746688

6675-
zend_error_noreturn(E_ERROR, "Attribute \"%s\" cannot target %s (allowed targets: %s)",
6676-
ZSTR_VAL(attr->name), ZSTR_VAL(location), ZSTR_VAL(allowed)
6677-
);
6678-
}
6689+
zend_error_noreturn(E_ERROR, "Attribute \"%s\" cannot target %s (allowed targets: %s)",
6690+
ZSTR_VAL(attr->name), ZSTR_VAL(location), ZSTR_VAL(allowed)
6691+
);
6692+
}
66796693

6680-
if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) {
6681-
if (zend_is_attribute_repeated(*attributes, attr)) {
6682-
zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name));
6694+
if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) {
6695+
if (zend_is_attribute_repeated(*attributes, attr)) {
6696+
zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name));
6697+
}
66836698
}
6684-
}
66856699

6686-
if (config->validator != NULL) {
6687-
config->validator(attr, target, CG(active_class_entry));
6688-
}
6689-
} ZEND_HASH_FOREACH_END();
6700+
if (config->validator != NULL) {
6701+
config->validator(attr, target, CG(active_class_entry));
6702+
}
6703+
} ZEND_HASH_FOREACH_END();
6704+
}
66906705
}
66916706
/* }}} */
66926707

@@ -6821,7 +6836,10 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
68216836
arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0);
68226837

68236838
if (attributes_ast) {
6824-
zend_compile_attributes(&op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER);
6839+
zend_compile_attributes(
6840+
&op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER,
6841+
property_flags ? ZEND_ATTRIBUTE_TARGET_PROPERTY : 0
6842+
);
68256843
}
68266844

68276845
if (type_ast) {
@@ -6927,7 +6945,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
69276945
scope, name, &default_value, property_flags | ZEND_ACC_PROMOTED, doc_comment, type);
69286946
if (attributes_ast) {
69296947
zend_compile_attributes(
6930-
&prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY);
6948+
&prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, ZEND_ATTRIBUTE_TARGET_PARAMETER);
69316949
}
69326950
}
69336951
}
@@ -7364,7 +7382,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel)
73647382
target = ZEND_ATTRIBUTE_TARGET_METHOD;
73657383
}
73667384

7367-
zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target);
7385+
zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target, 0);
73687386
}
73697387

73707388
/* Do not leak the class scope into free standing functions, even if they are dynamically
@@ -7546,7 +7564,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
75467564
info = zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type);
75477565

75487566
if (attr_ast) {
7549-
zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY);
7567+
zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0);
75507568
}
75517569
}
75527570
}
@@ -7607,7 +7625,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
76077625
c = zend_declare_class_constant_ex(ce, name, &value_zv, flags, doc_comment);
76087626

76097627
if (attr_ast) {
7610-
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
7628+
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
76117629
}
76127630
}
76137631
}
@@ -7869,7 +7887,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
78697887
CG(active_class_entry) = ce;
78707888

78717889
if (decl->child[3]) {
7872-
zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS);
7890+
zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0);
78737891
}
78747892

78757893
if (implements_ast) {
@@ -8027,7 +8045,7 @@ static void zend_compile_enum_case(zend_ast *ast)
80278045

80288046
zend_ast *attr_ast = ast->child[3];
80298047
if (attr_ast) {
8030-
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
8048+
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
80318049
}
80328050
}
80338051

ext/zend_test/test.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ static zend_class_entry *zend_test_child_class;
3939
static zend_class_entry *zend_test_trait;
4040
static zend_class_entry *zend_test_attribute;
4141
static zend_class_entry *zend_test_parameter_attribute;
42+
static zend_class_entry *zend_test_property_attribute;
4243
static zend_class_entry *zend_test_class_with_method_with_parameter_attribute;
4344
static zend_class_entry *zend_test_child_class_with_method_with_parameter_attribute;
4445
static zend_class_entry *zend_test_forbid_dynamic_call;
@@ -587,6 +588,17 @@ static ZEND_METHOD(ZendTestParameterAttribute, __construct)
587588
ZVAL_STR_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), parameter);
588589
}
589590

591+
static ZEND_METHOD(ZendTestPropertyAttribute, __construct)
592+
{
593+
zend_string *parameter;
594+
595+
ZEND_PARSE_PARAMETERS_START(1, 1)
596+
Z_PARAM_STR(parameter)
597+
ZEND_PARSE_PARAMETERS_END();
598+
599+
ZVAL_STR_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), parameter);
600+
}
601+
590602
static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, no_override)
591603
{
592604
zend_string *parameter;
@@ -688,6 +700,9 @@ PHP_MINIT_FUNCTION(zend_test)
688700
ZVAL_PSTRING(&attr->args[0].value, "value1");
689701
}
690702

703+
zend_test_property_attribute = register_class_ZendTestPropertyAttribute();
704+
zend_mark_internal_attribute(zend_test_property_attribute);
705+
691706
zend_test_class_with_method_with_parameter_attribute = register_class_ZendTestClassWithMethodWithParameterAttribute();
692707

693708
{

ext/zend_test/test.stub.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ final class ZendTestParameterAttribute {
6666
public function __construct(string $parameter) {}
6767
}
6868

69+
#[Attribute(Attribute::TARGET_PROPERTY)]
70+
final class ZendTestPropertyAttribute {
71+
public string $parameter;
72+
73+
public function __construct(string $parameter) {}
74+
}
75+
6976
class ZendTestClassWithMethodWithParameterAttribute {
7077
final public function no_override(string $parameter): int {}
7178
public function override(string $parameter): int {}

ext/zend_test/test_arginfo.h

Lines changed: 36 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Attribute on promoted property may only target parameter
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
class AttrTest
9+
{
10+
public function __construct(
11+
#[ZendTestParameterAttribute('foo')] public $param
12+
) {}
13+
}
14+
15+
$ref = new ReflectionClass(AttrTest::class);
16+
$attr = $ref->getConstructor()->getParameters()[0]->getAttributes();
17+
18+
var_dump(count($attr));
19+
var_dump($attr[0]->getName());
20+
var_dump($attr[0]->newInstance()->parameter);
21+
22+
$attr = $ref->getProperty('param')->getAttributes();
23+
24+
var_dump(count($attr));
25+
26+
?>
27+
--EXPECTF--
28+
int(1)
29+
string(26) "ZendTestParameterAttribute"
30+
string(3) "foo"
31+
int(0)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Attribute on promoted property may only target property
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
class AttrTest
9+
{
10+
public function __construct(
11+
#[ZendTestPropertyAttribute('foo')] public $param
12+
) {}
13+
}
14+
15+
$ref = new ReflectionClass(AttrTest::class);
16+
$attr = $ref->getConstructor()->getParameters()[0]->getAttributes();
17+
18+
var_dump(count($attr));
19+
20+
$attr = $ref->getProperty('param')->getAttributes();
21+
22+
var_dump(count($attr));
23+
var_dump($attr[0]->getName());
24+
var_dump($attr[0]->newInstance()->parameter);
25+
26+
?>
27+
--EXPECTF--
28+
int(0)
29+
int(1)
30+
string(25) "ZendTestPropertyAttribute"
31+
string(3) "foo"

0 commit comments

Comments
 (0)