diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 72a5bddeb38d3..170cb6d260efe 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -113,7 +113,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast * } ZEND_API zend_ast *zend_ast_create_decl( - zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, HashTable *attributes, + zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3 ) { zend_ast_decl *ast; @@ -126,7 +126,7 @@ ZEND_API zend_ast *zend_ast_create_decl( ast->flags = flags; ast->lex_pos = LANG_SCNG(yy_text); ast->doc_comment = doc_comment; - ast->attributes = attributes; + ast->attributes = NULL; ast->name = name; ast->child[0] = child0; ast->child[1] = child1; @@ -859,7 +859,7 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) zend_string_release_ex(decl->doc_comment, 0); } if (decl->attributes) { - zend_array_ptr_dtor(decl->attributes); + zend_ast_destroy(decl->attributes); } zend_ast_destroy(decl->child[0]); zend_ast_destroy(decl->child[1]); @@ -1545,6 +1545,9 @@ 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: + zend_ast_export_ex(str, ast->child[0], 0, indent); + break; case ZEND_AST_NAME_LIST: zend_ast_export_name_list(str, (zend_ast_list*)ast, indent); break; @@ -2116,101 +2119,31 @@ ZEND_API ZEND_COLD zend_string *zend_ast_export(const char *prefix, zend_ast *as return str.s; } -ZEND_API void zend_ast_convert_to_object(zval *p, zend_ast *ast, zend_class_entry *ce) -{ - if (ast->kind == ZEND_AST_CONSTANT) { - zend_string *name = zend_ast_get_constant_name(ast); - zval *zv = zend_get_constant_ex(name, ce, ast->attr); - if (UNEXPECTED(zv == NULL)) { - return; - } - - ZVAL_COPY_OR_DUP(p, zv); - } else { - zval tmp; - - if (UNEXPECTED(zend_ast_evaluate(&tmp, ast, ce) != SUCCESS)) { - return; - } - - ZVAL_COPY_VALUE(p, &tmp); - } -} - -zval *zend_ast_convert_attributes(HashTable *attributes, zend_class_entry *ce) +zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) { - zval *val, tmp; - zval *res = emalloc(sizeof(zval)); - - array_init(res); - - ZEND_HASH_FOREACH_VAL(attributes, val) { - if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - zend_ast_convert_to_object(&tmp, Z_ASTVAL_P(val), ce); - zend_hash_next_index_insert(Z_ARRVAL_P(res), &tmp); - break; - } else { - if (Z_REFCOUNTED_P(val)) { - Z_ADDREF_P(val); - } - zend_hash_next_index_insert(Z_ARRVAL_P(res), val); - } - } ZEND_HASH_FOREACH_END(); - - return res; -} + ZEND_ASSERT(attr->kind == ZEND_AST_ATTRIBUTE_LIST); -void zend_ast_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ -{ - zval *val, tmp; - zend_string *key; - - zval *zv = zend_ast_get_zval(name); - key = Z_STR_P(zv); - - if (!CG(attributes)) { - ALLOC_HASHTABLE(CG(attributes)); - zend_hash_init(CG(attributes), 8, NULL, ZVAL_PTR_DTOR, 0); - } - - ZVAL_NULL(&tmp); - val = zend_hash_add(CG(attributes), key, &tmp); - - if (!val) { - zend_error_noreturn(E_COMPILE_ERROR, "Redeclared attribute %s", ZSTR_VAL(key)); - } - - if (value) { - if (value->kind == ZEND_AST_ZVAL) { - zval *zv = zend_ast_get_zval(value); - - ZVAL_COPY_VALUE(val, zv); - } else { - zend_const_expr_to_zval(&tmp, value); - ZVAL_COPY_VALUE(val, &tmp); - } - } else { - array_init(val); - } -} -/* }}} */ - -zend_ast *zend_ast_add_attribute_value(zend_ast *list_ast, zend_ast *val_ast) /* {{{ */ -{ - zval *list, *val, arr; - - if (list_ast->kind == ZEND_AST_ZVAL) { - list = zend_ast_get_zval(list_ast); - if (Z_TYPE_P(list) != IS_ARRAY) { - array_init(&arr); - zend_hash_next_index_insert_new(Z_ARRVAL(arr), list); - ZVAL_ARR(list, Z_ARR(arr)); - } - - val = zend_ast_get_zval(val_ast); - zend_hash_next_index_insert_new(Z_ARRVAL_P(list), val); + switch (ast->kind) { + case ZEND_AST_FUNC_DECL: + case ZEND_AST_CLOSURE: + case ZEND_AST_METHOD: + case ZEND_AST_CLASS: + case ZEND_AST_ARROW_FUNC: + ((zend_ast_decl *) ast)->attributes = attr; + break; + case ZEND_AST_PROP_GROUP: + ast->child[2] = attr; + break; + case ZEND_AST_PARAM: + ast->child[3] = attr; + break; + case ZEND_AST_CLASS_CONST_DECL: + ast = zend_ast_create(ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES, ast, attr); + ast->lineno = ast->child[0]->lineno; + break; + default: + zend_error_noreturn(E_COMPILE_ERROR, "Invalid use of attributes"); } - return list_ast; + return ast; } -/* }}} */ diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index ee7965e220e05..97595b97b89d5 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -62,6 +62,7 @@ enum _zend_ast_kind { ZEND_AST_TRAIT_ADAPTATIONS, ZEND_AST_USE, ZEND_AST_TYPE_UNION, + ZEND_AST_ATTRIBUTE_LIST, /* 0 child nodes */ ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -138,7 +139,8 @@ enum _zend_ast_kind { ZEND_AST_USE_ELEM, ZEND_AST_TRAIT_ALIAS, ZEND_AST_GROUP_USE, - ZEND_AST_PROP_GROUP, + ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES, + ZEND_AST_ATTRIBUTE, /* 3 child nodes */ ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -147,13 +149,14 @@ enum _zend_ast_kind { ZEND_AST_TRY, ZEND_AST_CATCH, - ZEND_AST_PARAM, + ZEND_AST_PROP_GROUP, + ZEND_AST_PROP_ELEM, + ZEND_AST_CONST_ELEM, /* 4 child nodes */ ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_FOREACH, - ZEND_AST_PROP_ELEM, - ZEND_AST_CONST_ELEM, + ZEND_AST_PARAM, }; typedef uint16_t zend_ast_kind; @@ -191,7 +194,7 @@ typedef struct _zend_ast_decl { uint32_t flags; unsigned char *lex_pos; zend_string *doc_comment; - HashTable *attributes; + zend_ast *attributes; zend_string *name; zend_ast *child[4]; } zend_ast_decl; @@ -270,7 +273,7 @@ ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind ki ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zend_ast *list, zend_ast *op); ZEND_API zend_ast *zend_ast_create_decl( - zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, HashTable *attributes, + zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3 ); @@ -354,7 +357,6 @@ static zend_always_inline zend_ast *zend_ast_list_rtrim(zend_ast *ast) { return ast; } -void zend_ast_add_attribute(zend_ast *name, zend_ast *value); -zend_ast *zend_ast_add_attribute_value(zend_ast *list_ast, zend_ast *val_ast); -zval *zend_ast_convert_attributes(HashTable *attributes, zend_class_entry *ce); +zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr); + #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c63b7d2edda28..1c1f13d213c6c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1063,6 +1063,10 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */ ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*))); ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL); } + + if (op_array->attributes) { + GC_ADDREF(op_array->attributes); + } } else if (function->type == ZEND_INTERNAL_FUNCTION) { if (function->common.function_name) { zend_string_addref(function->common.function_name); @@ -5715,6 +5719,91 @@ static zend_bool zend_is_valid_default_value(zend_type type, zval *value) return 0; } +static void zend_compile_attribute(zval *v, zend_ast *ast) /* {{{ */ +{ + ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE); + + array_init_size(v, 1 + (ast->child[1] ? zend_ast_get_list(ast->child[1])->children : 0)); + add_next_index_str(v, zend_resolve_class_name(zend_ast_get_str(ast->child[0]), ZEND_NAME_NOT_FQ)); + + if (ast->child[1]) { + zend_ast_list *list; + uint32_t i; + + zval tmp; + zval *x; + + ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_ARG_LIST); + + ZVAL_NULL(&tmp); + + for (list = zend_ast_get_list(ast->child[1]), i = 0; i < list->children; i++) { + zend_ast *el = list->child[i]; + + x = zend_hash_next_index_insert(Z_ARRVAL_P(v), &tmp); + zend_const_expr_to_zval(x, el); + } + } +} +/* }}} */ + +static HashTable *zend_compile_attributes(zend_ast *ast) /* {{{ */ +{ + HashTable *attr; + + zend_ast_list *list; + uint32_t i; + + zval tmp; + + ZVAL_NULL(&tmp); + + ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE_LIST); + + ALLOC_HASHTABLE(attr); + zend_hash_init(attr, zend_ast_get_list(ast)->children, NULL, ZVAL_PTR_DTOR, 0); + + for (list = zend_ast_get_list(ast), i = 0; i < list->children; i++) { + zend_ast *el = list->child[i]; + zend_string *name; + + zval a; + zval *x; + + zend_compile_attribute(&a, el); + + name = zend_string_tolower(Z_STR_P(zend_hash_index_find(Z_ARRVAL(a), 0))); + x = zend_hash_find(attr, name); + + if (x) { + ZEND_ASSERT(Z_TYPE_P(x) == IS_ARRAY); + + if (Z_TYPE_P(zend_hash_index_find(Z_ARRVAL_P(x), 0)) == IS_ARRAY) { + add_next_index_zval(x, &a); + } else { + zval array; + zval ref; + + ZEND_ASSERT(Z_TYPE_P(zend_hash_index_find(Z_ARRVAL_P(x), 0)) == IS_STRING); + + ZVAL_COPY(&ref, x); + + array_init(&array); + add_next_index_zval(&array, &ref); + add_next_index_zval(&array, &a); + zend_hash_update(attr, name, &array); + } + } else { + zend_hash_add(attr, name, &a); + } + + zend_string_release(name); + } + + return attr; +} +/* }}} */ + void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); @@ -5745,6 +5834,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ zend_ast *type_ast = param_ast->child[0]; zend_ast *var_ast = param_ast->child[1]; zend_ast *default_ast = param_ast->child[2]; + zend_ast *attributes_ast = param_ast->child[3]; zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast)); zend_bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; zend_bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0; @@ -5814,6 +5904,18 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ arg_info->name = zend_string_copy(name); arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0); + if (attributes_ast) { + zval attr; + + if (!op_array->attributes) { + ALLOC_HASHTABLE(op_array->attributes); + zend_hash_init(op_array->attributes, 8, NULL, ZVAL_PTR_DTOR, 0); + } + + ZVAL_ARR(&attr, zend_compile_attributes(attributes_ast)); + zend_hash_index_add(op_array->attributes, i, &attr); + } + if (type_ast) { uint32_t default_type = default_ast ? Z_TYPE(default_node.u.constant) : IS_UNDEF; @@ -6216,42 +6318,6 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as } /* }}} */ -static HashTable* zend_compile_resolve_attributes(HashTable *declared_attributes) -{ - zval *attr, *attrname, *val, tmp; - zend_string *resolved_name; - zend_string *key; - // declared attributes are in structure: idx => ["class" => "...", 0 => arg1, 1 => arg2, ...] - // resolved attributes are in structure fqcn => [0 => arg1, 1 => arg2, ..] - HashTable *resolved_attributes; - - ALLOC_HASHTABLE(resolved_attributes); - zend_hash_init(resolved_attributes, 8, NULL, ZVAL_PTR_DTOR, 0); - - ZEND_HASH_FOREACH_STR_KEY_VAL(declared_attributes, key, attr) { - // @todo not yet "class" key with ast that stores ZEND_NAME_*, - // first step here is to have class name as key of attributes and always assume relative to imports - resolved_name = zend_resolve_class_name(key, ZEND_NAME_NOT_FQ); - - ZVAL_NULL(&tmp); - val = zend_hash_add(resolved_attributes, resolved_name, &tmp); - - if (val) { - array_init(val); - } else { - zend_error_noreturn(E_COMPILE_ERROR, "Redeclared attribute %s", ZSTR_VAL(resolved_name)); - } - - zend_hash_next_index_insert(Z_ARRVAL_P(val), attr); - Z_TRY_ADDREF_P(attr); - - } ZEND_HASH_FOREACH_END(); - - zend_hash_destroy(declared_attributes); - - return resolved_attributes; -} - void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* {{{ */ { zend_ast_decl *decl = (zend_ast_decl *) ast; @@ -6287,8 +6353,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* op_array->doc_comment = zend_string_copy(decl->doc_comment); } if (decl->attributes) { - op_array->attributes = zend_compile_resolve_attributes(decl->attributes); - decl->attributes = NULL; + op_array->attributes = zend_compile_attributes(decl->attributes); } if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) { op_array->fn_flags |= ZEND_ACC_CLOSURE; @@ -6370,7 +6435,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* } /* }}} */ -void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) /* {{{ */ +void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, HashTable *attributes) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); zend_class_entry *ce = CG(active_class_entry); @@ -6389,10 +6454,8 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) / zend_ast *name_ast = prop_ast->child[0]; zend_ast *value_ast = prop_ast->child[1]; zend_ast *doc_comment_ast = prop_ast->child[2]; - zend_ast *attributes_ast = prop_ast->child[3]; zend_string *name = zval_make_interned_string(zend_ast_get_zval(name_ast)); zend_string *doc_comment = NULL; - HashTable *attributes = NULL; zval value_zv; zend_type type = ZEND_TYPE_INIT_NONE(0); @@ -6411,10 +6474,6 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) / if (doc_comment_ast) { doc_comment = zend_string_copy(zend_ast_get_str(doc_comment_ast)); } - if (attributes_ast) { - attributes = zend_ast_get_hash(attributes_ast); - prop_ast->child[3] = NULL; - } if (flags & ZEND_ACC_FINAL) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, " @@ -6451,6 +6510,10 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) / ZVAL_UNDEF(&value_zv); } + if (attributes) { + GC_ADDREF(attributes); + } + zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, attributes, type); } } @@ -6458,10 +6521,18 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) / void zend_compile_prop_group(zend_ast *list) /* {{{ */ { + HashTable *attributes; + zend_ast *type_ast = list->child[0]; zend_ast *prop_ast = list->child[1]; - zend_compile_prop_decl(prop_ast, type_ast, list->attr); + attributes = list->child[2] ? zend_compile_attributes(list->child[2]) : NULL; + + zend_compile_prop_decl(prop_ast, type_ast, list->attr, attributes); + + if (attributes) { + zend_array_ptr_dtor(attributes); + } } /* }}} */ @@ -6477,10 +6548,11 @@ static void zend_check_const_and_trait_alias_attr(uint32_t attr, const char* ent } /* }}} */ -void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ +void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); zend_class_entry *ce = CG(active_class_entry); + HashTable *attributes; uint32_t i; if ((ce->ce_flags & ZEND_ACC_TRAIT) != 0) { @@ -6488,29 +6560,32 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ return; } + attributes = attr_ast ? zend_compile_attributes(attr_ast) : NULL; + for (i = 0; i < list->children; ++i) { zend_ast *const_ast = list->child[i]; zend_ast *name_ast = const_ast->child[0]; zend_ast *value_ast = const_ast->child[1]; zend_ast *doc_comment_ast = const_ast->child[2]; - zend_ast *attributes_ast = const_ast->child[3]; zend_string *name = zval_make_interned_string(zend_ast_get_zval(name_ast)); zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL; - HashTable *attributes = NULL; zval value_zv; if (UNEXPECTED(ast->attr & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_FINAL))) { zend_check_const_and_trait_alias_attr(ast->attr, "constant"); } - if (attributes_ast) { - attributes = zend_ast_get_hash(attributes_ast); - const_ast->child[3] = NULL; + if (attributes) { + GC_ADDREF(attributes); } zend_const_expr_to_zval(&value_zv, value_ast); zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment, attributes); } + + if (attributes) { + zend_array_ptr_dtor(attributes); + } } /* }}} */ @@ -6716,8 +6791,7 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */ ce->info.user.doc_comment = zend_string_copy(decl->doc_comment); } if (decl->attributes) { - ce->info.user.attributes = zend_compile_resolve_attributes(decl->attributes); - decl->attributes = NULL; + ce->info.user.attributes = zend_compile_attributes(decl->attributes); } if (UNEXPECTED((decl->flags & ZEND_ACC_ANON_CLASS))) { @@ -8822,7 +8896,10 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */ zend_compile_prop_group(ast); break; case ZEND_AST_CLASS_CONST_DECL: - zend_compile_class_const_decl(ast); + zend_compile_class_const_decl(ast, NULL); + break; + case ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES: + zend_compile_class_const_decl(ast->child[0], ast->child[1]); break; case ZEND_AST_USE_TRAIT: zend_compile_use_trait(ast); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index f234c76e33abe..df466d53df1aa 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -44,10 +44,6 @@ zend_string_release_ex(CG(doc_comment), 0); \ CG(doc_comment) = NULL; \ } \ - if (CG(attributes)) { \ - zend_array_ptr_dtor(CG(attributes)); \ - CG(attributes) = NULL; \ - } \ } while (0) typedef struct _zend_op_array zend_op_array; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index c674051246762..2dc769a5efe17 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -97,7 +97,6 @@ struct _zend_compiler_globals { zend_bool increment_lineno; zend_string *doc_comment; - HashTable *attributes; uint32_t extra_fn_flags; uint32_t compiler_options; /* set of ZEND_COMPILE_* constants */ diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 9bb3e3f14fdce..37b5005e35c1c 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -50,7 +50,6 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %destructor { zend_ast_destroy($$); } %destructor { if ($$) zend_string_release_ex($$, 0); } -%destructor { if ($$) zend_array_ptr_dtor($$); } %precedence PREC_ARROW_FUNCTION %precedence T_INCLUDE T_INCLUDE_ONCE T_REQUIRE T_REQUIRE_ONCE @@ -234,9 +233,9 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type group_use_declaration inline_use_declarations inline_use_declaration %type mixed_group_use_declaration use_declaration unprefixed_use_declaration %type unprefixed_use_declarations const_decl inner_statement -%type attribute_arguments expr optional_expr while_statement for_statement foreach_variable +%type expr optional_expr while_statement for_statement foreach_variable %type foreach_statement declare_statement finally_statement unset_variable variable -%type extends_from parameter optional_type_without_static argument global_var +%type extends_from annotated_parameter parameter optional_type_without_static argument global_var %type static_var class_statement annotated_class_statement trait_adaptation trait_precedence trait_alias %type absolute_trait_method_reference trait_method_reference property echo_expr %type new_expr anonymous_class class_name class_name_reference simple_variable @@ -258,6 +257,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type isset_variable type return_type type_expr type_without_static %type identifier type_expr_without_static union_type_without_static %type inline_function union_type +%type attribute_arguments attribute_decl attribute attributes %type returns_ref function fn is_reference is_variadic variable_modifiers %type method_modifiers non_empty_member_modifiers member_modifier @@ -266,8 +266,6 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type backup_lex_pos %type backup_doc_comment -%type backup_attributes - %% /* Rules */ start: @@ -315,22 +313,28 @@ name: ; attribute_arguments: - expr { $$ = $1; } - | attribute_arguments ',' expr { $$ = zend_ast_add_attribute_value($1, $3); } + expr + { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); } + | attribute_arguments ',' expr + { $$ = zend_ast_list_add($1, $3); } ; attribute_decl: - class_name_reference { zend_ast_add_attribute($1, NULL); } - | class_name_reference '(' attribute_arguments ')' { zend_ast_add_attribute($1, $3); } + class_name_reference + { $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, NULL); } + | class_name_reference '(' ')' + { $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, NULL); } + | class_name_reference '(' attribute_arguments ')' + { $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, $3); } ; attribute: - T_SL attribute_decl T_SR + T_SL attribute_decl T_SR { $$ = $2; } ; attributes: - attributes attribute - | attribute + attribute { $$ = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_LIST, $1); } + | attributes attribute { $$ = zend_ast_list_add($1, $2); } ; annotated_statement: @@ -342,7 +346,7 @@ annotated_statement: top_statement: statement { $$ = $1; } | annotated_statement { $$ = $1; } - | attributes annotated_statement { $$ = $2; } + | attributes annotated_statement { $$ = zend_ast_with_attributes($2, $1); } | T_HALT_COMPILER '(' ')' ';' { $$ = zend_ast_create(ZEND_AST_HALT_COMPILER, zend_ast_create_zval_from_long(zend_get_scanned_file_offset())); @@ -441,7 +445,7 @@ inner_statement_list: inner_statement: statement { $$ = $1; } | annotated_statement { $$ = $1; } - | attributes annotated_statement { $$ = $2; } + | attributes annotated_statement { $$ = zend_ast_with_attributes($2, $1); } | T_HALT_COMPILER '(' ')' ';' { $$ = NULL; zend_throw_exception(zend_ce_compile_error, "__HALT_COMPILER() can only be used from the outermost scope", 0); YYERROR; } @@ -513,10 +517,10 @@ unset_variable: ; function_declaration_statement: - function returns_ref T_STRING backup_doc_comment backup_attributes '(' parameter_list ')' return_type + function returns_ref T_STRING backup_doc_comment '(' parameter_list ')' return_type backup_fn_flags '{' inner_statement_list '}' backup_fn_flags - { $$ = zend_ast_create_decl(ZEND_AST_FUNC_DECL, $2 | $14, $1, $4, $5, - zend_ast_get_str($3), $7, NULL, $12, $9); CG(extra_fn_flags) = $10; } + { $$ = zend_ast_create_decl(ZEND_AST_FUNC_DECL, $2 | $13, $1, $4, + zend_ast_get_str($3), $6, NULL, $11, $8); CG(extra_fn_flags) = $9; } ; is_reference: @@ -531,11 +535,11 @@ is_variadic: class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment backup_attributes '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, $8, zend_ast_get_str($4), $5, $6, $10, NULL); } + T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL); } | T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment backup_attributes '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, $7, zend_ast_get_str($3), $4, $5, $9, NULL); } + T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL); } ; class_modifiers: @@ -551,14 +555,14 @@ class_modifier: trait_declaration_statement: T_TRAIT { $$ = CG(zend_lineno); } - T_STRING backup_doc_comment backup_attributes '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, $5, zend_ast_get_str($3), NULL, NULL, $7, NULL); } + T_STRING backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL); } ; interface_declaration_statement: T_INTERFACE { $$ = CG(zend_lineno); } - T_STRING interface_extends_list backup_doc_comment backup_attributes '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, $6, zend_ast_get_str($3), NULL, $4, $8, NULL); } + T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL); } ; extends_from: @@ -663,17 +667,22 @@ parameter_list: non_empty_parameter_list: - parameter + annotated_parameter { $$ = zend_ast_create_list(1, ZEND_AST_PARAM_LIST, $1); } - | non_empty_parameter_list ',' parameter + | non_empty_parameter_list ',' annotated_parameter { $$ = zend_ast_list_add($1, $3); } ; +annotated_parameter: + attributes parameter { $$ = zend_ast_with_attributes($2, $1); } + | parameter { $$ = $1; } +; + parameter: optional_type_without_static is_reference is_variadic T_VARIABLE - { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $2 | $3, $1, $4, NULL); } + { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $2 | $3, $1, $4, NULL, NULL); } | optional_type_without_static is_reference is_variadic T_VARIABLE '=' expr - { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $2 | $3, $1, $4, $6); } + { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $2 | $3, $1, $4, $6, NULL); } ; @@ -773,18 +782,18 @@ class_statement_list: annotated_class_statement: variable_modifiers optional_type_without_static property_list ';' - { $$ = zend_ast_create(ZEND_AST_PROP_GROUP, $2, $3); + { $$ = zend_ast_create(ZEND_AST_PROP_GROUP, $2, $3, NULL); $$->attr = $1; } | method_modifiers T_CONST class_const_list ';' { $$ = $3; $$->attr = $1; } - | method_modifiers function returns_ref identifier backup_doc_comment backup_attributes '(' parameter_list ')' + | 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 | $13, $2, $5, $6, - zend_ast_get_str($4), $8, NULL, $12, $10); CG(extra_fn_flags) = $11; } + { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1 | $12, $2, $5, + zend_ast_get_str($4), $7, NULL, $11, $9); CG(extra_fn_flags) = $10; } class_statement: annotated_class_statement { $$ = $1; } - | attributes annotated_class_statement { $$ = $2; } + | attributes annotated_class_statement { $$ = zend_ast_with_attributes($2, $1); } | T_USE class_name_list trait_adaptations { $$ = zend_ast_create(ZEND_AST_USE_TRAIT, $2, $3); } ; @@ -876,10 +885,10 @@ property_list: ; property: - T_VARIABLE backup_doc_comment backup_attributes - { $$ = zend_ast_create(ZEND_AST_PROP_ELEM, $1, NULL, ($2 ? zend_ast_create_zval_from_str($2) : NULL), ($3 ? zend_ast_create_zval_from_hash($3) : NULL)); } - | T_VARIABLE '=' expr backup_doc_comment backup_attributes - { $$ = zend_ast_create(ZEND_AST_PROP_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), ($5 ? zend_ast_create_zval_from_hash($5) : NULL)); } + T_VARIABLE backup_doc_comment + { $$ = zend_ast_create(ZEND_AST_PROP_ELEM, $1, NULL, ($2 ? zend_ast_create_zval_from_str($2) : NULL)); } + | T_VARIABLE '=' expr backup_doc_comment + { $$ = zend_ast_create(ZEND_AST_PROP_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); } ; class_const_list: @@ -888,11 +897,11 @@ class_const_list: ; class_const_decl: - identifier '=' expr backup_doc_comment backup_attributes { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), ($5 ? zend_ast_create_zval_from_hash($5) : NULL)); } + identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); } ; const_decl: - T_STRING '=' expr backup_doc_comment backup_attributes { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), ($5 ? zend_ast_create_zval_from_hash($5) : NULL)); } + T_STRING '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); } ; echo_expr_list: @@ -915,10 +924,10 @@ non_empty_for_exprs: anonymous_class: T_CLASS { $$ = CG(zend_lineno); } ctor_arguments - extends_from implements_list backup_doc_comment backup_attributes '{' class_statement_list '}' { + extends_from implements_list backup_doc_comment '{' class_statement_list '}' { zend_ast *decl = zend_ast_create_decl( - ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS, $2, $6, $7, NULL, - $4, $5, $9, NULL); + ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS, $2, $6, NULL, + $4, $5, $8, NULL); $$ = zend_ast_create(ZEND_AST_NEW, decl, $3); } ; @@ -928,6 +937,8 @@ new_expr: { $$ = zend_ast_create(ZEND_AST_NEW, $2, $3); } | T_NEW anonymous_class { $$ = $2; } + | T_NEW attributes anonymous_class + { zend_ast_with_attributes($3->child[0], $2); $$ = $3; } ; expr: @@ -1047,22 +1058,25 @@ expr: | T_YIELD expr T_DOUBLE_ARROW expr { $$ = zend_ast_create(ZEND_AST_YIELD, $4, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; } | T_YIELD_FROM expr { $$ = zend_ast_create(ZEND_AST_YIELD_FROM, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; } | inline_function { $$ = $1; } + | attributes inline_function { $$ = zend_ast_with_attributes($2, $1); } | T_STATIC inline_function { $$ = $2; ((zend_ast_decl *) $$)->flags |= ZEND_ACC_STATIC; } + | attributes T_STATIC inline_function + { $$ = zend_ast_with_attributes($3, $1); ((zend_ast_decl *) $$)->flags |= ZEND_ACC_STATIC; } ; inline_function: - function returns_ref backup_doc_comment backup_attributes '(' parameter_list ')' lexical_vars return_type + function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars return_type backup_fn_flags '{' inner_statement_list '}' backup_fn_flags - { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $14, $1, $3, $4, + { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $13, $1, $3, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), - $6, $8, $12, $9); CG(extra_fn_flags) = $10; } - | fn returns_ref '(' parameter_list ')' return_type backup_doc_comment backup_attributes T_DOUBLE_ARROW backup_fn_flags backup_lex_pos expr backup_fn_flags - { $$ = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, $2 | $13, $1, $7, $8, + $5, $7, $11, $8); CG(extra_fn_flags) = $9; } + | fn returns_ref '(' parameter_list ')' return_type backup_doc_comment T_DOUBLE_ARROW backup_fn_flags backup_lex_pos expr backup_fn_flags + { $$ = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, $2 | $12, $1, $7, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), $4, NULL, - zend_ast_create(ZEND_AST_RETURN, $12), $6); - ((zend_ast_decl *) $$)->lex_pos = $11; - CG(extra_fn_flags) = $10; } + zend_ast_create(ZEND_AST_RETURN, $11), $6); + ((zend_ast_decl *) $$)->lex_pos = $10; + CG(extra_fn_flags) = $9; } ; fn: @@ -1085,10 +1099,6 @@ backup_lex_pos: %empty { $$ = LANG_SCNG(yy_text); } ; -backup_attributes: - %empty { $$ = CG(attributes); CG(attributes) = NULL; } -; - returns_ref: %empty { $$ = 0; } | '&' { $$ = ZEND_ACC_RETURN_REFERENCE; } diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 465b3786d3ba0..0c066c52ab304 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -190,7 +190,6 @@ void startup_scanner(void) { CG(parse_error) = 0; CG(doc_comment) = NULL; - CG(attributes) = NULL; CG(extra_fn_flags) = 0; zend_stack_init(&SCNG(state_stack), sizeof(int)); zend_ptr_stack_init(&SCNG(heredoc_label_stack)); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 86dc6d5761eed..6b504cb633b40 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -137,7 +137,7 @@ typedef struct _type_reference { /* Struct for attributes */ typedef struct _attribute_reference { zend_string *name; - zval *arguments; + zval arguments; } attribute_reference; typedef enum { @@ -257,9 +257,8 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ break; case REF_TYPE_ATTRIBUTE: attr_reference = (attribute_reference*)intern->ptr; - if (attr_reference->arguments) { - zval_ptr_dtor(attr_reference->arguments); - } + zend_string_release(attr_reference->name); + zval_ptr_dtor(&attr_reference->arguments); efree(attr_reference); break; case REF_TYPE_GENERATOR: @@ -1049,29 +1048,115 @@ static void reflection_attribute_factory(zval *object, zend_string *name, zval * reflection_instantiate(reflection_attribute_ptr, object); intern = Z_REFLECTION_P(object); reference = (attribute_reference*) emalloc(sizeof(attribute_reference)); - reference->name = name; - reference->arguments = arguments; + reference->name = zend_string_copy(name); + ZVAL_COPY(&reference->arguments, arguments); intern->ptr = reference; intern->ref_type = REF_TYPE_ATTRIBUTE; } /* }}} */ +static int convert_ast_to_zval(zval *ret, zend_ast *ast, zend_class_entry *ce) +{ + if (ast->kind == ZEND_AST_CONSTANT) { + zend_string *name = zend_ast_get_constant_name(ast); + zval *zv = zend_get_constant_ex(name, ce, ast->attr); + + if (UNEXPECTED(zv == NULL)) { + return FAILURE; + } + + ZVAL_COPY_OR_DUP(ret, zv); + } else { + zval tmp; + + if (UNEXPECTED(zend_ast_evaluate(&tmp, ast, ce) != SUCCESS)) { + return FAILURE; + } + + ZVAL_COPY_OR_DUP(ret, &tmp); + zval_ptr_dtor(&tmp); + } + + return SUCCESS; +} + +static int convert_ast_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) +{ + Bucket *p; + zval tmp; + + array_init(ret); + + ZEND_HASH_FOREACH_BUCKET(attributes, p) { + if (!p->key && p->h == 0) { + continue; + } + + if (Z_TYPE(p->val) == IS_CONSTANT_AST) { + if (FAILURE == convert_ast_to_zval(&tmp, Z_ASTVAL(p->val), ce)) { + return FAILURE; + } + + add_next_index_zval(ret, &tmp); + } else { + Z_TRY_ADDREF(p->val); + add_next_index_zval(ret, &p->val); + } + } ZEND_HASH_FOREACH_END(); + + return SUCCESS; +} -static void convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) +static int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) { - zend_string *attribute_name; - zval *attr, *object; - zval *converted_attributes; + Bucket *p; + zval *v; + + zval result; + zval obj; array_init(ret); - ZEND_HASH_FOREACH_STR_KEY_VAL(attributes, attribute_name, attr) { - if (attribute_name) { - converted_attributes = zend_ast_convert_attributes(Z_ARRVAL_P(attr), ce); - reflection_attribute_factory(object, attribute_name, converted_attributes); - zend_hash_next_index_insert(Z_ARRVAL_P(ret), object); + ZEND_HASH_FOREACH_BUCKET(attributes, p) { + if (!p->key) { + // Skip inlined parameter annotations. + continue; + } + + ZEND_ASSERT(Z_TYPE(p->val) == IS_ARRAY); + + v = zend_hash_index_find(Z_ARRVAL(p->val), 0); + + if (Z_TYPE_P(v) == IS_STRING) { + if (FAILURE == convert_ast_attributes(&result, Z_ARRVAL(p->val), ce)) { + zval_ptr_dtor(ret); + return FAILURE; + } + + reflection_attribute_factory(&obj, Z_STR_P(v), &result); + add_next_index_zval(ret, &obj); + zval_ptr_dtor(&result); + } else { + zval *zv; + + ZEND_ASSERT(Z_TYPE_P(v) == IS_ARRAY); + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL(p->val), zv) { + ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY); + + if (FAILURE == convert_ast_attributes(&result, Z_ARRVAL_P(zv), ce)) { + zval_ptr_dtor(ret); + return FAILURE; + } + + reflection_attribute_factory(&obj, Z_STR_P(zend_hash_index_find(Z_ARRVAL_P(zv), 0)), &result); + add_next_index_zval(ret, &obj); + zval_ptr_dtor(&result); + } ZEND_HASH_FOREACH_END(); } } ZEND_HASH_FOREACH_END(); + + return SUCCESS; } static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent) /* {{{ */ @@ -1692,11 +1777,18 @@ ZEND_METHOD(reflection_function, getAttributes) return; } GET_REFLECTION_OBJECT_PTR(fptr); + if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.attributes) { - convert_attributes(return_value, fptr->op_array.attributes, NULL); - } else { - array_init(return_value); + zval ret; + + if (FAILURE == convert_attributes(&ret, fptr->op_array.attributes, fptr->common.scope)) { + RETURN_THROWS(); + } + + RETURN_ZVAL(&ret, 1, 1); } + + RETURN_EMPTY_ARRAY(); } /* }}} */ @@ -2622,6 +2714,37 @@ ZEND_METHOD(reflection_parameter, canBePassedByValue) } /* }}} */ +/* {{{ proto public bool ReflectionParameter::getAttributes(?string $name = null) + Get parameter attributes. */ +ZEND_METHOD(reflection_parameter, getAttributes) +{ + reflection_object *intern; + parameter_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT_PTR(param); + + if (param->fptr->type == ZEND_USER_FUNCTION && param->fptr->op_array.attributes) { + zval *attr; + + if (NULL != (attr = zend_hash_index_find(param->fptr->op_array.attributes, param->offset))) { + zval ret; + + ZEND_ASSERT(Z_TYPE_P(attr) == IS_ARRAY); + + if (FAILURE == convert_attributes(&ret, Z_ARRVAL_P(attr), param->fptr->common.scope)) { + RETURN_THROWS(); + } + + RETURN_ZVAL(&ret, 1, 1); + } + } + + RETURN_EMPTY_ARRAY(); +} + /* {{{ proto public bool ReflectionParameter::getPosition() Returns whether this parameter is an optional parameter */ ZEND_METHOD(reflection_parameter, getPosition) @@ -3695,11 +3818,18 @@ ZEND_METHOD(reflection_class_constant, getAttributes) return; } GET_REFLECTION_OBJECT_PTR(ref); + if (ref->attributes) { - convert_attributes(return_value, ref->attributes, ref->ce); - } else { - array_init(return_value); + zval ret; + + if (FAILURE == convert_attributes(&ret, ref->attributes, ref->ce)) { + RETURN_THROWS(); + } + + RETURN_ZVAL(&ret, 1, 1); } + + RETURN_EMPTY_ARRAY(); } /* }}} */ @@ -4076,11 +4206,18 @@ ZEND_METHOD(reflection_class, getAttributes) return; } GET_REFLECTION_OBJECT_PTR(ce); + if (ce->type == ZEND_USER_CLASS && ce->info.user.attributes) { - convert_attributes(return_value, ce->info.user.attributes, ce); - } else { - array_init(return_value); + zval ret; + + if (FAILURE == convert_attributes(&ret, ce->info.user.attributes, ce)) { + RETURN_THROWS(); + } + + RETURN_ZVAL(&ret, 1, 1); } + + RETURN_EMPTY_ARRAY(); } /* }}} */ @@ -5594,11 +5731,18 @@ ZEND_METHOD(reflection_property, getAttributes) return; } GET_REFLECTION_OBJECT_PTR(ref); + if (ref->prop->attributes) { - convert_attributes(return_value, ref->prop->attributes, ref->prop->ce); - } else { - array_init(return_value); + zval ret; + + if (FAILURE == convert_attributes(&ret, ref->prop->attributes, ref->prop->ce)) { + RETURN_THROWS(); + } + + RETURN_ZVAL(&ret, 1, 1); } + + RETURN_EMPTY_ARRAY(); } /* }}} */ @@ -6338,7 +6482,7 @@ ZEND_METHOD(reflection_attribute, getName) } GET_REFLECTION_OBJECT_PTR(param); - RETVAL_STR(param->name); + RETVAL_STR_COPY(param->name); } /* }}} */ @@ -6358,7 +6502,7 @@ ZEND_METHOD(reflection_attribute, getArguments) } GET_REFLECTION_OBJECT_PTR(param); - ZVAL_COPY(return_value, param->arguments); + RETVAL_ZVAL(¶m->arguments, 1, 0); } /* }}} */ @@ -6576,6 +6720,7 @@ static const zend_function_entry reflection_parameter_functions[] = { ZEND_ME(reflection_parameter, isArray, arginfo_class_ReflectionParameter_isArray, 0) ZEND_ME(reflection_parameter, isCallable, arginfo_class_ReflectionParameter_isCallable, 0) ZEND_ME(reflection_parameter, allowsNull, arginfo_class_ReflectionParameter_allowsNull, 0) + ZEND_ME(reflection_parameter, getAttributes, arginfo_class_ReflectionParameter_getAttributes, 0) ZEND_ME(reflection_parameter, getPosition, arginfo_class_ReflectionParameter_getPosition, 0) ZEND_ME(reflection_parameter, isOptional, arginfo_class_ReflectionParameter_isOptional, 0) ZEND_ME(reflection_parameter, isDefaultValueAvailable, arginfo_class_ReflectionParameter_isDefaultValueAvailable, 0) diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index c9b666e90c050..8772e062029fa 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -96,7 +96,7 @@ public function hasReturnType() {} public function getReturnType() {} /** @return ReflectionAttribute[] */ - public function getAttributes($name = null) {} + public function getAttributes(?string $name = null) {} } class ReflectionFunction extends ReflectionFunctionAbstract @@ -358,7 +358,7 @@ public function getNamespaceName() {} public function getShortName() {} /** @return ReflectionAttribute[] */ - public function getAttributes($name = null) {} + public function getAttributes(?string $name = null) {} } class ReflectionObject extends ReflectionClass @@ -426,7 +426,7 @@ public function hasDefaultValue(): bool {} public function getDefaultValue() {} /** @return ReflectionAttribute[] */ - public function getAttributes($name = null) {} + public function getAttributes(?string $name = null) {} } class ReflectionClassConstant implements Reflector @@ -463,7 +463,7 @@ public function getDeclaringClass() {} public function getDocComment() {} /** @return ReflectionAttribute[] */ - public function getAttributes($name = null) {} + public function getAttributes(?string $name = null) {} } class ReflectionParameter implements Reflector @@ -531,6 +531,9 @@ public function getDefaultValueConstantName() {} /** @return bool */ public function isVariadic() {} + + /** @return ReflectionAttribute[] */ + public function getAttributes(?string $name = null) {} } abstract class ReflectionType diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index f04cd0a1fa70a..d796577499ac8 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -60,7 +60,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionFunctionAbstract_getReturnType arginfo_class_Reflector___toString ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getAttributes, 0, 0, 0) - ZEND_ARG_INFO(0, name) + ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunction___construct, 0, 0, 1) @@ -398,6 +398,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionParameter_isVariadic arginfo_class_Reflector___toString +#define arginfo_class_ReflectionParameter_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes + #define arginfo_class_ReflectionType___clone arginfo_class_Reflector___toString #define arginfo_class_ReflectionType_allowsNull arginfo_class_Reflector___toString diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index b5deca0a957a8..b1d94f2b26742 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -27,7 +27,7 @@ Class [ class ReflectionClass implements Reflector ] { Property [ public $name ] } - - Methods [53] { + - Methods [54] { Method [ final private method __clone ] { - Parameters [0] { @@ -109,7 +109,8 @@ Class [ class ReflectionClass implements Reflector ] { Method [ public method getAttributes ] { - - Parameters [0] { + - Parameters [1] { + Parameter #0 [ $name ] } } diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt index 61a0d24b0887a..81d3c8d55b146 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -8,7 +8,7 @@ $ext = new ReflectionExtension('reflection'); var_dump($ext->getClasses()); ?> --EXPECT-- -array(18) { +array(19) { ["ReflectionException"]=> object(ReflectionClass)#2 (1) { ["name"]=> @@ -99,4 +99,9 @@ array(18) { ["name"]=> string(19) "ReflectionReference" } + ["ReflectionAttribute"]=> + object(ReflectionClass)#20 (1) { + ["name"]=> + string(19) "ReflectionAttribute" + } } diff --git a/ext/reflection/tests/attributes/ast.phpt b/ext/reflection/tests/attributes/ast.phpt new file mode 100644 index 0000000000000..5805092438ded --- /dev/null +++ b/ext/reflection/tests/attributes/ast.phpt @@ -0,0 +1,68 @@ +--TEST-- +Attributes can deal with AST nodes. +--FILE-- + V1])>> +class C1 +{ + public const BAR = 'bar'; +} + +$ref = new \ReflectionClass(C1::class); +$attr = $ref->getAttributes(); +var_dump(count($attr)); + +$args = $attr[0]->getArguments(); +var_dump(count($args), $args[0][V1] === V1); + +echo "\n"; + +<> +class C2 { } + +$ref = new \ReflectionClass(C2::class); +$attr = $ref->getAttributes(); +var_dump(count($attr)); + +$args = $attr[0]->getArguments(); +var_dump(count($args)); +var_dump($args[0] === V1); +var_dump($args[1] === 3); +var_dump($args[2] === C1::class); + +echo "\n"; + +<> +class C3 +{ + private const FOO = 'foo'; +} + +$ref = new \ReflectionClass(C3::class); +$attr = $ref->getAttributes(); +var_dump(count($attr)); + +$args = $attr[0]->getArguments(); +var_dump(count($args)); +var_dump($args[0] === 'foo'); +var_dump($args[1] === C1::BAR); + +?> +--EXPECT-- +int(1) +int(1) +bool(true) + +int(1) +int(3) +bool(true) +bool(true) +bool(true) + +int(1) +int(2) +bool(true) +bool(true) diff --git a/ext/reflection/tests/attributes/placement.phpt b/ext/reflection/tests/attributes/placement.phpt new file mode 100644 index 0000000000000..a99500570a5df --- /dev/null +++ b/ext/reflection/tests/attributes/placement.phpt @@ -0,0 +1,112 @@ +--TEST-- +Attributes can be placed on all supported elements. +--FILE-- +> +class Foo +{ + <> + public const FOO = 'foo', BAR = 'bar'; + + <> + public $x, $y; + + <> + public function foo(<> $a, <> $b) { } +} + +$object = new <> class () { }; + +<> +function f1() { } + +$f2 = <> function () { }; + +$f3 = <> fn () => 1; + +$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], + new \ReflectionObject($object), + new \ReflectionFunction('f1'), + new \ReflectionFunction($f2), + new \ReflectionFunction($f3) +]; + +foreach ($sources as $r) { + foreach ($r->getAttributes() as $attr) { + var_dump($attr->getName(), $attr->getArguments()); + } +} + +?> +--EXPECT-- +string(2) "A1" +array(1) { + [0]=> + int(1) +} +string(2) "A1" +array(1) { + [0]=> + int(2) +} +string(2) "A1" +array(1) { + [0]=> + int(2) +} +string(2) "A1" +array(1) { + [0]=> + int(3) +} +string(2) "A1" +array(1) { + [0]=> + int(3) +} +string(2) "A1" +array(1) { + [0]=> + int(4) +} +string(2) "A1" +array(1) { + [0]=> + int(5) +} +string(2) "A1" +array(1) { + [0]=> + int(6) +} +string(2) "A1" +array(1) { + [0]=> + int(7) +} +string(2) "A1" +array(1) { + [0]=> + int(8) +} +string(2) "A1" +array(1) { + [0]=> + int(9) +} +string(2) "A1" +array(1) { + [0]=> + int(10) +}