Merge branch 'PHP-8.2'

* PHP-8.2:
  Properly deal with internal attributes used on promoted properties.
This commit is contained in:
Ilija Tovilo 2022-11-03 14:29:44 +01:00
commit 28dd6006c0
No known key found for this signature in database
GPG Key ID: A4F5D403F118200A
6 changed files with 170 additions and 33 deletions

View File

@ -6590,8 +6590,9 @@ static bool zend_is_valid_default_value(zend_type type, zval *value)
return 0; return 0;
} }
static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint32_t offset, uint32_t target) /* {{{ */ static void zend_compile_attributes(
{ HashTable **attributes, zend_ast *ast, uint32_t offset, uint32_t target, uint32_t promoted
) /* {{{ */ {
zend_attribute *attr; zend_attribute *attr;
zend_internal_attribute *config; zend_internal_attribute *config;
@ -6617,8 +6618,20 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
} }
zend_string *name = zend_resolve_class_name_ast(el->child[0]); zend_string *name = zend_resolve_class_name_ast(el->child[0]);
zend_string *lcname = zend_string_tolower_ex(name, false);
zend_ast_list *args = el->child[1] ? zend_ast_get_list(el->child[1]) : NULL; zend_ast_list *args = el->child[1] ? zend_ast_get_list(el->child[1]) : NULL;
config = zend_internal_attribute_get(lcname);
zend_string_release(lcname);
/* Exclude internal attributes that do not match on promoted properties. */
if (config && !(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
if (promoted & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL)) {
zend_string_release(name);
continue;
}
}
uint32_t flags = (CG(active_op_array)->fn_flags & ZEND_ACC_STRICT_TYPES) uint32_t flags = (CG(active_op_array)->fn_flags & ZEND_ACC_STRICT_TYPES)
? ZEND_ATTRIBUTE_STRICT_TYPES : 0; ? ZEND_ATTRIBUTE_STRICT_TYPES : 0;
attr = zend_add_attribute( attr = zend_add_attribute(
@ -6663,31 +6676,33 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
} }
} }
/* Validate attributes in a secondary loop (needed to detect repeated attributes). */ if (*attributes != NULL) {
ZEND_HASH_PACKED_FOREACH_PTR(*attributes, attr) { /* Validate attributes in a secondary loop (needed to detect repeated attributes). */
if (attr->offset != offset || NULL == (config = zend_internal_attribute_get(attr->lcname))) { ZEND_HASH_PACKED_FOREACH_PTR(*attributes, attr) {
continue; if (attr->offset != offset || NULL == (config = zend_internal_attribute_get(attr->lcname))) {
} continue;
if (!(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
zend_string *location = zend_get_attribute_target_names(target);
zend_string *allowed = zend_get_attribute_target_names(config->flags);
zend_error_noreturn(E_ERROR, "Attribute \"%s\" cannot target %s (allowed targets: %s)",
ZSTR_VAL(attr->name), ZSTR_VAL(location), ZSTR_VAL(allowed)
);
}
if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) {
if (zend_is_attribute_repeated(*attributes, attr)) {
zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name));
} }
}
if (config->validator != NULL) { if (!(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
config->validator(attr, target, CG(active_class_entry)); zend_string *location = zend_get_attribute_target_names(target);
} zend_string *allowed = zend_get_attribute_target_names(config->flags);
} ZEND_HASH_FOREACH_END();
zend_error_noreturn(E_ERROR, "Attribute \"%s\" cannot target %s (allowed targets: %s)",
ZSTR_VAL(attr->name), ZSTR_VAL(location), ZSTR_VAL(allowed)
);
}
if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) {
if (zend_is_attribute_repeated(*attributes, attr)) {
zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name));
}
}
if (config->validator != NULL) {
config->validator(attr, target, CG(active_class_entry));
}
} ZEND_HASH_FOREACH_END();
}
} }
/* }}} */ /* }}} */
@ -6822,7 +6837,10 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0); arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0);
if (attributes_ast) { if (attributes_ast) {
zend_compile_attributes(&op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER); zend_compile_attributes(
&op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER,
property_flags ? ZEND_ATTRIBUTE_TARGET_PROPERTY : 0
);
} }
if (type_ast) { if (type_ast) {
@ -6928,7 +6946,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
scope, name, &default_value, property_flags | ZEND_ACC_PROMOTED, doc_comment, type); scope, name, &default_value, property_flags | ZEND_ACC_PROMOTED, doc_comment, type);
if (attributes_ast) { if (attributes_ast) {
zend_compile_attributes( zend_compile_attributes(
&prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY); &prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, ZEND_ATTRIBUTE_TARGET_PARAMETER);
} }
} }
} }
@ -7365,7 +7383,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel)
target = ZEND_ATTRIBUTE_TARGET_METHOD; target = ZEND_ATTRIBUTE_TARGET_METHOD;
} }
zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target); zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target, 0);
} }
/* Do not leak the class scope into free standing functions, even if they are dynamically /* 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
info = zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type); info = zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type);
if (attr_ast) { if (attr_ast) {
zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY); zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0);
} }
} }
} }
@ -7608,7 +7626,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
c = zend_declare_class_constant_ex(ce, name, &value_zv, flags, doc_comment); c = zend_declare_class_constant_ex(ce, name, &value_zv, flags, doc_comment);
if (attr_ast) { if (attr_ast) {
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST); zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
} }
} }
} }
@ -7870,7 +7888,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
CG(active_class_entry) = ce; CG(active_class_entry) = ce;
if (decl->child[3]) { if (decl->child[3]) {
zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS); zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0);
} }
if (implements_ast) { if (implements_ast) {
@ -8028,7 +8046,7 @@ static void zend_compile_enum_case(zend_ast *ast)
zend_ast *attr_ast = ast->child[3]; zend_ast *attr_ast = ast->child[3];
if (attr_ast) { if (attr_ast) {
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST); zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
} }
} }

View File

@ -39,6 +39,7 @@ static zend_class_entry *zend_test_child_class;
static zend_class_entry *zend_test_trait; static zend_class_entry *zend_test_trait;
static zend_class_entry *zend_test_attribute; static zend_class_entry *zend_test_attribute;
static zend_class_entry *zend_test_parameter_attribute; static zend_class_entry *zend_test_parameter_attribute;
static zend_class_entry *zend_test_property_attribute;
static zend_class_entry *zend_test_class_with_method_with_parameter_attribute; static zend_class_entry *zend_test_class_with_method_with_parameter_attribute;
static zend_class_entry *zend_test_child_class_with_method_with_parameter_attribute; static zend_class_entry *zend_test_child_class_with_method_with_parameter_attribute;
static zend_class_entry *zend_test_forbid_dynamic_call; static zend_class_entry *zend_test_forbid_dynamic_call;
@ -587,6 +588,17 @@ static ZEND_METHOD(ZendTestParameterAttribute, __construct)
ZVAL_STR_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), parameter); ZVAL_STR_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), parameter);
} }
static ZEND_METHOD(ZendTestPropertyAttribute, __construct)
{
zend_string *parameter;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(parameter)
ZEND_PARSE_PARAMETERS_END();
ZVAL_STR_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), parameter);
}
static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, no_override) static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, no_override)
{ {
zend_string *parameter; zend_string *parameter;
@ -688,6 +700,9 @@ PHP_MINIT_FUNCTION(zend_test)
ZVAL_PSTRING(&attr->args[0].value, "value1"); ZVAL_PSTRING(&attr->args[0].value, "value1");
} }
zend_test_property_attribute = register_class_ZendTestPropertyAttribute();
zend_mark_internal_attribute(zend_test_property_attribute);
zend_test_class_with_method_with_parameter_attribute = register_class_ZendTestClassWithMethodWithParameterAttribute(); zend_test_class_with_method_with_parameter_attribute = register_class_ZendTestClassWithMethodWithParameterAttribute();
{ {

View File

@ -66,6 +66,13 @@ namespace {
public function __construct(string $parameter) {} public function __construct(string $parameter) {}
} }
#[Attribute(Attribute::TARGET_PROPERTY)]
final class ZendTestPropertyAttribute {
public string $parameter;
public function __construct(string $parameter) {}
}
class ZendTestClassWithMethodWithParameterAttribute { class ZendTestClassWithMethodWithParameterAttribute {
final public function no_override(string $parameter): int {} final public function no_override(string $parameter): int {}
public function override(string $parameter): int {} public function override(string $parameter): int {}

View File

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead. /* This is a generated file, edit the .stub.php file instead.
* Stub hash: d8c0689141c897c3f31d00550128df7b8c00274f */ * Stub hash: 786b35a1fbff38215431d5ae46a5816c70defce5 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
@ -134,6 +134,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZendTestParameterAttribute___construct, 0,
ZEND_ARG_TYPE_INFO(0, parameter, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, parameter, IS_STRING, 0)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
#define arginfo_class_ZendTestPropertyAttribute___construct arginfo_class_ZendTestParameterAttribute___construct
#define arginfo_class_ZendTestClassWithMethodWithParameterAttribute_no_override arginfo_zend_test_parameter_with_attribute #define arginfo_class_ZendTestClassWithMethodWithParameterAttribute_no_override arginfo_zend_test_parameter_with_attribute
#define arginfo_class_ZendTestClassWithMethodWithParameterAttribute_override arginfo_zend_test_parameter_with_attribute #define arginfo_class_ZendTestClassWithMethodWithParameterAttribute_override arginfo_zend_test_parameter_with_attribute
@ -194,6 +196,7 @@ static ZEND_METHOD(_ZendTestClass, returnsThrowable);
static ZEND_METHOD(_ZendTestChildClass, returnsThrowable); static ZEND_METHOD(_ZendTestChildClass, returnsThrowable);
static ZEND_METHOD(_ZendTestTrait, testMethod); static ZEND_METHOD(_ZendTestTrait, testMethod);
static ZEND_METHOD(ZendTestParameterAttribute, __construct); static ZEND_METHOD(ZendTestParameterAttribute, __construct);
static ZEND_METHOD(ZendTestPropertyAttribute, __construct);
static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, no_override); static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, no_override);
static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, override); static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, override);
static ZEND_METHOD(ZendTestChildClassWithMethodWithParameterAttribute, override); static ZEND_METHOD(ZendTestChildClassWithMethodWithParameterAttribute, override);
@ -281,6 +284,12 @@ static const zend_function_entry class_ZendTestParameterAttribute_methods[] = {
}; };
static const zend_function_entry class_ZendTestPropertyAttribute_methods[] = {
ZEND_ME(ZendTestPropertyAttribute, __construct, arginfo_class_ZendTestPropertyAttribute___construct, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
static const zend_function_entry class_ZendTestClassWithMethodWithParameterAttribute_methods[] = { static const zend_function_entry class_ZendTestClassWithMethodWithParameterAttribute_methods[] = {
ZEND_ME(ZendTestClassWithMethodWithParameterAttribute, no_override, arginfo_class_ZendTestClassWithMethodWithParameterAttribute_no_override, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) ZEND_ME(ZendTestClassWithMethodWithParameterAttribute, no_override, arginfo_class_ZendTestClassWithMethodWithParameterAttribute_no_override, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
ZEND_ME(ZendTestClassWithMethodWithParameterAttribute, override, arginfo_class_ZendTestClassWithMethodWithParameterAttribute_override, ZEND_ACC_PUBLIC) ZEND_ME(ZendTestClassWithMethodWithParameterAttribute, override, arginfo_class_ZendTestClassWithMethodWithParameterAttribute_override, ZEND_ACC_PUBLIC)
@ -506,6 +515,32 @@ static zend_class_entry *register_class_ZendTestParameterAttribute(void)
return class_entry; return class_entry;
} }
static zend_class_entry *register_class_ZendTestPropertyAttribute(void)
{
zend_class_entry ce, *class_entry;
INIT_CLASS_ENTRY(ce, "ZendTestPropertyAttribute", class_ZendTestPropertyAttribute_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_FINAL;
zval property_parameter_default_value;
ZVAL_UNDEF(&property_parameter_default_value);
zend_string *property_parameter_name = zend_string_init("parameter", sizeof("parameter") - 1, 1);
zend_declare_typed_property(class_entry, property_parameter_name, &property_parameter_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_string_release(property_parameter_name);
#if (PHP_VERSION_ID >= 80200)
zend_string *attribute_name_Attribute_class_ZendTestPropertyAttribute = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
zend_attribute *attribute_Attribute_class_ZendTestPropertyAttribute = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ZendTestPropertyAttribute, 1);
zend_string_release(attribute_name_Attribute_class_ZendTestPropertyAttribute);
zval attribute_Attribute_class_ZendTestPropertyAttribute_arg0;
ZVAL_LONG(&attribute_Attribute_class_ZendTestPropertyAttribute_arg0, ZEND_ATTRIBUTE_TARGET_PROPERTY);
ZVAL_COPY_VALUE(&attribute_Attribute_class_ZendTestPropertyAttribute->args[0].value, &attribute_Attribute_class_ZendTestPropertyAttribute_arg0);
#endif
return class_entry;
}
static zend_class_entry *register_class_ZendTestClassWithMethodWithParameterAttribute(void) static zend_class_entry *register_class_ZendTestClassWithMethodWithParameterAttribute(void)
{ {
zend_class_entry ce, *class_entry; zend_class_entry ce, *class_entry;

View File

@ -0,0 +1,31 @@
--TEST--
Attribute on promoted property may only target parameter
--EXTENSIONS--
zend_test
--FILE--
<?php
class AttrTest
{
public function __construct(
#[ZendTestParameterAttribute('foo')] public $param
) {}
}
$ref = new ReflectionClass(AttrTest::class);
$attr = $ref->getConstructor()->getParameters()[0]->getAttributes();
var_dump(count($attr));
var_dump($attr[0]->getName());
var_dump($attr[0]->newInstance()->parameter);
$attr = $ref->getProperty('param')->getAttributes();
var_dump(count($attr));
?>
--EXPECTF--
int(1)
string(26) "ZendTestParameterAttribute"
string(3) "foo"
int(0)

View File

@ -0,0 +1,31 @@
--TEST--
Attribute on promoted property may only target property
--EXTENSIONS--
zend_test
--FILE--
<?php
class AttrTest
{
public function __construct(
#[ZendTestPropertyAttribute('foo')] public $param
) {}
}
$ref = new ReflectionClass(AttrTest::class);
$attr = $ref->getConstructor()->getParameters()[0]->getAttributes();
var_dump(count($attr));
$attr = $ref->getProperty('param')->getAttributes();
var_dump(count($attr));
var_dump($attr[0]->getName());
var_dump($attr[0]->newInstance()->parameter);
?>
--EXPECTF--
int(0)
int(1)
string(25) "ZendTestPropertyAttribute"
string(3) "foo"