diff --git a/Zend/tests/attributes/001_placement.phpt b/Zend/tests/attributes/001_placement.phpt index 8f16f4cbe8481..cf7bcd450406d 100644 --- a/Zend/tests/attributes/001_placement.phpt +++ b/Zend/tests/attributes/001_placement.phpt @@ -7,10 +7,10 @@ Attributes can be placed on all supported elements. class Foo { <> - public const FOO = 'foo', BAR = 'bar'; + public const FOO = 'foo'; <> - public $x, $y; + public $x; <> public function foo(<> $a, <> $b) { } @@ -30,9 +30,7 @@ $ref = new \ReflectionClass(Foo::class); $sources = [ $ref, $ref->getReflectionConstant('FOO'), - $ref->getReflectionConstant('BAR'), $ref->getProperty('x'), - $ref->getProperty('y'), $ref->getMethod('foo'), $ref->getMethod('foo')->getParameters()[0], $ref->getMethod('foo')->getParameters()[1], @@ -71,9 +69,6 @@ array(1) { int(2) } -string(23) "ReflectionClassConstant" -int(0) - string(18) "ReflectionProperty" int(1) string(2) "A1" @@ -82,9 +77,6 @@ array(1) { int(3) } -string(18) "ReflectionProperty" -int(0) - string(16) "ReflectionMethod" int(1) string(2) "A1" diff --git a/Zend/tests/attributes/013_scope_resolution.phpt b/Zend/tests/attributes/013_scope_resolution.phpt new file mode 100644 index 0000000000000..fa4103cbce4f9 --- /dev/null +++ b/Zend/tests/attributes/013_scope_resolution.phpt @@ -0,0 +1,112 @@ +--TEST-- +Attributes make use of correct scope. +--FILE-- +> +class C1 +{ + <> + private const FOO = 'foo'; + + <> + public $a; + + <> + public function bar(<> $p) { } +} + +$ref = new \ReflectionClass(C1::class); +print_r($ref->getAttributes()[0]->getArguments()); +print_r($ref->getReflectionConstant('FOO')->getAttributes()[0]->getArguments()); +print_r($ref->getProperty('a')->getAttributes()[0]->getArguments()); +print_r($ref->getMethod('bar')->getAttributes()[0]->getArguments()); +print_r($ref->getMethod('bar')->getParameters()[0]->getAttributes()[0]->getArguments()); + +echo "\n"; + +class C2 +{ + private const FOO = 'foo'; + + public static function foo() + { + return <> function (<> $p) { }; + } +} + +$ref = new \ReflectionFunction(C2::foo()); +print_r($ref->getAttributes()[0]->getArguments()); +print_r($ref->getParameters()[0]->getAttributes()[0]->getArguments()); + +echo "\n"; + +class C3 +{ + private const FOO = 'foo'; + + public static function foo() + { + return new <> class() { + private const FOO = 'bar'; + + <> + public function bar() { } + }; + } +} + +$obj = C3::foo(); +$ref = new \ReflectionObject($obj); +$name = $ref->getMethod('bar')->getAttributes()[0]->getArguments()[0]; + +print_r($ref->getAttributes()[0]->getArguments()); +var_dump($name == get_class($obj)); +var_dump($ref->getMethod('bar')->getAttributes()[0]->getArguments()[1]); + +?> +--EXPECT-- +Array +( + [0] => C1 + [1] => foo +) +Array +( + [0] => C1 + [1] => foo +) +Array +( + [0] => C1 + [1] => foo +) +Array +( + [0] => C1 + [1] => foo +) +Array +( + [0] => C1 + [1] => foo +) + +Array +( + [0] => C2 + [1] => foo +) +Array +( + [0] => C2 + [1] => foo +) + +Array +( + [0] => C3 + [1] => foo +) +bool(true) +string(3) "bar" diff --git a/Zend/tests/attributes/014_class_const_group.phpt b/Zend/tests/attributes/014_class_const_group.phpt new file mode 100644 index 0000000000000..18ff93683aff9 --- /dev/null +++ b/Zend/tests/attributes/014_class_const_group.phpt @@ -0,0 +1,14 @@ +--TEST-- +Attributes cannot be applied to groups of class constants. +--FILE-- +> + public const A = 1, B = 2; +} + +?> +--EXPECTF-- +Fatal error: Cannot apply attributes to a group of constants in %s diff --git a/Zend/tests/attributes/015_property_group.phpt b/Zend/tests/attributes/015_property_group.phpt new file mode 100644 index 0000000000000..493e4ae260136 --- /dev/null +++ b/Zend/tests/attributes/015_property_group.phpt @@ -0,0 +1,14 @@ +--TEST-- +Attributes cannot be applied to groups of properties. +--FILE-- +> + public $x, $y; +} + +?> +--EXPECTF-- +Fatal error: Cannot apply attributes to a group of properties in %s diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 2cb11a4e6b22a..5313b60c772a8 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1590,7 +1590,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_CLASS_CONST_DECL: smart_str_appends(str, "const "); goto simple_list; - case ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES: + case ZEND_AST_CLASS_CONST_GROUP: if (ast->child[1]) { zend_ast_export_attributes(str, ast->child[1], indent, 1); } @@ -2192,7 +2192,7 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) case ZEND_AST_PARAM: ast->child[3] = attr; break; - case ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES: + case ZEND_AST_CLASS_CONST_GROUP: ast->child[1] = attr; break; EMPTY_SWITCH_DEFAULT_CASE() diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 97595b97b89d5..90efaa7bbc107 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -139,7 +139,7 @@ enum _zend_ast_kind { ZEND_AST_USE_ELEM, ZEND_AST_TRAIT_ALIAS, ZEND_AST_GROUP_USE, - ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES, + ZEND_AST_CLASS_CONST_GROUP, ZEND_AST_ATTRIBUTE, /* 3 child nodes */ diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index cc5b0d0fa2ddc..ee93a74a83f2d 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -38,7 +38,7 @@ ZEND_API void zend_attribute_free(zend_attribute *attr) efree(attr); } -ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) +static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) { if (attributes) { zend_attribute *attr; @@ -53,7 +53,7 @@ ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string * return NULL; } -ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) +static zend_attribute *get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) { if (attributes) { zend_attribute *attr; @@ -70,6 +70,26 @@ ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const cha return NULL; } +ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname) +{ + return get_attribute(attributes, lcname, 0); +} + +ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len) +{ + return get_attribute_str(attributes, str, len, 0); +} + +ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) +{ + return get_attribute(attributes, lcname, offset + 1); +} + +ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) +{ + return get_attribute_str(attributes, str, len, offset + 1); +} + ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator) { zend_string *lcname = zend_string_tolower_ex(ce->name, 1); diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index cd30f5305bab4..62a132681efbb 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -19,6 +19,7 @@ extern ZEND_API zend_class_entry *zend_ce_php_compiler_attribute; typedef struct _zend_attribute { zend_string *name; zend_string *lcname; + /* Parameter offsets start at 1, everything else uses 0. */ uint32_t offset; uint32_t argc; zval argv[1]; @@ -28,8 +29,11 @@ typedef void (*zend_attributes_internal_validator)(zend_attribute *attr, int tar ZEND_API void zend_attribute_free(zend_attribute *attr); -ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset); -ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset); +ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname); +ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len); + +ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset); +ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset); ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator); ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 6db136d6353e3..bdfea39fe9f69 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2367,7 +2367,7 @@ static inline zend_bool zend_is_variable_or_call(zend_ast *ast) /* {{{ */ static inline zend_bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */ { return ast->kind == ZEND_AST_STMT_LIST || ast->kind == ZEND_AST_LABEL - || ast->kind == ZEND_AST_PROP_DECL || ast->kind == ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES + || ast->kind == ZEND_AST_PROP_DECL || ast->kind == ZEND_AST_CLASS_CONST_GROUP || ast->kind == ZEND_AST_USE_TRAIT || ast->kind == ZEND_AST_METHOD; } /* }}} */ @@ -6520,8 +6520,6 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, H } zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, attributes, type); - - attributes = NULL; } } /* }}} */ @@ -6534,6 +6532,11 @@ void zend_compile_prop_group(zend_ast *list) /* {{{ */ zend_ast *prop_ast = list->child[1]; if (list->child[2]) { + if (zend_ast_get_list(prop_ast)->children > 1) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot apply attributes to a group of properties"); + return; + } + attributes = create_attribute_array(); zend_compile_attributes(attributes, list->child[2], 0, ZEND_ATTRIBUTE_TARGET_PROPERTY); } @@ -6567,6 +6570,11 @@ void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */ } if (attr_ast) { + if (list->children > 1) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot apply attributes to a group of constants"); + return; + } + attributes = create_attribute_array(); zend_compile_attributes(attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST); } @@ -6586,8 +6594,6 @@ void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */ zend_const_expr_to_zval(&value_zv, value_ast); zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment, attributes); - - attributes = NULL; } } /* }}} */ @@ -8885,7 +8891,7 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */ case ZEND_AST_PROP_GROUP: zend_compile_prop_group(ast); break; - case ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES: + case ZEND_AST_CLASS_CONST_GROUP: zend_compile_class_const_decl(ast->child[0], ast->child[1]); break; case ZEND_AST_USE_TRAIT: diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 9c344d8ad10bf..002919e7bd619 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -786,7 +786,7 @@ annotated_class_statement: { $$ = zend_ast_create(ZEND_AST_PROP_GROUP, $2, $3, NULL); $$->attr = $1; } | method_modifiers T_CONST class_const_list ';' - { $$ = zend_ast_create(ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES, $3, NULL); $3->attr = $1; } + { $$ = zend_ast_create(ZEND_AST_CLASS_CONST_GROUP, $3, NULL); $3->attr = $1; } | method_modifiers function returns_ref identifier backup_doc_comment '(' parameter_list ')' return_type backup_fn_flags method_body backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1 | $12, $2, $5, diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 87a1d29adad9d..cf6972d9542d2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6582,7 +6582,7 @@ ZEND_METHOD(ReflectionAttribute, newInstance) RETURN_THROWS(); } - if (ce->type == ZEND_USER_CLASS && !zend_get_attribute_str(ce->info.user.attributes, ZEND_STRL("phpattribute"), 0)) { + if (ce->type == ZEND_USER_CLASS && !zend_get_attribute_str(ce->info.user.attributes, ZEND_STRL("phpattribute"))) { zend_throw_error(NULL, "Attempting to use class '%s' as attribute that does not have <>.", ZSTR_VAL(attr->data->name)); RETURN_THROWS(); } else if (ce->type == ZEND_INTERNAL_CLASS && !zend_attribute_get_validator(attr->data->lcname)) {