Skip to content

Commit 28dd600

Browse files
committed
Merge branch 'PHP-8.2'
* PHP-8.2: Properly deal with internal attributes used on promoted properties.
2 parents ef5ed06 + fdd088f commit 28dd600

File tree

6 files changed

+167
-30
lines changed

6 files changed

+167
-30
lines changed

Zend/zend_compile.c

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

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

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

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

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

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

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

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

6681-
if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) {
6682-
if (zend_is_attribute_repeated(*attributes, attr)) {
6683-
zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name));
6695+
if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) {
6696+
if (zend_is_attribute_repeated(*attributes, attr)) {
6697+
zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name));
6698+
}
66846699
}
6685-
}
66866700

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

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

68246839
if (attributes_ast) {
6825-
zend_compile_attributes(&op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER);
6840+
zend_compile_attributes(
6841+
&op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER,
6842+
property_flags ? ZEND_ATTRIBUTE_TARGET_PROPERTY : 0
6843+
);
68266844
}
68276845

68286846
if (type_ast) {
@@ -6928,7 +6946,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
69286946
scope, name, &default_value, property_flags | ZEND_ACC_PROMOTED, doc_comment, type);
69296947
if (attributes_ast) {
69306948
zend_compile_attributes(
6931-
&prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY);
6949+
&prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, ZEND_ATTRIBUTE_TARGET_PARAMETER);
69326950
}
69336951
}
69346952
}
@@ -7365,7 +7383,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel)
73657383
target = ZEND_ATTRIBUTE_TARGET_METHOD;
73667384
}
73677385

7368-
zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target);
7386+
zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target, 0);
73697387
}
73707388

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

75497567
if (attr_ast) {
7550-
zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY);
7568+
zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0);
75517569
}
75527570
}
75537571
}
@@ -7608,7 +7626,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
76087626
c = zend_declare_class_constant_ex(ce, name, &value_zv, flags, doc_comment);
76097627

76107628
if (attr_ast) {
7611-
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
7629+
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
76127630
}
76137631
}
76147632
}
@@ -7870,7 +7888,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
78707888
CG(active_class_entry) = ce;
78717889

78727890
if (decl->child[3]) {
7873-
zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS);
7891+
zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0);
78747892
}
78757893

78767894
if (implements_ast) {
@@ -8028,7 +8046,7 @@ static void zend_compile_enum_case(zend_ast *ast)
80288046

80298047
zend_ast *attr_ast = ast->child[3];
80308048
if (attr_ast) {
8031-
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
8049+
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
80328050
}
80338051
}
80348052

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)