From 5b5e895c9b655337a92768ad47a098d9dfd5ce73 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 20 Feb 2020 21:10:00 +0100 Subject: [PATCH 01/37] Adopt dstogov Attributes PR #1878 to php8 without Ast Nodes but no values yet. --- Zend/tests/attributes_001.phpt | 203 ++++++++++++++++++ Zend/tests/attributes_002.phpt | 39 ++++ Zend/zend.h | 1 + Zend/zend_API.c | 14 +- Zend/zend_API.h | 8 +- Zend/zend_ast.c | 71 +++++- Zend/zend_ast.h | 19 +- Zend/zend_compile.c | 70 +++++- Zend/zend_compile.h | 10 + Zend/zend_globals.h | 1 + Zend/zend_inheritance.c | 7 +- Zend/zend_language_parser.y | 121 +++++++---- Zend/zend_language_scanner.l | 1 + Zend/zend_opcode.c | 16 ++ Zend/zend_types.h | 8 + Zend/zend_variables.h | 9 + ext/opcache/zend_file_cache.c | 67 ++++++ ext/opcache/zend_persist.c | 43 ++++ ext/opcache/zend_persist_calc.c | 17 ++ ext/reflection/php_reflection.c | 80 +++++++ .../tests/ReflectionClass_toString_001.phpt | 6 + 21 files changed, 751 insertions(+), 60 deletions(-) create mode 100644 Zend/tests/attributes_001.phpt create mode 100644 Zend/tests/attributes_002.phpt diff --git a/Zend/tests/attributes_001.phpt b/Zend/tests/attributes_001.phpt new file mode 100644 index 0000000000000..e3d0236b6368c --- /dev/null +++ b/Zend/tests/attributes_001.phpt @@ -0,0 +1,203 @@ +--TEST-- +Basic attributes usage +--FILE-- +getAttributes()); + +// Function attributes +<> +function foo() { +} +$r = new ReflectionFunction("foo"); +var_dump($r->getAttributes()); + +// Class attributes +<> +class Bar { + <> + const C = 2; + <> + public $x = 3; + +} +$r = new ReflectionClass("Bar"); +var_dump($r->getAttributes()); +$r1 = $r->getReflectionConstant("C"); +var_dump($r1->getAttributes()); +$r2 = $r->getProperty("x"); +var_dump($r2->getAttributes()); + +// Multiple attributes with multiple values +<> +function f1() {} +$r = new ReflectionFunction("f1"); +var_dump($r->getAttributes()); + +// Attributes with AST +<1,"b"=>2])>> +function f2() {} +$r = new ReflectionFunction("f2"); +var_dump($r->getAttributes()); + +// Attributes with namespaces +<> +function f4() { +} +$r = new ReflectionFunction("f4"); +var_dump($r->getAttributes()); +?> +--EXPECT-- +array(0) { +} +array(1) { + ["TestFunction"]=> + array(0) { + } +} +array(1) { + ["TestClass"]=> + array(0) { + } +} +array(1) { + ["TestConst"]=> + array(0) { + } +} +array(1) { + ["TestProp"]=> + array(0) { + } +} +array(3) { + ["a1"]=> + array(0) { + } + ["a2"]=> + array(1) { + [0]=> + int(1) + } + ["a3"]=> + array(2) { + [0]=> + int(1) + [1]=> + int(2) + } +} +array(4) { + ["a1"]=> + array(0) { + } + ["a2"]=> + array(1) { + [0]=> + object(ast\Node)#4 (4) { + ["kind"]=> + int(520) + ["flags"]=> + int(1) + ["lineno"]=> + NULL + ["children"]=> + array(2) { + [0]=> + int(1) + [1]=> + string(1) "a" + } + } + } + ["a3"]=> + array(2) { + [0]=> + object(ast\Node)#5 (4) { + ["kind"]=> + int(520) + ["flags"]=> + int(1) + ["lineno"]=> + NULL + ["children"]=> + array(2) { + [0]=> + int(1) + [1]=> + string(1) "b" + } + } + [1]=> + object(ast\Node)#6 (4) { + ["kind"]=> + int(520) + ["flags"]=> + int(1) + ["lineno"]=> + NULL + ["children"]=> + array(2) { + [0]=> + int(2) + [1]=> + string(1) "c" + } + } + } + ["a4"]=> + array(1) { + [0]=> + object(ast\Node)#7 (4) { + ["kind"]=> + int(130) + ["flags"]=> + int(0) + ["lineno"]=> + NULL + ["children"]=> + array(2) { + [0]=> + object(ast\Node)#8 (4) { + ["kind"]=> + int(525) + ["flags"]=> + int(0) + ["lineno"]=> + NULL + ["children"]=> + array(2) { + [0]=> + int(1) + [1]=> + string(1) "a" + } + } + [1]=> + object(ast\Node)#9 (4) { + ["kind"]=> + int(525) + ["flags"]=> + int(0) + ["lineno"]=> + NULL + ["children"]=> + array(2) { + [0]=> + int(2) + [1]=> + string(1) "b" + } + } + } + } + } +} +array(1) { + ["Foo\Bar"]=> + array(0) { + } +} diff --git a/Zend/tests/attributes_002.phpt b/Zend/tests/attributes_002.phpt new file mode 100644 index 0000000000000..83d64bf0b70f1 --- /dev/null +++ b/Zend/tests/attributes_002.phpt @@ -0,0 +1,39 @@ +--TEST-- +Doctrine-like attributes usage +--FILE-- +name = $name; + } + } + + function GetClassAttributes($class_name) { + $reflClass = new \ReflectionClass($class_name); + $attrs = $reflClass->getAttributes(); + foreach ($attrs as $name => &$values) { + $name = "Doctrine\\" . $name; + $values = new $name(...$values); + } + return $attrs; + } +} + +namespace { + <> + class User {} + + var_dump(Doctrine\ORM\GetClassAttributes("User")); +} +?> +--EXPECT-- +array(1) { + ["ORM\Entity"]=> + object(Doctrine\ORM\Entity)#2 (1) { + ["name":"Doctrine\ORM\Entity":private]=> + string(4) "user" + } +} diff --git a/Zend/zend.h b/Zend/zend.h index b965a765e35f9..9b70783959bdd 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -177,6 +177,7 @@ struct _zend_class_entry { uint32_t line_start; uint32_t line_end; zend_string *doc_comment; + HashTable *attributes; } user; struct { const struct _zend_function_entry *builtin_functions; diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 595208033b189..6d64b04be35c3 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3491,7 +3491,7 @@ static zend_always_inline zend_bool is_persistent_class(zend_class_entry *ce) { && ce->info.internal.module->type == MODULE_PERSISTENT; } -ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type) /* {{{ */ +ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes, zend_type type) /* {{{ */ { zend_property_info *property_info, *property_info_ptr; @@ -3590,6 +3590,7 @@ ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name property_info->name = zend_new_interned_string(property_info->name); property_info->flags = access_type; property_info->doc_comment = doc_comment; + property_info->attributes = attributes; property_info->ce = ce; property_info->type = type; @@ -3726,16 +3727,16 @@ ZEND_API int zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, ze } /* }}} */ -ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment) /* {{{ */ +ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes) /* {{{ */ { - return zend_declare_typed_property(ce, name, property, access_type, doc_comment, (zend_type) ZEND_TYPE_INIT_NONE(0)); + return zend_declare_typed_property(ce, name, property, access_type, doc_comment, attributes, (zend_type) ZEND_TYPE_INIT_NONE(0)); } /* }}} */ ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type) /* {{{ */ { zend_string *key = zend_string_init(name, name_length, is_persistent_class(ce)); - int ret = zend_declare_property_ex(ce, key, property, access_type, NULL); + int ret = zend_declare_property_ex(ce, key, property, access_type, NULL, NULL); zend_string_release(key); return ret; } @@ -3795,7 +3796,7 @@ ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *nam } /* }}} */ -ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment) /* {{{ */ +ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment, HashTable *attributes) /* {{{ */ { zend_class_constant *c; @@ -3822,6 +3823,7 @@ ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *n ZVAL_COPY_VALUE(&c->value, value); Z_ACCESS_FLAGS(c->value) = access_type; c->doc_comment = doc_comment; + c->attributes = attributes; c->ce = ce; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; @@ -3847,7 +3849,7 @@ ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, } else { key = zend_string_init(name, name_length, 0); } - ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL); + ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL, NULL); zend_string_release(key); return ret; } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 9fd656b276db9..18db4e9251a51 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -333,9 +333,9 @@ ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_nam ZEND_API const char *zend_get_module_version(const char *module_name); ZEND_API int zend_get_module_started(const char *module_name); -ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type); +ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes, zend_type type); -ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment); +ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes); ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type); ZEND_API int zend_declare_property_null(zend_class_entry *ce, const char *name, size_t name_length, int access_type); ZEND_API int zend_declare_property_bool(zend_class_entry *ce, const char *name, size_t name_length, zend_long value, int access_type); @@ -344,7 +344,7 @@ ZEND_API int zend_declare_property_double(zend_class_entry *ce, const char *name ZEND_API int zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type); ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_len, int access_type); -ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment); +ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment, HashTable *attributes); ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value); ZEND_API int zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length); ZEND_API int zend_declare_class_constant_long(zend_class_entry *ce, const char *name, size_t name_length, zend_long value); @@ -650,6 +650,7 @@ END_EXTERN_C() #define RETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value) #define RETVAL_RES(r) ZVAL_RES(return_value, r) #define RETVAL_ARR(r) ZVAL_ARR(return_value, r) +#define RETVAL_IMMUTABLE_ARR(r) ZVAL_IMMUTABLE_ARR(return_value, r) #define RETVAL_EMPTY_ARRAY() ZVAL_EMPTY_ARRAY(return_value) #define RETVAL_OBJ(r) ZVAL_OBJ(return_value, r) #define RETVAL_COPY(zv) ZVAL_COPY(return_value, zv) @@ -671,6 +672,7 @@ END_EXTERN_C() #define RETURN_EMPTY_STRING() do { RETVAL_EMPTY_STRING(); return; } while (0) #define RETURN_RES(r) do { RETVAL_RES(r); return; } while (0) #define RETURN_ARR(r) do { RETVAL_ARR(r); return; } while (0) +#define RETURN_IMMUTABLE_ARR(r) do { RETVAL_IMMUTABLE_ARR(r); return; } while (0) #define RETURN_EMPTY_ARRAY() do { RETVAL_EMPTY_ARRAY(); return; } while (0) #define RETURN_OBJ(r) do { RETVAL_OBJ(r); return; } while (0) #define RETURN_COPY(zv) do { RETVAL_COPY(zv); return; } while (0) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 7cfc0450fd84e..49ca40c41ed18 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, + zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, HashTable *attributes, zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3 ) { zend_ast_decl *ast; @@ -126,6 +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->name = name; ast->child[0] = child0; ast->child[1] = child1; @@ -857,6 +858,9 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) if (decl->doc_comment) { zend_string_release_ex(decl->doc_comment, 0); } + if (decl->attributes) { + zend_array_ptr_dtor(decl->attributes); + } zend_ast_destroy(decl->child[0]); zend_ast_destroy(decl->child[1]); zend_ast_destroy(decl->child[2]); @@ -2111,3 +2115,68 @@ ZEND_API ZEND_COLD zend_string *zend_ast_export(const char *prefix, zend_ast *as smart_str_0(&str); return str.s; } + +ZEND_API zend_class_entry *zend_ast_node_ce = NULL; +ZEND_API zend_class_entry *zend_ast_decl_ce = NULL; + +ZEND_API void zend_ast_convert_attributes(zval *ret, HashTable *attributes) +{ + zval *val, tmp; + HashTable *ht, *ht2, *res_ht; + zend_string *key; + int convert_ast = 0; + + ZEND_HASH_FOREACH_VAL(attributes, val) { + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + convert_ast = 1; + break; + } else if (Z_TYPE_P(val) == IS_ARRAY) { + ht = Z_ARR_P(val); + ZEND_HASH_FOREACH_VAL(ht, val) { + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + convert_ast = 1; + break; + } + } ZEND_HASH_FOREACH_END(); + if (convert_ast) { + break; + } + } + } ZEND_HASH_FOREACH_END(); + + if (convert_ast) { + array_init_size(ret, zend_hash_num_elements(attributes)); + res_ht = Z_ARR_P(ret); + ZEND_HASH_FOREACH_STR_KEY_VAL(attributes, key, val) { + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + zend_hash_add_new(res_ht, key, &tmp); + } else if (Z_TYPE_P(val) == IS_ARRAY) { + ht = Z_ARR_P(val); + array_init_size(&tmp, zend_hash_num_elements(ht)); + val = zend_hash_add_new(res_ht, key, &tmp); + ht2 = Z_ARR_P(val); + ZEND_HASH_FOREACH_VAL(ht, val) { + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + zend_hash_next_index_insert_new(ht2, &tmp); + } else { + if (Z_REFCOUNTED_P(val)) { + Z_ADDREF_P(val); + } + zend_hash_next_index_insert_new(ht2, val); + } + } ZEND_HASH_FOREACH_END(); + } else { + if (Z_REFCOUNTED_P(val)) { + Z_ADDREF_P(val); + } + zend_hash_add_new(res_ht, key, val); + } + } ZEND_HASH_FOREACH_END(); + } else if (GC_FLAGS(attributes) & IS_ARRAY_IMMUTABLE) { + ZVAL_IMMUTABLE_ARR(ret, attributes); + } else { + GC_ADDREF(attributes); + ZVAL_ARR(ret, attributes); + } +} + diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 5b8aae6f96c25..3f5e80ea70e5a 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -148,12 +148,12 @@ enum _zend_ast_kind { ZEND_AST_TRY, ZEND_AST_CATCH, ZEND_AST_PARAM, - 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, }; typedef uint16_t zend_ast_kind; @@ -191,6 +191,7 @@ typedef struct _zend_ast_decl { uint32_t flags; unsigned char *lex_pos; zend_string *doc_comment; + HashTable *attributes; zend_string *name; zend_ast *child[4]; } zend_ast_decl; @@ -269,7 +270,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, + zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, HashTable *attributes, zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3 ); @@ -311,6 +312,12 @@ static zend_always_inline zend_string *zend_ast_get_constant_name(zend_ast *ast) return Z_STR(((zend_ast_zval *) ast)->val); } +static zend_always_inline HashTable *zend_ast_get_hash(zend_ast *ast) { + zval *zv = zend_ast_get_zval(ast); + ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY); + return Z_ARR_P(zv); +} + static zend_always_inline uint32_t zend_ast_get_num_children(zend_ast *ast) { ZEND_ASSERT(!zend_ast_is_list(ast)); return ast->kind >> ZEND_AST_NUM_CHILDREN_SHIFT; @@ -324,6 +331,12 @@ static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) { } } +static zend_always_inline zend_ast *zend_ast_create_zval_from_hash(HashTable *hash) { + zval zv; + ZVAL_ARR(&zv, hash); + return zend_ast_create_zval(&zv); +} + static zend_always_inline zend_ast *zend_ast_create_binary_op(uint32_t opcode, zend_ast *op0, zend_ast *op1) { return zend_ast_create_ex(ZEND_AST_BINARY_OP, opcode, op0, op1); } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7ec740ae4e161..7e8d0bfdb4962 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1825,6 +1825,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify } else { ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); ce->info.user.doc_comment = NULL; + ce->info.user.attributes = NULL; } ce->default_properties_count = 0; @@ -1916,6 +1917,50 @@ zend_ast *zend_negate_num_string(zend_ast *ast) /* {{{ */ } /* }}} */ +void zend_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ +{ + zval *val, tmp; + zend_string *key = zend_ast_get_str(name); + +#if 1 /* SPECIAL_ATTRIBUTE */ + if (ZSTR_LEN(key) >= 2 && ZSTR_VAL(key)[0] == '_' && ZSTR_VAL(key)[1] == '_') { + zend_error_noreturn(E_COMPILE_ERROR, "Unknown special attribute %s", ZSTR_VAL(key)); + } +#endif + 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)); + } + array_init(val); + zend_string_release(key); +} +/* }}} */ + +zend_ast *zend_add_attribute_value(zend_ast *list_ast, zend_ast *val_ast) /* {{{ */ +{ + zval *list, *val, arr, tmp; + + 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); + } + + return list_ast; +} +/* }}} */ + void zend_verify_namespace(void) /* {{{ */ { if (FC(has_bracketed_namespaces) && !FC(in_namespace)) { @@ -6249,6 +6294,10 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* if (decl->doc_comment) { op_array->doc_comment = zend_string_copy(decl->doc_comment); } + if (decl->attributes) { + op_array->attributes = decl->attributes; + decl->attributes = NULL; + } if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) { op_array->fn_flags |= ZEND_ACC_CLOSURE; } @@ -6348,8 +6397,10 @@ 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); @@ -6368,6 +6419,10 @@ 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, " @@ -6404,7 +6459,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) / ZVAL_UNDEF(&value_zv); } - zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type); + zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, attributes, type); } } /* }}} */ @@ -6446,16 +6501,23 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ 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; + } + zend_const_expr_to_zval(&value_zv, value_ast); - zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment); + zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment, attributes); } } /* }}} */ @@ -6661,6 +6723,10 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */ if (decl->doc_comment) { ce->info.user.doc_comment = zend_string_copy(decl->doc_comment); } + if (decl->attributes) { + ce->info.user.attributes = decl->attributes; + decl->attributes = NULL; + } if (UNEXPECTED((decl->flags & ZEND_ACC_ANON_CLASS))) { /* Serialization is not supported for anonymous classes */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 6c438b6902b59..6e0a92978685f 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -44,6 +44,10 @@ 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; @@ -120,6 +124,7 @@ typedef struct _zend_file_context { typedef union _zend_parser_stack_elem { zend_ast *ast; zend_string *str; + HashTable *hash; zend_ulong num; unsigned char *ptr; } zend_parser_stack_elem; @@ -351,6 +356,7 @@ typedef struct _zend_property_info { uint32_t flags; zend_string *name; zend_string *doc_comment; + HashTable *attributes; zend_class_entry *ce; zend_type type; } zend_property_info; @@ -367,6 +373,7 @@ typedef struct _zend_property_info { typedef struct _zend_class_constant { zval value; /* access flags are stored in reserved: zval.u2.access_flags */ zend_string *doc_comment; + HashTable *attributes; zend_class_entry *ce; } zend_class_constant; @@ -427,6 +434,7 @@ struct _zend_op_array { uint32_t line_start; uint32_t line_end; zend_string *doc_comment; + HashTable *attributes; int last_literal; zval *literals; @@ -741,6 +749,8 @@ zend_ast *zend_negate_num_string(zend_ast *ast); uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag); uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag); zend_bool zend_handle_encoding_declaration(zend_ast *ast); +void zend_add_attribute(zend_ast *name, zend_ast *value); +zend_ast *zend_add_attribute_value(zend_ast *list_ast, zend_ast *val_ast); /* parser-driven code generators */ void zend_do_free(znode *op1); diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 2dc769a5efe17..c674051246762 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -97,6 +97,7 @@ 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_inheritance.c b/Zend/zend_inheritance.c index 219c1ffcc9f06..4c3fabab4ecc3 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1959,6 +1959,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent zval* prop_value; uint32_t flags; zend_string *doc_comment; + HashTable *attributes = NULL; /* In the following steps the properties are inserted into the property table * for that, a very strict approach is applied: @@ -2058,8 +2059,12 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent Z_TRY_ADDREF_P(prop_value); doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL; + if (property_info->attributes) { + attributes = property_info->attributes; + GC_ADDREF(attributes); + } zend_type_copy_ctor(&property_info->type, /* persistent */ 0); - zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, property_info->type); + zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, attributes, property_info->type); zend_string_release_ex(prop_name, 0); } ZEND_HASH_FOREACH_END(); } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index c1ced9a35aa21..671f1332354d9 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -50,6 +50,7 @@ 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 @@ -227,16 +228,16 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); /* Token used to force a parse error from the lexer */ %token T_ERROR -%type top_statement namespace_name name statement function_declaration_statement +%type top_statement namespace_name name statement annotated_statement function_declaration_statement %type class_declaration_statement trait_declaration_statement %type interface_declaration_statement interface_extends_list %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 expr optional_expr while_statement for_statement foreach_variable +%type attribute_values 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 static_var class_statement trait_adaptation trait_precedence trait_alias +%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 %type internal_functions_in_yacc @@ -265,6 +266,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type backup_lex_pos %type backup_doc_comment +%type backup_attributes + %% /* Rules */ start: @@ -311,12 +314,37 @@ name: | T_NS_SEPARATOR namespace_name { $$ = $2; $$->attr = ZEND_NAME_FQ; } ; -top_statement: - statement { $$ = $1; } - | function_declaration_statement { $$ = $1; } +attribute_values: + expr { $$ = $1; } + | attribute_values ',' expr { $$ = zend_add_attribute_value($1, $3); } +; + +attribute_list: + namespace_name { zend_add_attribute($1, NULL); } + | namespace_name '(' attribute_values ')' { zend_add_attribute($1, $3); } + | attribute_list ',' namespace_name { zend_add_attribute($3, NULL); } + | attribute_list ',' namespace_name '(' attribute_values ')' { zend_add_attribute($3, $5); } +; + +attribute: + T_SL attribute_list T_SR +; + +attributes: + attributes attribute + | attribute +; + +annotated_statement: + function_declaration_statement { $$ = $1; } | class_declaration_statement { $$ = $1; } | trait_declaration_statement { $$ = $1; } | interface_declaration_statement { $$ = $1; } + +top_statement: + statement { $$ = $1; } + | annotated_statement { $$ = $1; } + | attributes annotated_statement { $$ = $2; } | T_HALT_COMPILER '(' ')' ';' { $$ = zend_ast_create(ZEND_AST_HALT_COMPILER, zend_ast_create_zval_from_long(zend_get_scanned_file_offset())); @@ -414,10 +442,8 @@ inner_statement_list: inner_statement: statement { $$ = $1; } - | function_declaration_statement { $$ = $1; } - | class_declaration_statement { $$ = $1; } - | trait_declaration_statement { $$ = $1; } - | interface_declaration_statement { $$ = $1; } + | annotated_statement { $$ = $1; } + | attributes annotated_statement { $$ = $2; } | T_HALT_COMPILER '(' ')' ';' { $$ = NULL; zend_throw_exception(zend_ce_compile_error, "__HALT_COMPILER() can only be used from the outermost scope", 0); YYERROR; } @@ -489,10 +515,10 @@ unset_variable: ; function_declaration_statement: - function returns_ref T_STRING backup_doc_comment '(' parameter_list ')' return_type + function returns_ref T_STRING backup_doc_comment backup_attributes '(' parameter_list ')' return_type backup_fn_flags '{' inner_statement_list '}' backup_fn_flags - { $$ = 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; } + { $$ = 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; } ; is_reference: @@ -507,11 +533,11 @@ is_variadic: class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } - 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_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_CLASS { $$ = CG(zend_lineno); } - 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); } + 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); } ; class_modifiers: @@ -527,14 +553,14 @@ class_modifier: trait_declaration_statement: T_TRAIT { $$ = CG(zend_lineno); } - 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); } + 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); } ; interface_declaration_statement: T_INTERFACE { $$ = CG(zend_lineno); } - 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); } + 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); } ; extends_from: @@ -739,7 +765,6 @@ static_var: | T_VARIABLE '=' expr { $$ = zend_ast_create(ZEND_AST_STATIC, $1, $3); } ; - class_statement_list: class_statement_list class_statement { $$ = zend_ast_list_add($1, $2); } @@ -748,18 +773,22 @@ class_statement_list: ; -class_statement: +annotated_class_statement: variable_modifiers optional_type_without_static property_list ';' { $$ = zend_ast_create(ZEND_AST_PROP_GROUP, $2, $3); $$->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 ')' + 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; } + +class_statement: + annotated_class_statement { $$ = $1; } + | attributes annotated_class_statement { $$ = $2; } | T_USE class_name_list trait_adaptations { $$ = zend_ast_create(ZEND_AST_USE_TRAIT, $2, $3); } - | 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, - zend_ast_get_str($4), $7, NULL, $11, $9); CG(extra_fn_flags) = $10; } ; class_name_list: @@ -849,10 +878,10 @@ property_list: ; property: - 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)); } + 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)); } ; class_const_list: @@ -861,11 +890,11 @@ class_const_list: ; class_const_decl: - identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); } + 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)); } ; const_decl: - T_STRING '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); } + 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)); } ; echo_expr_list: @@ -888,10 +917,10 @@ non_empty_for_exprs: anonymous_class: T_CLASS { $$ = CG(zend_lineno); } ctor_arguments - extends_from implements_list backup_doc_comment '{' class_statement_list '}' { + extends_from implements_list backup_doc_comment backup_attributes '{' class_statement_list '}' { zend_ast *decl = zend_ast_create_decl( - ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS, $2, $6, NULL, - $4, $5, $8, NULL); + ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS, $2, $6, $7, NULL, + $4, $5, $9, NULL); $$ = zend_ast_create(ZEND_AST_NEW, decl, $3); } ; @@ -1025,17 +1054,17 @@ expr: inline_function: - function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars return_type + function returns_ref backup_doc_comment backup_attributes '(' parameter_list ')' lexical_vars return_type backup_fn_flags '{' inner_statement_list '}' backup_fn_flags - { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $13, $1, $3, + { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $14, $1, $3, $4, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), - $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, + $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, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), $4, NULL, - zend_ast_create(ZEND_AST_RETURN, $11), $6); - ((zend_ast_decl *) $$)->lex_pos = $10; - CG(extra_fn_flags) = $9; } + zend_ast_create(ZEND_AST_RETURN, $12), $6); + ((zend_ast_decl *) $$)->lex_pos = $11; + CG(extra_fn_flags) = $10; } ; fn: @@ -1058,6 +1087,10 @@ 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 0c066c52ab304..465b3786d3ba0 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -190,6 +190,7 @@ 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/Zend/zend_opcode.c b/Zend/zend_opcode.c index 881626b71e487..a6c170f5e0c7c 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -63,6 +63,7 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz op_array->function_name = NULL; op_array->filename = zend_get_compiled_filename(); op_array->doc_comment = NULL; + op_array->attributes = NULL; op_array->arg_info = NULL; op_array->num_args = 0; @@ -317,6 +318,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (prop_info->doc_comment) { zend_string_release_ex(prop_info->doc_comment, 0); } + if (prop_info->attributes) { + zend_array_ptr_dtor(prop_info->attributes); + } zend_type_release(prop_info->type, /* persistent */ 0); } } ZEND_HASH_FOREACH_END(); @@ -332,6 +336,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (c->doc_comment) { zend_string_release_ex(c->doc_comment, 0); } + if (c->attributes) { + zend_array_ptr_dtor(c->attributes); + } } } ZEND_HASH_FOREACH_END(); } @@ -350,6 +357,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->info.user.doc_comment) { zend_string_release_ex(ce->info.user.doc_comment, 0); } + if (ce->info.user.attributes) { + zend_array_ptr_dtor(ce->info.user.attributes); + } if (ce->num_traits > 0) { _destroy_zend_class_traits_info(ce); @@ -401,6 +411,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (c->doc_comment) { zend_string_release_ex(c->doc_comment, 1); } + if (c->attributes) { + zend_array_ptr_dtor(c->attributes); + } } free(c); } ZEND_HASH_FOREACH_END(); @@ -482,6 +495,9 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) if (op_array->doc_comment) { zend_string_release_ex(op_array->doc_comment, 0); } + if (op_array->attributes) { + zend_array_ptr_dtor(op_array->attributes); + } if (op_array->live_range) { efree(op_array->live_range); } diff --git a/Zend/zend_types.h b/Zend/zend_types.h index d8b2280e47f8b..735c9eb43564d 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -681,6 +681,8 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define IS_RESOURCE_EX (IS_RESOURCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) #define IS_REFERENCE_EX (IS_REFERENCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) +#define IS_IMMUTABLE_ARRAY_EX (IS_ARRAY | !(IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) + #define IS_CONSTANT_AST_EX (IS_CONSTANT_AST | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) /* string flags (zval.value->gc.u.flags) */ @@ -939,6 +941,12 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { Z_TYPE_INFO_P(__z) = IS_ARRAY_EX; \ } while (0) +#define ZVAL_IMMUTABLE_ARR(z, a) do { \ + zval *__z = (z); \ + Z_ARR_P(__z) = (a); \ + Z_TYPE_INFO_P(__z) = IS_IMMUTABLE_ARRAY_EX; \ + } while (0) + #define ZVAL_NEW_ARR(z) do { \ zval *__z = (z); \ zend_array *_arr = \ diff --git a/Zend/zend_variables.h b/Zend/zend_variables.h index bdee8b1879d5b..af412f1c3e2f6 100644 --- a/Zend/zend_variables.h +++ b/Zend/zend_variables.h @@ -48,6 +48,15 @@ static zend_always_inline void i_zval_ptr_dtor(zval *zval_ptr) } } +static zend_always_inline void zend_array_ptr_dtor(zend_array *array) +{ + if (!(GC_FLAGS(array) & IS_ARRAY_IMMUTABLE)) { + if (GC_DELREF(array) == 0) { + zend_array_destroy(array); + } + } +} + static zend_always_inline void zval_copy_ctor(zval *zvalue) { if (Z_TYPE_P(zvalue) == IS_ARRAY) { diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index eb74187e5d419..f336428ab0d85 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -421,6 +421,14 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_PTR(op_array->live_range); SERIALIZE_PTR(op_array->scope); SERIALIZE_STR(op_array->doc_comment); + if (op_array->attributes && !IS_SERIALIZED(op_array->attributes)) { + HashTable *ht; + + SERIALIZE_PTR(op_array->attributes); + ht = op_array->attributes; + UNSERIALIZE_PTR(ht); + zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval); + } SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->prototype); return; @@ -591,6 +599,16 @@ static void zend_file_cache_serialize_prop_info(zval *zv, if (prop->doc_comment) { SERIALIZE_STR(prop->doc_comment); } + + if (prop->attributes) { + HashTable *ht; + + SERIALIZE_PTR(prop->attributes); + ht = prop->attributes; + UNSERIALIZE_PTR(ht); + zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval); + } + zend_file_cache_serialize_type(&prop->type, script, info, buf); } } @@ -617,6 +635,15 @@ static void zend_file_cache_serialize_class_constant(zval *z if (c->doc_comment) { SERIALIZE_STR(c->doc_comment); } + + if (c->attributes) { + HashTable *ht; + + SERIALIZE_PTR(c->attributes); + ht = c->attributes; + UNSERIALIZE_PTR(ht); + zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval); + } } } } @@ -674,6 +701,14 @@ static void zend_file_cache_serialize_class(zval *zv, zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_class_constant); SERIALIZE_STR(ce->info.user.filename); SERIALIZE_STR(ce->info.user.doc_comment); + if (ce->info.user.attributes && !IS_SERIALIZED(ce->info.user.attributes)) { + HashTable *ht; + + SERIALIZE_PTR(ce->info.user.attributes); + ht = ce->info.user.attributes; + UNSERIALIZE_PTR(ht); + zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval); + } zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info); if (ce->properties_info_table) { @@ -1254,6 +1289,14 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_PTR(op_array->live_range); UNSERIALIZE_PTR(op_array->scope); UNSERIALIZE_STR(op_array->doc_comment); + if (op_array->attributes && !IS_UNSERIALIZED(op_array->attributes)) { + HashTable *ht; + + UNSERIALIZE_PTR(op_array->attributes); + ht = op_array->attributes; + zend_file_cache_unserialize_hash(ht, + script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR); + } UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->prototype); @@ -1307,6 +1350,14 @@ static void zend_file_cache_unserialize_prop_info(zval *zv, if (prop->doc_comment) { UNSERIALIZE_STR(prop->doc_comment); } + if (prop->attributes && !IS_UNSERIALIZED(prop->attributes)) { + HashTable *ht; + + UNSERIALIZE_PTR(prop->attributes); + ht = prop->attributes; + zend_file_cache_unserialize_hash(ht, + script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR); + } zend_file_cache_unserialize_type(&prop->type, script, buf); } } @@ -1331,6 +1382,14 @@ static void zend_file_cache_unserialize_class_constant(zval * if (c->doc_comment) { UNSERIALIZE_STR(c->doc_comment); } + if (c->attributes && !IS_UNSERIALIZED(c->attributes)) { + HashTable *ht; + + UNSERIALIZE_PTR(c->attributes); + ht = c->attributes; + zend_file_cache_unserialize_hash(ht, + script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR); + } } } } @@ -1385,6 +1444,14 @@ static void zend_file_cache_unserialize_class(zval *zv, script, buf, zend_file_cache_unserialize_class_constant, NULL); UNSERIALIZE_STR(ce->info.user.filename); UNSERIALIZE_STR(ce->info.user.doc_comment); + if (ce->info.user.attributes && !IS_UNSERIALIZED(ce->info.user.attributes)) { + HashTable *ht; + + UNSERIALIZE_PTR(ce->info.user.attributes); + ht = ce->info.user.attributes; + zend_file_cache_unserialize_hash(ht, + script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR); + } zend_file_cache_unserialize_hash(&ce->properties_info, script, buf, zend_file_cache_unserialize_prop_info, NULL); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 1bcec7caf09de..4f3e84cd19f36 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -369,6 +369,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc op_array->doc_comment = NULL; } } + if (op_array->try_catch_array) { op_array->try_catch_array = zend_shared_alloc_get_xlat_entry(op_array->try_catch_array); ZEND_ASSERT(op_array->try_catch_array != NULL); @@ -549,6 +550,21 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc } } + if (op_array->attributes) { + if (already_stored) { + op_array->attributes = zend_shared_alloc_get_xlat_entry(op_array->attributes); + ZEND_ASSERT(op_array->attributes != NULL); + } else { + zend_hash_persist(op_array->attributes, zend_persist_zval); + zend_accel_store(op_array->attributes, sizeof(HashTable)); + /* make immutable array */ + GC_REFCOUNT(op_array->attributes) = 2; + GC_TYPE_INFO(op_array->attributes) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8); + op_array->attributes->u.flags |= HASH_FLAG_STATIC_KEYS; + op_array->attributes->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; + } + } + if (op_array->try_catch_array) { op_array->try_catch_array = zend_shared_memdup_put_free(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); } @@ -683,6 +699,15 @@ static void zend_persist_property_info(zval *zv) prop->doc_comment = NULL; } } + if (prop->attributes) { + zend_hash_persist(prop->attributes, zend_persist_zval); + zend_accel_store(prop->attributes, sizeof(HashTable)); + /* make immutable array */ + GC_REFCOUNT(prop->attributes) = 2; + GC_TYPE_INFO(prop->attributes) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8); + prop->attributes->u.flags |= HASH_FLAG_STATIC_KEYS; + prop->attributes->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; + } zend_persist_type(&prop->type); } @@ -722,6 +747,15 @@ static void zend_persist_class_constant(zval *zv) c->doc_comment = NULL; } } + if (c->attributes) { + zend_hash_persist(c->attributes, zend_persist_zval); + zend_accel_store(c->attributes, sizeof(HashTable)); + /* make immutable array */ + GC_REFCOUNT(c->attributes) = 2; + GC_TYPE_INFO(c->attributes) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8); + c->attributes->u.flags |= HASH_FLAG_STATIC_KEYS; + c->attributes->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; + } } static void zend_persist_class_entry(zval *zv) @@ -808,6 +842,15 @@ static void zend_persist_class_entry(zval *zv) ce->info.user.doc_comment = NULL; } } + if (ce->info.user.attributes) { + zend_hash_persist(ce->info.user.attributes, zend_persist_zval); + zend_accel_store(ce->info.user.attributes, sizeof(HashTable)); + /* make immutable array */ + GC_REFCOUNT(ce->info.user.attributes) = 2; + GC_TYPE_INFO(ce->info.user.attributes) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8); + ce->info.user.attributes->u.flags |= HASH_FLAG_STATIC_KEYS; + ce->info.user.attributes->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; + } zend_hash_persist(&ce->properties_info); ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) { ZEND_ASSERT(p->key != NULL); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 293af3b3e63f1..7b0258f6da28e 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -255,6 +255,11 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) ADD_STRING(op_array->doc_comment); } + if (op_array->attributes) { + ADD_DUP_SIZE(op_array->attributes, sizeof(HashTable)); + zend_hash_persist_calc(op_array->attributes, zend_persist_zval_calc); + } + if (op_array->try_catch_array) { ADD_SIZE(sizeof(zend_try_catch_element) * op_array->last_try_catch); } @@ -323,6 +328,10 @@ static void zend_persist_property_info_calc(zval *zv) if (ZCG(accel_directives).save_comments && prop->doc_comment) { ADD_STRING(prop->doc_comment); } + if (prop->attributes) { + ADD_DUP_SIZE(prop->attributes, sizeof(HashTable)); + zend_hash_persist_calc(prop->attributes, zend_persist_zval_calc); + } } } @@ -337,6 +346,10 @@ static void zend_persist_class_constant_calc(zval *zv) if (ZCG(accel_directives).save_comments && c->doc_comment) { ADD_STRING(c->doc_comment); } + if (c->attributes) { + ADD_DUP_SIZE(c->attributes, sizeof(HashTable)); + zend_hash_persist_calc(c->attributes, zend_persist_zval_calc); + } } } @@ -422,6 +435,10 @@ static void zend_persist_class_entry_calc(zval *zv) if (ZCG(accel_directives).save_comments && ce->info.user.doc_comment) { ADD_STRING(ce->info.user.doc_comment); } + if (ce->info.user.attributes) { + ADD_DUP_SIZE(ce->info.user.attributes, sizeof(HashTable)); + zend_hash_persist_calc(ce->info.user.attributes, zend_persist_zval_calc); + } zend_hash_persist_calc(&ce->properties_info); ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) { diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 101913c4940d7..7ab70545d6be9 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1630,6 +1630,25 @@ ZEND_METHOD(reflection_function, getDocComment) } /* }}} */ +/* {{{ proto public array ReflectionFunction::getAttributes() + Returns the attributes of this function */ +ZEND_METHOD(reflection_function, getAttributes) +{ + reflection_object *intern; + zend_function *fptr; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(fptr); + if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.attributes) { + zend_ast_convert_attributes(return_value, fptr->op_array.attributes); + } else { + array_init(return_value); + } +} +/* }}} */ + /* {{{ proto public array ReflectionFunction::getStaticVariables() Returns an associative array containing this function's static variables and their values */ ZEND_METHOD(reflection_function, getStaticVariables) @@ -3614,6 +3633,25 @@ ZEND_METHOD(reflection_class_constant, getDocComment) } /* }}} */ +/* {{{ proto public array ReflectionClassConstant::getAttributes() + Returns the attributes of this constant */ +ZEND_METHOD(reflection_class_constant, getAttributes) +{ + reflection_object *intern; + zend_class_constant *ref; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ref); + if (ref->attributes) { + zend_ast_convert_attributes(return_value, ref->attributes); + } else { + array_init(return_value); + } +} +/* }}} */ + /* {{{ reflection_class_object_ctor */ static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_object) { @@ -3976,6 +4014,25 @@ ZEND_METHOD(reflection_class, getDocComment) } /* }}} */ +/* {{{ proto public array ReflectionClass::getAttributes() + Returns the attributes for this class */ +ZEND_METHOD(reflection_class, getAttributes) +{ + reflection_object *intern; + zend_class_entry *ce; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ce); + if (ce->type == ZEND_USER_CLASS && ce->info.user.attributes) { + zend_ast_convert_attributes(return_value, ce->info.user.attributes); + } else { + array_init(return_value); + } +} +/* }}} */ + /* {{{ proto public ReflectionMethod ReflectionClass::getConstructor() Returns the class' constructor if there is one, NULL otherwise */ ZEND_METHOD(reflection_class, getConstructor) @@ -5475,6 +5532,25 @@ ZEND_METHOD(reflection_property, getDocComment) } /* }}} */ +/* {{{ proto public array ReflectionProperty::getAttributes() + Returns the attributes of this property */ +ZEND_METHOD(reflection_property, getAttributes) +{ + reflection_object *intern; + property_reference *ref; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(ref); + if (ref->prop->attributes) { + zend_ast_convert_attributes(return_value, ref->prop->attributes); + } else { + array_init(return_value); + } +} +/* }}} */ + /* {{{ proto public int ReflectionProperty::setAccessible(bool visible) Sets whether non-public properties can be requested */ ZEND_METHOD(reflection_property, setAccessible) @@ -6226,6 +6302,7 @@ static const zend_function_entry reflection_function_abstract_functions[] = { ZEND_ME(reflection_function, getClosureThis, arginfo_class_ReflectionFunctionAbstract_getClosureThis, 0) ZEND_ME(reflection_function, getClosureScopeClass, arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass, 0) ZEND_ME(reflection_function, getDocComment, arginfo_class_ReflectionFunctionAbstract_getDocComment, 0) + ZEND_ME(reflection_function, getAttributes, arginfo_class_ReflectionFunctionAbstract_getDocComment, 0) ZEND_ME(reflection_function, getEndLine, arginfo_class_ReflectionFunctionAbstract_getEndLine, 0) ZEND_ME(reflection_function, getExtension, arginfo_class_ReflectionFunctionAbstract_getExtension, 0) ZEND_ME(reflection_function, getExtensionName, arginfo_class_ReflectionFunctionAbstract_getExtensionName, 0) @@ -6300,6 +6377,7 @@ static const zend_function_entry reflection_class_functions[] = { ZEND_ME(reflection_class, getStartLine, arginfo_class_ReflectionClass_getStartLine, 0) ZEND_ME(reflection_class, getEndLine, arginfo_class_ReflectionClass_getEndLine, 0) ZEND_ME(reflection_class, getDocComment, arginfo_class_ReflectionClass_getDocComment, 0) + ZEND_ME(reflection_class, getAttributes, arginfo_class_ReflectionClass_getDocComment, 0) ZEND_ME(reflection_class, getConstructor, arginfo_class_ReflectionClass_getConstructor, 0) ZEND_ME(reflection_class, hasMethod, arginfo_class_ReflectionClass_hasMethod, 0) ZEND_ME(reflection_class, getMethod, arginfo_class_ReflectionClass_getMethod, 0) @@ -6364,6 +6442,7 @@ static const zend_function_entry reflection_property_functions[] = { ZEND_ME(reflection_property, getModifiers, arginfo_class_ReflectionProperty_getModifiers, 0) ZEND_ME(reflection_property, getDeclaringClass, arginfo_class_ReflectionProperty_getDeclaringClass, 0) ZEND_ME(reflection_property, getDocComment, arginfo_class_ReflectionProperty_getDocComment, 0) + ZEND_ME(reflection_property, getAttributes, arginfo_class_ReflectionProperty_getDocComment, 0) ZEND_ME(reflection_property, setAccessible, arginfo_class_ReflectionProperty_setAccessible, 0) ZEND_ME(reflection_property, getType, arginfo_class_ReflectionProperty_getType, 0) ZEND_ME(reflection_property, hasType, arginfo_class_ReflectionProperty_hasType, 0) @@ -6384,6 +6463,7 @@ static const zend_function_entry reflection_class_constant_functions[] = { ZEND_ME(reflection_class_constant, getModifiers, arginfo_class_ReflectionClassConstant_getModifiers, 0) ZEND_ME(reflection_class_constant, getDeclaringClass, arginfo_class_ReflectionClassConstant_getDeclaringClass, 0) ZEND_ME(reflection_class_constant, getDocComment, arginfo_class_ReflectionClassConstant_getDocComment, 0) + ZEND_ME(reflection_class_constant, getAttributes, arginfo_class_ReflectionClassConstant_getDocComment, 0) PHP_FE_END }; diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index 0aa46652b7b70..b5deca0a957a8 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -107,6 +107,12 @@ Class [ class ReflectionClass implements Reflector ] { } } + Method [ public method getAttributes ] { + + - Parameters [0] { + } + } + Method [ public method getConstructor ] { - Parameters [0] { From ed7630853bfc4dbfa3f3c20c88c7b9225443bdbb Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 21 Feb 2020 00:43:56 +0100 Subject: [PATCH 02/37] Tinkering around to evaluate constant AST when accessing attributes. --- Zend/tests/attributes_002.phpt | 11 +++- Zend/tests/attributes_003.phpt | 19 ++++++ Zend/zend_ast.c | 24 ++++++- Zend/zend_compile.c | 111 +++++++++++++++++++------------- Zend/zend_language_parser.y | 8 +-- ext/reflection/php_reflection.c | 8 +-- 6 files changed, 126 insertions(+), 55 deletions(-) create mode 100644 Zend/tests/attributes_003.phpt diff --git a/Zend/tests/attributes_002.phpt b/Zend/tests/attributes_002.phpt index 83d64bf0b70f1..ad6adc0a2c4d0 100644 --- a/Zend/tests/attributes_002.phpt +++ b/Zend/tests/attributes_002.phpt @@ -16,14 +16,21 @@ namespace Doctrine\ORM { $attrs = $reflClass->getAttributes(); foreach ($attrs as $name => &$values) { $name = "Doctrine\\" . $name; - $values = new $name(...$values); + $values = new $name($values); } return $attrs; } } +namespace Doctrine\ORM\Mapping { + class Entity { + } +} + namespace { - <> + use Doctrine\ORM\Mapping as ORM; + + < "user", "repository" => UserRepository::class])>> class User {} var_dump(Doctrine\ORM\GetClassAttributes("User")); diff --git a/Zend/tests/attributes_003.phpt b/Zend/tests/attributes_003.phpt new file mode 100644 index 0000000000000..459089c6c1c7f --- /dev/null +++ b/Zend/tests/attributes_003.phpt @@ -0,0 +1,19 @@ +--TEST-- +Resolve attribute names +--FILE-- +> + function foo() { + } + var_dump((new ReflectionFunction('foo'))->getAttributes()); +} +--EXPECTF-- diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 49ca40c41ed18..c06e9dbf57a87 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2119,7 +2119,27 @@ ZEND_API ZEND_COLD zend_string *zend_ast_export(const char *prefix, zend_ast *as ZEND_API zend_class_entry *zend_ast_node_ce = NULL; ZEND_API zend_class_entry *zend_ast_decl_ce = NULL; -ZEND_API void zend_ast_convert_attributes(zval *ret, HashTable *attributes) +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); + } +} + +ZEND_API void zend_ast_convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) { zval *val, tmp; HashTable *ht, *ht2, *res_ht; @@ -2149,6 +2169,7 @@ ZEND_API void zend_ast_convert_attributes(zval *ret, HashTable *attributes) res_ht = Z_ARR_P(ret); ZEND_HASH_FOREACH_STR_KEY_VAL(attributes, key, val) { if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + zend_ast_convert_to_object(&tmp, Z_ASTVAL_P(val), ce); zend_hash_add_new(res_ht, key, &tmp); } else if (Z_TYPE_P(val) == IS_ARRAY) { ht = Z_ARR_P(val); @@ -2157,6 +2178,7 @@ ZEND_API void zend_ast_convert_attributes(zval *ret, HashTable *attributes) ht2 = Z_ARR_P(val); ZEND_HASH_FOREACH_VAL(ht, 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_new(ht2, &tmp); } else { if (Z_REFCOUNTED_P(val)) { diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7e8d0bfdb4962..11ff03511b6e8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1917,50 +1917,6 @@ zend_ast *zend_negate_num_string(zend_ast *ast) /* {{{ */ } /* }}} */ -void zend_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ -{ - zval *val, tmp; - zend_string *key = zend_ast_get_str(name); - -#if 1 /* SPECIAL_ATTRIBUTE */ - if (ZSTR_LEN(key) >= 2 && ZSTR_VAL(key)[0] == '_' && ZSTR_VAL(key)[1] == '_') { - zend_error_noreturn(E_COMPILE_ERROR, "Unknown special attribute %s", ZSTR_VAL(key)); - } -#endif - 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)); - } - array_init(val); - zend_string_release(key); -} -/* }}} */ - -zend_ast *zend_add_attribute_value(zend_ast *list_ast, zend_ast *val_ast) /* {{{ */ -{ - zval *list, *val, arr, tmp; - - 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); - } - - return list_ast; -} -/* }}} */ - void zend_verify_namespace(void) /* {{{ */ { if (FC(has_bracketed_namespaces) && !FC(in_namespace)) { @@ -9326,3 +9282,70 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ *ast_ptr = zend_ast_create_zval(&result); } /* }}} */ + +void zend_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ +{ + zval *val, tmp; + //zend_string *key = zend_ast_get_str(name); + + znode class_node; + + zend_compile_class_ref(&class_node, name, ZEND_FETCH_CLASS_EXCEPTION); + zend_string *key = Z_STR(class_node.u.constant); + + 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); + + if (Z_TYPE_P(zv) == IS_ARRAY) { + ZVAL_COPY_VALUE(val, zv); + } else { + array_init(val); + zend_hash_next_index_insert_new(Z_ARRVAL_P(val), zv); + } + } else { + ZVAL_AST(&tmp, zend_ast_copy(value)); + zend_ast_destroy(value); + array_init(val); + zend_hash_next_index_insert_new(Z_ARRVAL_P(val), &tmp); + } + } else { + array_init(val); + } + + zend_string_release(key); +} +/* }}} */ + +zend_ast *zend_add_attribute_value(zend_ast *list_ast, zend_ast *val_ast) /* {{{ */ +{ + zval *list, *val, arr, tmp; + + 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); + } + + return list_ast; +} +/* }}} */ + diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 671f1332354d9..931a2a491a2fb 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -320,10 +320,10 @@ attribute_values: ; attribute_list: - namespace_name { zend_add_attribute($1, NULL); } - | namespace_name '(' attribute_values ')' { zend_add_attribute($1, $3); } - | attribute_list ',' namespace_name { zend_add_attribute($3, NULL); } - | attribute_list ',' namespace_name '(' attribute_values ')' { zend_add_attribute($3, $5); } + class_name_reference { zend_add_attribute($1, NULL); } + | class_name_reference '(' attribute_values ')' { zend_add_attribute($1, $3); } + | attribute_list ',' class_name_reference { zend_add_attribute($3, NULL); } + | attribute_list ',' class_name_reference '(' attribute_values ')' { zend_add_attribute($3, $5); } ; attribute: diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 7ab70545d6be9..001129b7390c9 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1642,7 +1642,7 @@ ZEND_METHOD(reflection_function, getAttributes) } GET_REFLECTION_OBJECT_PTR(fptr); if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.attributes) { - zend_ast_convert_attributes(return_value, fptr->op_array.attributes); + zend_ast_convert_attributes(return_value, fptr->op_array.attributes, NULL); } else { array_init(return_value); } @@ -3645,7 +3645,7 @@ ZEND_METHOD(reflection_class_constant, getAttributes) } GET_REFLECTION_OBJECT_PTR(ref); if (ref->attributes) { - zend_ast_convert_attributes(return_value, ref->attributes); + zend_ast_convert_attributes(return_value, ref->attributes, NULL); } else { array_init(return_value); } @@ -4026,7 +4026,7 @@ ZEND_METHOD(reflection_class, getAttributes) } GET_REFLECTION_OBJECT_PTR(ce); if (ce->type == ZEND_USER_CLASS && ce->info.user.attributes) { - zend_ast_convert_attributes(return_value, ce->info.user.attributes); + zend_ast_convert_attributes(return_value, ce->info.user.attributes, NULL); } else { array_init(return_value); } @@ -5544,7 +5544,7 @@ ZEND_METHOD(reflection_property, getAttributes) } GET_REFLECTION_OBJECT_PTR(ref); if (ref->prop->attributes) { - zend_ast_convert_attributes(return_value, ref->prop->attributes); + zend_ast_convert_attributes(return_value, ref->prop->attributes, NULL); } else { array_init(return_value); } From b7378799360d22d8d01d26c4ff4979a5c7db8ff6 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 21 Feb 2020 22:44:41 +0100 Subject: [PATCH 03/37] Adjust tests, fix node conversion, still leaking and not resolving names. --- Zend/tests/attributes_001.phpt | 101 ++++----------------------------- Zend/tests/attributes_002.phpt | 4 +- Zend/tests/attributes_003.phpt | 19 ++++++- Zend/zend_ast.c | 1 + Zend/zend_compile.c | 9 +-- 5 files changed, 32 insertions(+), 102 deletions(-) diff --git a/Zend/tests/attributes_001.phpt b/Zend/tests/attributes_001.phpt index e3d0236b6368c..54e3c285ce9a3 100644 --- a/Zend/tests/attributes_001.phpt +++ b/Zend/tests/attributes_001.phpt @@ -22,7 +22,7 @@ class Bar { const C = 2; <> public $x = 3; - + } $r = new ReflectionClass("Bar"); var_dump($r->getAttributes()); @@ -38,7 +38,7 @@ $r = new ReflectionFunction("f1"); var_dump($r->getAttributes()); // Attributes with AST -<1,"b"=>2])>> +<1,"b"=>2])>> function f2() {} $r = new ReflectionFunction("f2"); var_dump($r->getAttributes()); @@ -97,102 +97,21 @@ array(4) { ["a2"]=> array(1) { [0]=> - object(ast\Node)#4 (4) { - ["kind"]=> - int(520) - ["flags"]=> - int(1) - ["lineno"]=> - NULL - ["children"]=> - array(2) { - [0]=> - int(1) - [1]=> - string(1) "a" - } - } + int(2) } ["a3"]=> - array(2) { + array(1) { [0]=> - object(ast\Node)#5 (4) { - ["kind"]=> - int(520) - ["flags"]=> - int(1) - ["lineno"]=> - NULL - ["children"]=> - array(2) { - [0]=> - int(1) - [1]=> - string(1) "b" - } - } - [1]=> - object(ast\Node)#6 (4) { - ["kind"]=> - int(520) - ["flags"]=> - int(1) - ["lineno"]=> - NULL - ["children"]=> - array(2) { - [0]=> - int(2) - [1]=> - string(1) "c" - } - } + int(4) } ["a4"]=> array(1) { [0]=> - object(ast\Node)#7 (4) { - ["kind"]=> - int(130) - ["flags"]=> - int(0) - ["lineno"]=> - NULL - ["children"]=> - array(2) { - [0]=> - object(ast\Node)#8 (4) { - ["kind"]=> - int(525) - ["flags"]=> - int(0) - ["lineno"]=> - NULL - ["children"]=> - array(2) { - [0]=> - int(1) - [1]=> - string(1) "a" - } - } - [1]=> - object(ast\Node)#9 (4) { - ["kind"]=> - int(525) - ["flags"]=> - int(0) - ["lineno"]=> - NULL - ["children"]=> - array(2) { - [0]=> - int(2) - [1]=> - string(1) "b" - } - } - } + array(2) { + ["a"]=> + int(1) + ["b"]=> + int(2) } } } diff --git a/Zend/tests/attributes_002.phpt b/Zend/tests/attributes_002.phpt index ad6adc0a2c4d0..e0002d2b9736a 100644 --- a/Zend/tests/attributes_002.phpt +++ b/Zend/tests/attributes_002.phpt @@ -14,9 +14,9 @@ namespace Doctrine\ORM { function GetClassAttributes($class_name) { $reflClass = new \ReflectionClass($class_name); $attrs = $reflClass->getAttributes(); - foreach ($attrs as $name => &$values) { + foreach ($attrs as $name => $values) { $name = "Doctrine\\" . $name; - $values = new $name($values); + $attrs[$name] = new $name($values); } return $attrs; } diff --git a/Zend/tests/attributes_003.phpt b/Zend/tests/attributes_003.phpt index 459089c6c1c7f..bdb8e464bee71 100644 --- a/Zend/tests/attributes_003.phpt +++ b/Zend/tests/attributes_003.phpt @@ -8,12 +8,25 @@ namespace Doctrine\ORM\Mapping { } } -namespace { +namespace Foo { use Doctrine\ORM\Mapping\Entity; - <> + < "bar"])>> function foo() { } - var_dump((new ReflectionFunction('foo'))->getAttributes()); +} + +namespace { + var_dump((new ReflectionFunction('Foo\foo'))->getAttributes()); } --EXPECTF-- +array(1) { + ["Doctrine\ORM\Mapping\Entity"]=> + array(1) { + [0]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + } +} diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index c06e9dbf57a87..3927c8ff70112 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2135,6 +2135,7 @@ ZEND_API void zend_ast_convert_to_object(zval *p, zend_ast *ast, zend_class_entr if (UNEXPECTED(zend_ast_evaluate(&tmp, ast, ce) != SUCCESS)) { return; } + ZVAL_COPY_VALUE(p, &tmp); } } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 11ff03511b6e8..aa39475a42612 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9286,12 +9286,12 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ void zend_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ { zval *val, tmp; - //zend_string *key = zend_ast_get_str(name); + zend_string *key; znode class_node; zend_compile_class_ref(&class_node, name, ZEND_FETCH_CLASS_EXCEPTION); - zend_string *key = Z_STR(class_node.u.constant); + key = Z_STR(class_node.u.constant); if (!CG(attributes)) { ALLOC_HASHTABLE(CG(attributes)); @@ -9316,16 +9316,13 @@ void zend_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ zend_hash_next_index_insert_new(Z_ARRVAL_P(val), zv); } } else { - ZVAL_AST(&tmp, zend_ast_copy(value)); - zend_ast_destroy(value); + zend_const_expr_to_zval(&tmp, value); array_init(val); zend_hash_next_index_insert_new(Z_ARRVAL_P(val), &tmp); } } else { array_init(val); } - - zend_string_release(key); } /* }}} */ From 44340c4eac5cc51f575514bc411aa7674614b653 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 3 Mar 2020 00:40:53 +0100 Subject: [PATCH 04/37] Move AST attribute functions to zend_ast.c --- Zend/zend_ast.c | 62 +++++++++++++++++++++++++++++++++++++ Zend/zend_ast.h | 3 ++ Zend/zend_compile.c | 62 ------------------------------------- Zend/zend_compile.h | 2 -- Zend/zend_language_parser.y | 10 +++--- 5 files changed, 70 insertions(+), 69 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 3927c8ff70112..d402dd333f503 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2203,3 +2203,65 @@ ZEND_API void zend_ast_convert_attributes(zval *ret, HashTable *attributes, zend } } +void zend_ast_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ +{ + zval *val, tmp; + zend_string *key; + + znode class_node; + + 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); + + if (Z_TYPE_P(zv) == IS_ARRAY) { + ZVAL_COPY_VALUE(val, zv); + } else { + array_init(val); + zend_hash_next_index_insert_new(Z_ARRVAL_P(val), zv); + } + } else { + zend_const_expr_to_zval(&tmp, value); + array_init(val); + zend_hash_next_index_insert_new(Z_ARRVAL_P(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, tmp; + + 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); + } + + return list_ast; +} +/* }}} */ diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 3f5e80ea70e5a..1434420009730 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -353,4 +353,7 @@ 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); #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index aa39475a42612..1c1d1aa7860a0 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9283,66 +9283,4 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ } /* }}} */ -void zend_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ -{ - zval *val, tmp; - zend_string *key; - - znode class_node; - - zend_compile_class_ref(&class_node, name, ZEND_FETCH_CLASS_EXCEPTION); - key = Z_STR(class_node.u.constant); - - 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); - - if (Z_TYPE_P(zv) == IS_ARRAY) { - ZVAL_COPY_VALUE(val, zv); - } else { - array_init(val); - zend_hash_next_index_insert_new(Z_ARRVAL_P(val), zv); - } - } else { - zend_const_expr_to_zval(&tmp, value); - array_init(val); - zend_hash_next_index_insert_new(Z_ARRVAL_P(val), &tmp); - } - } else { - array_init(val); - } -} -/* }}} */ - -zend_ast *zend_add_attribute_value(zend_ast *list_ast, zend_ast *val_ast) /* {{{ */ -{ - zval *list, *val, arr, tmp; - - 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); - } - - return list_ast; -} -/* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 6e0a92978685f..f234c76e33abe 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -749,8 +749,6 @@ zend_ast *zend_negate_num_string(zend_ast *ast); uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag); uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag); zend_bool zend_handle_encoding_declaration(zend_ast *ast); -void zend_add_attribute(zend_ast *name, zend_ast *value); -zend_ast *zend_add_attribute_value(zend_ast *list_ast, zend_ast *val_ast); /* parser-driven code generators */ void zend_do_free(znode *op1); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 931a2a491a2fb..318aa2fe53458 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -316,14 +316,14 @@ name: attribute_values: expr { $$ = $1; } - | attribute_values ',' expr { $$ = zend_add_attribute_value($1, $3); } + | attribute_values ',' expr { $$ = zend_ast_add_attribute_value($1, $3); } ; attribute_list: - class_name_reference { zend_add_attribute($1, NULL); } - | class_name_reference '(' attribute_values ')' { zend_add_attribute($1, $3); } - | attribute_list ',' class_name_reference { zend_add_attribute($3, NULL); } - | attribute_list ',' class_name_reference '(' attribute_values ')' { zend_add_attribute($3, $5); } + class_name_reference { zend_ast_add_attribute($1, NULL); } + | class_name_reference '(' attribute_values ')' { zend_ast_add_attribute($1, $3); } + | attribute_list ',' class_name_reference { zend_ast_add_attribute($3, NULL); } + | attribute_list ',' class_name_reference '(' attribute_values ')' { zend_ast_add_attribute($3, $5); } ; attribute: From 32df5b7311e6384e925deeba579fbef94be0ac92 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 3 Mar 2020 01:24:03 +0100 Subject: [PATCH 05/37] Resolve attribute names according to imports (still requiring attributes to be unique) --- Zend/tests/attributes_002.phpt | 12 ++++++++-- Zend/tests/attributes_003.phpt | 7 ++++-- Zend/zend_compile.c | 40 ++++++++++++++++++++++++++++++++-- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/Zend/tests/attributes_002.phpt b/Zend/tests/attributes_002.phpt index e0002d2b9736a..f035c374def79 100644 --- a/Zend/tests/attributes_002.phpt +++ b/Zend/tests/attributes_002.phpt @@ -15,8 +15,7 @@ namespace Doctrine\ORM { $reflClass = new \ReflectionClass($class_name); $attrs = $reflClass->getAttributes(); foreach ($attrs as $name => $values) { - $name = "Doctrine\\" . $name; - $attrs[$name] = new $name($values); + $attrs[$name] = new $name($values[0][0]); } return $attrs; } @@ -24,6 +23,15 @@ namespace Doctrine\ORM { namespace Doctrine\ORM\Mapping { class Entity { + public $tableName; + public $repository; + + public function __construct(array $values) + { + foreach ($values as $k => $v) { + $this->$k = $v; + } + } } } diff --git a/Zend/tests/attributes_003.phpt b/Zend/tests/attributes_003.phpt index bdb8e464bee71..fe42f7a01ad98 100644 --- a/Zend/tests/attributes_003.phpt +++ b/Zend/tests/attributes_003.phpt @@ -25,8 +25,11 @@ array(1) { array(1) { [0]=> array(1) { - ["foo"]=> - string(3) "bar" + [0]=> + array(1) { + ["foo"]=> + string(3) "bar" + } } } } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 1c1d1aa7860a0..941f63fb78144 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6216,6 +6216,42 @@ 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], [0 => arg12, 1 => arg22] ] + 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 { + val = zend_hash_find(resolved_attributes, 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; @@ -6251,7 +6287,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 = decl->attributes; + op_array->attributes = zend_compile_resolve_attributes(decl->attributes); decl->attributes = NULL; } if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) { @@ -6680,7 +6716,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 = decl->attributes; + ce->info.user.attributes = zend_compile_resolve_attributes(decl->attributes); decl->attributes = NULL; } From 9a2b5067060fe0778b5ac5f8db0944597d136fab Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 3 Mar 2020 04:30:27 +0100 Subject: [PATCH 06/37] Basics of ReflectionAttribute support. --- ext/reflection/php_reflection.c | 46 ++++++++++++++++++++++--- ext/reflection/php_reflection.h | 1 + ext/reflection/php_reflection.stub.php | 23 +++++++++++++ ext/reflection/php_reflection_arginfo.h | 21 +++++++++++ 4 files changed, 87 insertions(+), 4 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 001129b7390c9..de231e2250a6c 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -84,6 +84,7 @@ PHPAPI zend_class_entry *reflection_class_constant_ptr; PHPAPI zend_class_entry *reflection_extension_ptr; PHPAPI zend_class_entry *reflection_zend_extension_ptr; PHPAPI zend_class_entry *reflection_reference_ptr; +PHPAPI zend_class_entry *reflection_attribute_ptr; /* Exception throwing macro */ #define _DO_THROW(msg) \ @@ -6275,6 +6276,27 @@ ZEND_METHOD(reflection_reference, getId) } /* }}} */ +/* {{{ proto public string ReflectionAttribute::getName() + * Returns the name of the attribute */ +ZEND_METHOD(reflection_attribute, getName) +{ +} +/* }}} */ + +/* {{{ proto public string ReflectionAttribute::getArguments() + * Returns the arguments passed to the attribute */ +ZEND_METHOD(reflection_attribute, getArguments) +{ +} +/* }}} */ + +/* {{{ proto public string ReflectionAttribute::getAsObject() + * Returns the attribute as an object */ +ZEND_METHOD(reflection_attribute, getAsObject) +{ +} +/* }}} */ + /* {{{ method tables */ static const zend_function_entry reflection_exception_functions[] = { PHP_FE_END @@ -6302,7 +6324,7 @@ static const zend_function_entry reflection_function_abstract_functions[] = { ZEND_ME(reflection_function, getClosureThis, arginfo_class_ReflectionFunctionAbstract_getClosureThis, 0) ZEND_ME(reflection_function, getClosureScopeClass, arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass, 0) ZEND_ME(reflection_function, getDocComment, arginfo_class_ReflectionFunctionAbstract_getDocComment, 0) - ZEND_ME(reflection_function, getAttributes, arginfo_class_ReflectionFunctionAbstract_getDocComment, 0) + ZEND_ME(reflection_function, getAttributes, arginfo_class_ReflectionFunctionAbstract_getAttributes, 0) ZEND_ME(reflection_function, getEndLine, arginfo_class_ReflectionFunctionAbstract_getEndLine, 0) ZEND_ME(reflection_function, getExtension, arginfo_class_ReflectionFunctionAbstract_getExtension, 0) ZEND_ME(reflection_function, getExtensionName, arginfo_class_ReflectionFunctionAbstract_getExtensionName, 0) @@ -6377,7 +6399,7 @@ static const zend_function_entry reflection_class_functions[] = { ZEND_ME(reflection_class, getStartLine, arginfo_class_ReflectionClass_getStartLine, 0) ZEND_ME(reflection_class, getEndLine, arginfo_class_ReflectionClass_getEndLine, 0) ZEND_ME(reflection_class, getDocComment, arginfo_class_ReflectionClass_getDocComment, 0) - ZEND_ME(reflection_class, getAttributes, arginfo_class_ReflectionClass_getDocComment, 0) + ZEND_ME(reflection_class, getAttributes, arginfo_class_ReflectionClass_getAttributes, 0) ZEND_ME(reflection_class, getConstructor, arginfo_class_ReflectionClass_getConstructor, 0) ZEND_ME(reflection_class, hasMethod, arginfo_class_ReflectionClass_hasMethod, 0) ZEND_ME(reflection_class, getMethod, arginfo_class_ReflectionClass_getMethod, 0) @@ -6442,7 +6464,7 @@ static const zend_function_entry reflection_property_functions[] = { ZEND_ME(reflection_property, getModifiers, arginfo_class_ReflectionProperty_getModifiers, 0) ZEND_ME(reflection_property, getDeclaringClass, arginfo_class_ReflectionProperty_getDeclaringClass, 0) ZEND_ME(reflection_property, getDocComment, arginfo_class_ReflectionProperty_getDocComment, 0) - ZEND_ME(reflection_property, getAttributes, arginfo_class_ReflectionProperty_getDocComment, 0) + ZEND_ME(reflection_property, getAttributes, arginfo_class_ReflectionProperty_getAttributes, 0) ZEND_ME(reflection_property, setAccessible, arginfo_class_ReflectionProperty_setAccessible, 0) ZEND_ME(reflection_property, getType, arginfo_class_ReflectionProperty_getType, 0) ZEND_ME(reflection_property, hasType, arginfo_class_ReflectionProperty_hasType, 0) @@ -6463,7 +6485,7 @@ static const zend_function_entry reflection_class_constant_functions[] = { ZEND_ME(reflection_class_constant, getModifiers, arginfo_class_ReflectionClassConstant_getModifiers, 0) ZEND_ME(reflection_class_constant, getDeclaringClass, arginfo_class_ReflectionClassConstant_getDeclaringClass, 0) ZEND_ME(reflection_class_constant, getDocComment, arginfo_class_ReflectionClassConstant_getDocComment, 0) - ZEND_ME(reflection_class_constant, getAttributes, arginfo_class_ReflectionClassConstant_getDocComment, 0) + ZEND_ME(reflection_class_constant, getAttributes, arginfo_class_ReflectionClassConstant_getAttributes, 0) PHP_FE_END }; @@ -6549,6 +6571,17 @@ static const zend_function_entry reflection_reference_functions[] = { ZEND_ME(reflection_reference, __construct, arginfo_class_ReflectionReference___construct, ZEND_ACC_PRIVATE) PHP_FE_END }; + +static const zend_function_entry reflection_attribute_functions[] = { + ZEND_ME(reflection_attribute, getName, arginfo_class_ReflectionAttribute_getName, ZEND_ACC_PUBLIC) + ZEND_ME(reflection_attribute, getArguments, arginfo_class_ReflectionAttribute_getArguments, ZEND_ACC_PUBLIC) + ZEND_ME(reflection_attribute, getAsObject, arginfo_class_ReflectionAttribute_getAsObject, ZEND_ACC_PUBLIC) + + /* Always throwing dummy methods */ + ZEND_ME(reflection, __clone, arginfo_class_ReflectionReference___clone, ZEND_ACC_PRIVATE) + ZEND_ME(reflection_reference, __construct, arginfo_class_ReflectionReference___construct, ZEND_ACC_PRIVATE) + PHP_FE_END +}; /* }}} */ static const zend_function_entry reflection_ext_functions[] = { /* {{{ */ @@ -6699,6 +6732,11 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ _reflection_entry.ce_flags |= ZEND_ACC_FINAL; reflection_reference_ptr = zend_register_internal_class(&_reflection_entry); + INIT_CLASS_ENTRY(_reflection_entry, "ReflectionAttribute", reflection_attribute_functions); + reflection_init_class_handlers(&_reflection_entry); + _reflection_entry.ce_flags |= ZEND_ACC_FINAL; + reflection_attribute_ptr = zend_register_internal_class(&_reflection_entry); + REFLECTION_G(key_initialized) = 0; return SUCCESS; diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h index e4e08c16e60bc..654ba55256c8f 100644 --- a/ext/reflection/php_reflection.h +++ b/ext/reflection/php_reflection.h @@ -42,6 +42,7 @@ extern PHPAPI zend_class_entry *reflection_property_ptr; extern PHPAPI zend_class_entry *reflection_extension_ptr; extern PHPAPI zend_class_entry *reflection_zend_extension_ptr; extern PHPAPI zend_class_entry *reflection_reference_ptr; +extern PHPAPI zend_class_entry *reflection_attribute_ptr; PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object); diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index dc6b7014737e1..c9b666e90c050 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -94,6 +94,9 @@ public function hasReturnType() {} /** @return ?ReflectionType */ public function getReturnType() {} + + /** @return ReflectionAttribute[] */ + public function getAttributes($name = null) {} } class ReflectionFunction extends ReflectionFunctionAbstract @@ -353,6 +356,9 @@ public function getNamespaceName() {} /** @return string|false */ public function getShortName() {} + + /** @return ReflectionAttribute[] */ + public function getAttributes($name = null) {} } class ReflectionObject extends ReflectionClass @@ -418,6 +424,9 @@ public function hasDefaultValue(): bool {} /** @return mixed */ public function getDefaultValue() {} + + /** @return ReflectionAttribute[] */ + public function getAttributes($name = null) {} } class ReflectionClassConstant implements Reflector @@ -452,6 +461,9 @@ public function getDeclaringClass() {} /** @return string|false */ public function getDocComment() {} + + /** @return ReflectionAttribute[] */ + public function getAttributes($name = null) {} } class ReflectionParameter implements Reflector @@ -625,3 +637,14 @@ private function __clone() {} private function __construct() {} } + +final class ReflectionAttribute +{ + public function getName(): string {} + public function getArguments(): array {} + public function getAsObject(): object {} + + private function __clone() {} + + private function __construct() {} +} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 5f5079f59edf2..f04cd0a1fa70a 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -59,6 +59,10 @@ 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_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunction___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) ZEND_END_ARG_INFO() @@ -268,6 +272,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClass_getShortName arginfo_class_Reflector___toString +#define arginfo_class_ReflectionClass_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionObject___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, argument, IS_OBJECT, 0) ZEND_END_ARG_INFO() @@ -321,6 +327,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_getDefaultValue arginfo_class_Reflector___toString +#define arginfo_class_ReflectionProperty_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes + #define arginfo_class_ReflectionClassConstant___clone arginfo_class_Reflector___toString #define arginfo_class_ReflectionClassConstant___construct arginfo_class_ReflectionProperty___construct @@ -343,6 +351,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassConstant_getDocComment arginfo_class_Reflector___toString +#define arginfo_class_ReflectionClassConstant_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes + #define arginfo_class_ReflectionParameter___clone arginfo_class_Reflector___toString ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionParameter___construct, 0, 0, 2) @@ -456,3 +466,14 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionReference___clone arginfo_class_Reflector___toString #define arginfo_class_ReflectionReference___construct arginfo_class_Reflector___toString + +#define arginfo_class_ReflectionAttribute_getName arginfo_class_ReflectionReference_getId + +#define arginfo_class_ReflectionAttribute_getArguments arginfo_class_ReflectionUnionType_getTypes + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionAttribute_getAsObject, 0, 0, IS_OBJECT, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionAttribute___clone arginfo_class_Reflector___toString + +#define arginfo_class_ReflectionAttribute___construct arginfo_class_Reflector___toString From 6d49c93b8fe7b574dede34e30ee71e3dea018ea8 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 3 Mar 2020 05:23:44 +0100 Subject: [PATCH 07/37] Prepare for returning ReflectionAttribute not array. --- ext/reflection/php_reflection.c | 36 +++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index de231e2250a6c..ac51b5de57e69 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -133,6 +133,12 @@ typedef struct _type_reference { zend_bool legacy_behavior; } type_reference; +/* Struct for attributes */ +typedef struct _attribute_reference { + zend_string *name; + zval *arguments; +} attribute_reference; + typedef enum { REF_TYPE_OTHER, /* Must be 0 */ REF_TYPE_FUNCTION, @@ -140,7 +146,8 @@ typedef enum { REF_TYPE_PARAMETER, REF_TYPE_TYPE, REF_TYPE_PROPERTY, - REF_TYPE_CLASS_CONSTANT + REF_TYPE_CLASS_CONSTANT, + REF_TYPE_ATTRIBUTE } reflection_type_t; /* Struct for reflection objects */ @@ -221,6 +228,7 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ reflection_object *intern = reflection_object_from_obj(object); parameter_reference *reference; property_reference *prop_reference; + attribute_reference *attr_reference; if (intern->ptr) { switch (intern->ref_type) { @@ -246,6 +254,10 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ zend_string_release_ex(prop_reference->unmangled_name, 0); efree(intern->ptr); break; + case REF_TYPE_ATTRIBUTE: + attr_reference = (attribute_reference*)intern->ptr; + efree(attr_reference); + break; case REF_TYPE_GENERATOR: case REF_TYPE_CLASS_CONSTANT: case REF_TYPE_OTHER: @@ -1179,6 +1191,22 @@ static void reflection_type_factory(zend_type type, zval *object, zend_bool lega } /* }}} */ +/* {{{ reflection_attribute_factory */ +static void reflection_attribute_factory(zval *object, zend_string *name, zval *arguments) +{ + reflection_object *intern; + attribute_reference *reference; + + reflection_instantiate(reflection_attribute_ptr, object); + intern = Z_REFLECTION_P(object); + reference = (attribute_reference*) emalloc(sizeof(attribute_reference)); + reference->name = name; + reference->arguments = arguments; + intern->ptr = reference; + intern->ref_type = REF_TYPE_ATTRIBUTE; +} +/* }}} */ + /* {{{ reflection_function_factory */ static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object) { @@ -3646,7 +3674,7 @@ ZEND_METHOD(reflection_class_constant, getAttributes) } GET_REFLECTION_OBJECT_PTR(ref); if (ref->attributes) { - zend_ast_convert_attributes(return_value, ref->attributes, NULL); + zend_ast_convert_attributes(return_value, ref->attributes, ref->ce); } else { array_init(return_value); } @@ -4027,7 +4055,7 @@ ZEND_METHOD(reflection_class, getAttributes) } GET_REFLECTION_OBJECT_PTR(ce); if (ce->type == ZEND_USER_CLASS && ce->info.user.attributes) { - zend_ast_convert_attributes(return_value, ce->info.user.attributes, NULL); + zend_ast_convert_attributes(return_value, ce->info.user.attributes, ce); } else { array_init(return_value); } @@ -5545,7 +5573,7 @@ ZEND_METHOD(reflection_property, getAttributes) } GET_REFLECTION_OBJECT_PTR(ref); if (ref->prop->attributes) { - zend_ast_convert_attributes(return_value, ref->prop->attributes, NULL); + zend_ast_convert_attributes(return_value, ref->prop->attributes, ref->prop->ce); } else { array_init(return_value); } From 2875a23dcad2e160a95492c918f822e3feff4129 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 3 Mar 2020 06:55:06 +0100 Subject: [PATCH 08/37] Convert getAttributes() to return list of ReflectionAttribute and adjust tests. --- Zend/tests/attributes_001.phpt | 26 +++++++--- Zend/tests/attributes_002.phpt | 16 +++--- Zend/tests/attributes_003.phpt | 12 ++++- Zend/zend_ast.c | 37 ++++++-------- Zend/zend_ast.h | 1 + ext/reflection/php_reflection.c | 89 +++++++++++++++++++++++++-------- 6 files changed, 125 insertions(+), 56 deletions(-) diff --git a/Zend/tests/attributes_001.phpt b/Zend/tests/attributes_001.phpt index 54e3c285ce9a3..b520f2ad764bb 100644 --- a/Zend/tests/attributes_001.phpt +++ b/Zend/tests/attributes_001.phpt @@ -2,18 +2,28 @@ Basic attributes usage --FILE-- getName()])) { + $arr[$attribute->getName()] = []; + } + $arr[$attribute->getName()][] = $attribute->getArguments(); + } + var_dump($arr); +} // No attributes function f0() { } $r = new ReflectionFunction("f0"); -var_dump($r->getAttributes()); +dump_attributes($r->getAttributes()); // Function attributes <> function foo() { } $r = new ReflectionFunction("foo"); -var_dump($r->getAttributes()); +dump_attributes($r->getAttributes()); // Class attributes <> @@ -25,30 +35,30 @@ class Bar { } $r = new ReflectionClass("Bar"); -var_dump($r->getAttributes()); +dump_attributes($r->getAttributes()); $r1 = $r->getReflectionConstant("C"); -var_dump($r1->getAttributes()); +dump_attributes($r1->getAttributes()); $r2 = $r->getProperty("x"); -var_dump($r2->getAttributes()); +dump_attributes($r2->getAttributes()); // Multiple attributes with multiple values <> function f1() {} $r = new ReflectionFunction("f1"); -var_dump($r->getAttributes()); +dump_attributes($r->getAttributes()); // Attributes with AST <1,"b"=>2])>> function f2() {} $r = new ReflectionFunction("f2"); -var_dump($r->getAttributes()); +dump_attributes($r->getAttributes()); // Attributes with namespaces <> function f4() { } $r = new ReflectionFunction("f4"); -var_dump($r->getAttributes()); +dump_attributes($r->getAttributes()); ?> --EXPECT-- array(0) { diff --git a/Zend/tests/attributes_002.phpt b/Zend/tests/attributes_002.phpt index f035c374def79..e5bbd1de3dfab 100644 --- a/Zend/tests/attributes_002.phpt +++ b/Zend/tests/attributes_002.phpt @@ -14,10 +14,12 @@ namespace Doctrine\ORM { function GetClassAttributes($class_name) { $reflClass = new \ReflectionClass($class_name); $attrs = $reflClass->getAttributes(); - foreach ($attrs as $name => $values) { - $attrs[$name] = new $name($values[0][0]); + $values = []; + foreach ($attrs as $attribute) { + $class = $attribute->getName(); + $values[$attribute->getName()] = new $class(...$attribute->getArguments()); } - return $attrs; + return $values; } } @@ -46,9 +48,11 @@ namespace { ?> --EXPECT-- array(1) { - ["ORM\Entity"]=> - object(Doctrine\ORM\Entity)#2 (1) { - ["name":"Doctrine\ORM\Entity":private]=> + ["Doctrine\ORM\Mapping\Entity"]=> + object(Doctrine\ORM\Mapping\Entity)#3 (2) { + ["tableName"]=> string(4) "user" + ["repository"]=> + string(14) "UserRepository" } } diff --git a/Zend/tests/attributes_003.phpt b/Zend/tests/attributes_003.phpt index fe42f7a01ad98..8b16722d66a28 100644 --- a/Zend/tests/attributes_003.phpt +++ b/Zend/tests/attributes_003.phpt @@ -2,6 +2,16 @@ Resolve attribute names --FILE-- getName()])) { + $arr[$attribute->getName()] = []; + } + $arr[$attribute->getName()][] = $attribute->getArguments(); + } + var_dump($arr); +} namespace Doctrine\ORM\Mapping { class Entity { @@ -17,7 +27,7 @@ namespace Foo { } namespace { - var_dump((new ReflectionFunction('Foo\foo'))->getAttributes()); + dump_attributes((new ReflectionFunction('Foo\foo'))->getAttributes()); } --EXPECTF-- array(1) { diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index d402dd333f503..51e0d392bd4dc 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2140,34 +2140,28 @@ ZEND_API void zend_ast_convert_to_object(zval *p, zend_ast *ast, zend_class_entr } } -ZEND_API void zend_ast_convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) +ZEND_API zval *zend_ast_convert_attributes(HashTable *attributes, zend_class_entry *ce) { zval *val, tmp; - HashTable *ht, *ht2, *res_ht; - zend_string *key; - int convert_ast = 0; + HashTable *ht, *ht2; + zval *res = emalloc(sizeof(zval)); + + array_init(res); ZEND_HASH_FOREACH_VAL(attributes, val) { if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - convert_ast = 1; + 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_TYPE_P(val) == IS_ARRAY) { - ht = Z_ARR_P(val); - ZEND_HASH_FOREACH_VAL(ht, val) { - if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - convert_ast = 1; - break; - } - } ZEND_HASH_FOREACH_END(); - if (convert_ast) { - 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(); - if (convert_ast) { - array_init_size(ret, zend_hash_num_elements(attributes)); - res_ht = Z_ARR_P(ret); +/* if (convert_ast) { ZEND_HASH_FOREACH_STR_KEY_VAL(attributes, key, val) { if (Z_TYPE_P(val) == IS_CONSTANT_AST) { zend_ast_convert_to_object(&tmp, Z_ASTVAL_P(val), ce); @@ -2201,6 +2195,9 @@ ZEND_API void zend_ast_convert_attributes(zval *ret, HashTable *attributes, zend GC_ADDREF(attributes); ZVAL_ARR(ret, attributes); } + */ + + return res; } void zend_ast_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ @@ -2208,8 +2205,6 @@ void zend_ast_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ zval *val, tmp; zend_string *key; - znode class_node; - zval *zv = zend_ast_get_zval(name); key = Z_STR_P(zv); @@ -2248,7 +2243,7 @@ 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 *list, *val, arr, tmp; + zval *list, *val, arr; if (list_ast->kind == ZEND_AST_ZVAL) { list = zend_ast_get_zval(list_ast); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 1434420009730..4c1f7b800f271 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -356,4 +356,5 @@ static zend_always_inline zend_ast *zend_ast_list_rtrim(zend_ast *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); +ZEND_API zval *zend_ast_convert_attributes(HashTable *attributes, zend_class_entry *ce); #endif diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ac51b5de57e69..07dfd32946f85 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -31,6 +31,7 @@ #include "zend.h" #include "zend_API.h" +#include "zend_ast.h" #include "zend_exceptions.h" #include "zend_operators.h" #include "zend_constants.h" @@ -256,6 +257,9 @@ 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); + } efree(attr_reference); break; case REF_TYPE_GENERATOR: @@ -1036,6 +1040,45 @@ static void _extension_string(smart_str *str, zend_module_entry *module, char *i } /* }}} */ +/* {{{ reflection_attribute_factory */ +static void reflection_attribute_factory(zval *object, zend_string *name, zval *arguments) +{ + reflection_object *intern; + attribute_reference *reference; + + reflection_instantiate(reflection_attribute_ptr, object); + intern = Z_REFLECTION_P(object); + reference = (attribute_reference*) emalloc(sizeof(attribute_reference)); + reference->name = name; + reference->arguments = arguments; + intern->ptr = reference; + intern->ref_type = REF_TYPE_ATTRIBUTE; +} +/* }}} */ + + +static zval* convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) +{ + zend_string *attribute_name; + zval *attrs, *attr, *object; + zval *converted_attributes; + + array_init(ret); + + ZEND_HASH_FOREACH_STR_KEY_VAL(attributes, attribute_name, attrs) { + if (attribute_name) { + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(attrs), attr) { + 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_END(); + } + } ZEND_HASH_FOREACH_END(); +} + static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent) /* {{{ */ { smart_str_append_printf(str, "%sZend Extension [ %s ", indent, extension->name); @@ -1191,22 +1234,6 @@ static void reflection_type_factory(zend_type type, zval *object, zend_bool lega } /* }}} */ -/* {{{ reflection_attribute_factory */ -static void reflection_attribute_factory(zval *object, zend_string *name, zval *arguments) -{ - reflection_object *intern; - attribute_reference *reference; - - reflection_instantiate(reflection_attribute_ptr, object); - intern = Z_REFLECTION_P(object); - reference = (attribute_reference*) emalloc(sizeof(attribute_reference)); - reference->name = name; - reference->arguments = arguments; - intern->ptr = reference; - intern->ref_type = REF_TYPE_ATTRIBUTE; -} -/* }}} */ - /* {{{ reflection_function_factory */ static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object) { @@ -1671,7 +1698,7 @@ ZEND_METHOD(reflection_function, getAttributes) } GET_REFLECTION_OBJECT_PTR(fptr); if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.attributes) { - zend_ast_convert_attributes(return_value, fptr->op_array.attributes, NULL); + convert_attributes(return_value, fptr->op_array.attributes, NULL); } else { array_init(return_value); } @@ -3674,7 +3701,7 @@ ZEND_METHOD(reflection_class_constant, getAttributes) } GET_REFLECTION_OBJECT_PTR(ref); if (ref->attributes) { - zend_ast_convert_attributes(return_value, ref->attributes, ref->ce); + convert_attributes(return_value, ref->attributes, ref->ce); } else { array_init(return_value); } @@ -4055,7 +4082,7 @@ ZEND_METHOD(reflection_class, getAttributes) } GET_REFLECTION_OBJECT_PTR(ce); if (ce->type == ZEND_USER_CLASS && ce->info.user.attributes) { - zend_ast_convert_attributes(return_value, ce->info.user.attributes, ce); + convert_attributes(return_value, ce->info.user.attributes, ce); } else { array_init(return_value); } @@ -5573,7 +5600,7 @@ ZEND_METHOD(reflection_property, getAttributes) } GET_REFLECTION_OBJECT_PTR(ref); if (ref->prop->attributes) { - zend_ast_convert_attributes(return_value, ref->prop->attributes, ref->prop->ce); + convert_attributes(return_value, ref->prop->attributes, ref->prop->ce); } else { array_init(return_value); } @@ -6308,6 +6335,15 @@ ZEND_METHOD(reflection_reference, getId) * Returns the name of the attribute */ ZEND_METHOD(reflection_attribute, getName) { + reflection_object *intern; + attribute_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_STR(param->name); } /* }}} */ @@ -6315,6 +6351,19 @@ ZEND_METHOD(reflection_attribute, getName) * Returns the arguments passed to the attribute */ ZEND_METHOD(reflection_attribute, getArguments) { + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + reflection_object *intern; + attribute_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT_PTR(param); + + ZVAL_COPY(return_value, param->arguments); } /* }}} */ From 781ca23685052d70ec372c377fde5c235b413929 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 3 Mar 2020 06:57:15 +0100 Subject: [PATCH 09/37] Some cleanup --- Zend/zend_ast.c | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 51e0d392bd4dc..45b58a0f26e87 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2116,9 +2116,6 @@ ZEND_API ZEND_COLD zend_string *zend_ast_export(const char *prefix, zend_ast *as return str.s; } -ZEND_API zend_class_entry *zend_ast_node_ce = NULL; -ZEND_API zend_class_entry *zend_ast_decl_ce = NULL; - ZEND_API void zend_ast_convert_to_object(zval *p, zend_ast *ast, zend_class_entry *ce) { if (ast->kind == ZEND_AST_CONSTANT) { @@ -2161,42 +2158,6 @@ ZEND_API zval *zend_ast_convert_attributes(HashTable *attributes, zend_class_ent } } ZEND_HASH_FOREACH_END(); -/* if (convert_ast) { - ZEND_HASH_FOREACH_STR_KEY_VAL(attributes, key, val) { - if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - zend_ast_convert_to_object(&tmp, Z_ASTVAL_P(val), ce); - zend_hash_add_new(res_ht, key, &tmp); - } else if (Z_TYPE_P(val) == IS_ARRAY) { - ht = Z_ARR_P(val); - array_init_size(&tmp, zend_hash_num_elements(ht)); - val = zend_hash_add_new(res_ht, key, &tmp); - ht2 = Z_ARR_P(val); - ZEND_HASH_FOREACH_VAL(ht, 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_new(ht2, &tmp); - } else { - if (Z_REFCOUNTED_P(val)) { - Z_ADDREF_P(val); - } - zend_hash_next_index_insert_new(ht2, val); - } - } ZEND_HASH_FOREACH_END(); - } else { - if (Z_REFCOUNTED_P(val)) { - Z_ADDREF_P(val); - } - zend_hash_add_new(res_ht, key, val); - } - } ZEND_HASH_FOREACH_END(); - } else if (GC_FLAGS(attributes) & IS_ARRAY_IMMUTABLE) { - ZVAL_IMMUTABLE_ARR(ret, attributes); - } else { - GC_ADDREF(attributes); - ZVAL_ARR(ret, attributes); - } - */ - return res; } From cd4d759b5f1b7f5b58eaf10b8b002fba519e0a7b Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 4 Mar 2020 22:40:56 +0100 Subject: [PATCH 10/37] Reduce variability in attribute declarations --- Zend/tests/attributes_001.phpt | 9 +++++++-- Zend/zend_language_parser.y | 6 ++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Zend/tests/attributes_001.phpt b/Zend/tests/attributes_001.phpt index b520f2ad764bb..97a383b59c525 100644 --- a/Zend/tests/attributes_001.phpt +++ b/Zend/tests/attributes_001.phpt @@ -42,13 +42,18 @@ $r2 = $r->getProperty("x"); dump_attributes($r2->getAttributes()); // Multiple attributes with multiple values -<> +<> +<> +<> function f1() {} $r = new ReflectionFunction("f1"); dump_attributes($r->getAttributes()); // Attributes with AST -<1,"b"=>2])>> +<> +<> +<> +<1,"b"=>2])>> function f2() {} $r = new ReflectionFunction("f2"); dump_attributes($r->getAttributes()); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 318aa2fe53458..94f954a9fc57e 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -319,15 +319,13 @@ attribute_values: | attribute_values ',' expr { $$ = zend_ast_add_attribute_value($1, $3); } ; -attribute_list: +attribute_decl: class_name_reference { zend_ast_add_attribute($1, NULL); } | class_name_reference '(' attribute_values ')' { zend_ast_add_attribute($1, $3); } - | attribute_list ',' class_name_reference { zend_ast_add_attribute($3, NULL); } - | attribute_list ',' class_name_reference '(' attribute_values ')' { zend_ast_add_attribute($3, $5); } ; attribute: - T_SL attribute_list T_SR + T_SL attribute_decl T_SR ; attributes: From 756a083cc1918e686d14754058f28acda0914896 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 4 Mar 2020 23:09:47 +0100 Subject: [PATCH 11/37] Go back to unique attribute names --- Zend/tests/attributes_003.phpt | 12 +++--------- Zend/zend_ast.c | 13 +++---------- Zend/zend_ast.h | 2 +- Zend/zend_language_parser.y | 8 ++++---- ext/reflection/php_reflection.c | 17 ++++++----------- 5 files changed, 17 insertions(+), 35 deletions(-) diff --git a/Zend/tests/attributes_003.phpt b/Zend/tests/attributes_003.phpt index 8b16722d66a28..a921d07c7eef4 100644 --- a/Zend/tests/attributes_003.phpt +++ b/Zend/tests/attributes_003.phpt @@ -5,10 +5,7 @@ Resolve attribute names function dump_attributes($attributes) { $arr = []; foreach ($attributes as $attribute) { - if (!isset($arr[$attribute->getName()])) { - $arr[$attribute->getName()] = []; - } - $arr[$attribute->getName()][] = $attribute->getArguments(); + $arr[$attribute->getName()] = $attribute->getArguments(); } var_dump($arr); } @@ -35,11 +32,8 @@ array(1) { array(1) { [0]=> array(1) { - [0]=> - array(1) { - ["foo"]=> - string(3) "bar" - } + ["foo"]=> + string(3) "bar" } } } diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 45b58a0f26e87..72a5bddeb38d3 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2137,10 +2137,9 @@ ZEND_API void zend_ast_convert_to_object(zval *p, zend_ast *ast, zend_class_entr } } -ZEND_API zval *zend_ast_convert_attributes(HashTable *attributes, zend_class_entry *ce) +zval *zend_ast_convert_attributes(HashTable *attributes, zend_class_entry *ce) { zval *val, tmp; - HashTable *ht, *ht2; zval *res = emalloc(sizeof(zval)); array_init(res); @@ -2185,16 +2184,10 @@ void zend_ast_add_attribute(zend_ast *name, zend_ast *value) /* {{{ */ if (value->kind == ZEND_AST_ZVAL) { zval *zv = zend_ast_get_zval(value); - if (Z_TYPE_P(zv) == IS_ARRAY) { - ZVAL_COPY_VALUE(val, zv); - } else { - array_init(val); - zend_hash_next_index_insert_new(Z_ARRVAL_P(val), zv); - } + ZVAL_COPY_VALUE(val, zv); } else { zend_const_expr_to_zval(&tmp, value); - array_init(val); - zend_hash_next_index_insert_new(Z_ARRVAL_P(val), &tmp); + ZVAL_COPY_VALUE(val, &tmp); } } else { array_init(val); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 4c1f7b800f271..ee7965e220e05 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -356,5 +356,5 @@ static zend_always_inline zend_ast *zend_ast_list_rtrim(zend_ast *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); -ZEND_API zval *zend_ast_convert_attributes(HashTable *attributes, zend_class_entry *ce); +zval *zend_ast_convert_attributes(HashTable *attributes, zend_class_entry *ce); #endif diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 94f954a9fc57e..9bb3e3f14fdce 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -234,7 +234,7 @@ 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_values expr optional_expr while_statement for_statement foreach_variable +%type attribute_arguments 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 static_var class_statement annotated_class_statement trait_adaptation trait_precedence trait_alias @@ -314,14 +314,14 @@ name: | T_NS_SEPARATOR namespace_name { $$ = $2; $$->attr = ZEND_NAME_FQ; } ; -attribute_values: +attribute_arguments: expr { $$ = $1; } - | attribute_values ',' expr { $$ = zend_ast_add_attribute_value($1, $3); } + | attribute_arguments ',' expr { $$ = zend_ast_add_attribute_value($1, $3); } ; attribute_decl: class_name_reference { zend_ast_add_attribute($1, NULL); } - | class_name_reference '(' attribute_values ')' { zend_ast_add_attribute($1, $3); } + | class_name_reference '(' attribute_arguments ')' { zend_ast_add_attribute($1, $3); } ; attribute: diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 07dfd32946f85..86dc6d5761eed 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1057,24 +1057,19 @@ static void reflection_attribute_factory(zval *object, zend_string *name, zval * /* }}} */ -static zval* convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) +static void convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) { zend_string *attribute_name; - zval *attrs, *attr, *object; + zval *attr, *object; zval *converted_attributes; array_init(ret); - ZEND_HASH_FOREACH_STR_KEY_VAL(attributes, attribute_name, attrs) { + ZEND_HASH_FOREACH_STR_KEY_VAL(attributes, attribute_name, attr) { if (attribute_name) { - - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(attrs), attr) { - 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_END(); + 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_END(); } From 03e2ecead35ecdfeeb8da9d1c486caef0990a6ba Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 4 Mar 2020 23:20:11 +0100 Subject: [PATCH 12/37] Test redeclaration of attributes throws error. --- Zend/tests/attributes_004.phpt | 10 ++++++++++ Zend/tests/attributes_005.phpt | 12 ++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 Zend/tests/attributes_004.phpt create mode 100644 Zend/tests/attributes_005.phpt diff --git a/Zend/tests/attributes_004.phpt b/Zend/tests/attributes_004.phpt new file mode 100644 index 0000000000000..ea3605fc3cdba --- /dev/null +++ b/Zend/tests/attributes_004.phpt @@ -0,0 +1,10 @@ +--TEST-- +Redclare attribute errors +--FILE-- +> +<> +function foo() {} +--EXPECTF-- +Fatal error: Redeclared attribute A in %s on line %d diff --git a/Zend/tests/attributes_005.phpt b/Zend/tests/attributes_005.phpt new file mode 100644 index 0000000000000..a294f8523f435 --- /dev/null +++ b/Zend/tests/attributes_005.phpt @@ -0,0 +1,12 @@ +--TEST-- +Redclare resolved attribute errors +--FILE-- +> +<<\Test\Attr>> +function foo() {} +--EXPECTF-- +Fatal error: Redeclared attribute Test\Attr in %s on line %d From 356538fbe579a303465e862b5e56e15640e334aa Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 4 Mar 2020 23:21:09 +0100 Subject: [PATCH 13/37] Test redeclaration of attributes throws error. --- Zend/zend_compile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 941f63fb78144..c63b7d2edda28 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6222,7 +6222,7 @@ static HashTable* zend_compile_resolve_attributes(HashTable *declared_attributes 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], [0 => arg12, 1 => arg22] ] + // resolved attributes are in structure fqcn => [0 => arg1, 1 => arg2, ..] HashTable *resolved_attributes; ALLOC_HASHTABLE(resolved_attributes); @@ -6239,7 +6239,7 @@ static HashTable* zend_compile_resolve_attributes(HashTable *declared_attributes if (val) { array_init(val); } else { - val = zend_hash_find(resolved_attributes, resolved_name); + zend_error_noreturn(E_COMPILE_ERROR, "Redeclared attribute %s", ZSTR_VAL(resolved_name)); } zend_hash_next_index_insert(Z_ARRVAL_P(val), attr); From 048bfa4840cd24c198bda4022ff09b3a1ec7062d Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 5 Mar 2020 00:07:13 +0100 Subject: [PATCH 14/37] Cleanup --- Zend/zend_API.h | 2 -- Zend/zend_types.h | 8 -------- 2 files changed, 10 deletions(-) diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 18db4e9251a51..f7f6690f4c834 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -650,7 +650,6 @@ END_EXTERN_C() #define RETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value) #define RETVAL_RES(r) ZVAL_RES(return_value, r) #define RETVAL_ARR(r) ZVAL_ARR(return_value, r) -#define RETVAL_IMMUTABLE_ARR(r) ZVAL_IMMUTABLE_ARR(return_value, r) #define RETVAL_EMPTY_ARRAY() ZVAL_EMPTY_ARRAY(return_value) #define RETVAL_OBJ(r) ZVAL_OBJ(return_value, r) #define RETVAL_COPY(zv) ZVAL_COPY(return_value, zv) @@ -672,7 +671,6 @@ END_EXTERN_C() #define RETURN_EMPTY_STRING() do { RETVAL_EMPTY_STRING(); return; } while (0) #define RETURN_RES(r) do { RETVAL_RES(r); return; } while (0) #define RETURN_ARR(r) do { RETVAL_ARR(r); return; } while (0) -#define RETURN_IMMUTABLE_ARR(r) do { RETVAL_IMMUTABLE_ARR(r); return; } while (0) #define RETURN_EMPTY_ARRAY() do { RETVAL_EMPTY_ARRAY(); return; } while (0) #define RETURN_OBJ(r) do { RETVAL_OBJ(r); return; } while (0) #define RETURN_COPY(zv) do { RETVAL_COPY(zv); return; } while (0) diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 735c9eb43564d..d8b2280e47f8b 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -681,8 +681,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define IS_RESOURCE_EX (IS_RESOURCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) #define IS_REFERENCE_EX (IS_REFERENCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) -#define IS_IMMUTABLE_ARRAY_EX (IS_ARRAY | !(IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) - #define IS_CONSTANT_AST_EX (IS_CONSTANT_AST | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) /* string flags (zval.value->gc.u.flags) */ @@ -941,12 +939,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { Z_TYPE_INFO_P(__z) = IS_ARRAY_EX; \ } while (0) -#define ZVAL_IMMUTABLE_ARR(z, a) do { \ - zval *__z = (z); \ - Z_ARR_P(__z) = (a); \ - Z_TYPE_INFO_P(__z) = IS_IMMUTABLE_ARRAY_EX; \ - } while (0) - #define ZVAL_NEW_ARR(z) do { \ zval *__z = (z); \ zend_array *_arr = \ From dcae6bda6d48af9f34a515f1da25360eb58aa770 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 5 Mar 2020 00:09:29 +0100 Subject: [PATCH 15/37] Cleanup --- Zend/zend_compile.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c63b7d2edda28..75d7e7d14c3cf 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9318,5 +9318,3 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ *ast_ptr = zend_ast_create_zval(&result); } /* }}} */ - - From e367e00846ad49d03ab10fd9b3ce621d13f3c020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sat, 4 Apr 2020 21:35:15 +0200 Subject: [PATCH 16/37] Reworked grammar and compiler. Fixed all memory leaks. --- Zend/zend_ast.c | 125 +++++------- Zend/zend_ast.h | 21 +- Zend/zend_compile.c | 183 ++++++++++++------ Zend/zend_compile.h | 4 - Zend/zend_globals.h | 1 - Zend/zend_language_parser.y | 118 +++++------ Zend/zend_language_scanner.l | 1 - ext/reflection/php_reflection.c | 90 ++++++--- .../tests/ReflectionClass_toString_001.phpt | 5 +- .../ReflectionExtension_getClasses_basic.phpt | 7 +- 10 files changed, 328 insertions(+), 227 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 72a5bddeb38d3..fdc6fc7346287 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,77 @@ 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) +zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) +{ + ZEND_ASSERT(attr->kind == ZEND_AST_ATTRIBUTE_LIST); + + 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_ast_destroy(attr); + } + + return ast; +} + +static int zend_ast_convert_to_object(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; + return FAILURE; } - ZVAL_COPY_OR_DUP(p, zv); + ZVAL_COPY_OR_DUP(ret, zv); } else { zval tmp; if (UNEXPECTED(zend_ast_evaluate(&tmp, ast, ce) != SUCCESS)) { - return; + return FAILURE; } - ZVAL_COPY_VALUE(p, &tmp); + ZVAL_COPY_VALUE(ret, &tmp); } + + return SUCCESS; } -zval *zend_ast_convert_attributes(HashTable *attributes, zend_class_entry *ce) +int zend_ast_convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) { zval *val, tmp; - zval *res = emalloc(sizeof(zval)); - array_init(res); + array_init(ret); 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); + if (FAILURE == zend_ast_convert_to_object(&tmp, Z_ASTVAL_P(val), ce)) { + return FAILURE; } - zend_hash_next_index_insert(Z_ARRVAL_P(res), val); - } - } ZEND_HASH_FOREACH_END(); - return res; -} - -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); + add_next_index_zval(ret, &tmp); } else { - zend_const_expr_to_zval(&tmp, value); - ZVAL_COPY_VALUE(val, &tmp); + Z_TRY_ADDREF_P(val); + add_next_index_zval(ret, val); } - } 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); - } + } ZEND_HASH_FOREACH_END(); - return list_ast; + return SUCCESS; } -/* }}} */ diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index ee7965e220e05..4e6240c478b31 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_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_PROP_GROUP, + 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,7 @@ 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); +int zend_ast_convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce); + #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c63b7d2edda28..c5da40daf35d0 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5715,6 +5715,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 +5830,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 +5900,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 +6314,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 +6349,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 +6431,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 +6450,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 +6470,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 +6506,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 +6517,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[3] ? zend_compile_attributes(list->child[3]) : NULL; + + zend_compile_prop_decl(prop_ast, type_ast, list->attr, attributes); + + if (attributes) { + zend_array_ptr_dtor(attributes); + } } /* }}} */ @@ -6477,10 +6544,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 +6556,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 +6787,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 +8892,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..0d0f77e073e46 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,26 @@ 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 '(' 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 +344,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 +443,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 +515,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 +533,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 +553,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 +665,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 +780,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, 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 +883,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 +895,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 +922,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 +935,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 +1056,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 +1097,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..5ee4e64b35f57 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,7 @@ 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); - } + zval_ptr_dtor(&attr_reference->arguments); efree(attr_reference); break; case REF_TYPE_GENERATOR: @@ -1050,28 +1048,40 @@ static void reflection_attribute_factory(zval *object, zend_string *name, zval * intern = Z_REFLECTION_P(object); reference = (attribute_reference*) emalloc(sizeof(attribute_reference)); reference->name = name; - reference->arguments = arguments; + ZVAL_COPY(&reference->arguments, arguments); intern->ptr = reference; intern->ref_type = REF_TYPE_ATTRIBUTE; } /* }}} */ -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; + zval *attr; + + zval converted_attributes; + 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); + if (!attribute_name) { + continue; + } + + if (FAILURE == zend_ast_convert_attributes(&converted_attributes, Z_ARRVAL_P(attr), ce)) { + zval_ptr_dtor(ret); + return FAILURE; } + + reflection_attribute_factory(&obj, attribute_name, &converted_attributes); + zval_ptr_dtor(&converted_attributes); + + add_next_index_zval(ret, &obj); } ZEND_HASH_FOREACH_END(); + + return SUCCESS; } static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent) /* {{{ */ @@ -1691,12 +1701,20 @@ ZEND_METHOD(reflection_function, getAttributes) if (zend_parse_parameters_none() == FAILURE) { 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(); } /* }}} */ @@ -3695,11 +3713,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 +4101,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(); } /* }}} */ @@ -5595,10 +5627,16 @@ ZEND_METHOD(reflection_property, getAttributes) } 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 +6376,7 @@ ZEND_METHOD(reflection_attribute, getName) } GET_REFLECTION_OBJECT_PTR(param); - RETVAL_STR(param->name); + RETVAL_STR_COPY(param->name); } /* }}} */ @@ -6358,7 +6396,7 @@ ZEND_METHOD(reflection_attribute, getArguments) } GET_REFLECTION_OBJECT_PTR(param); - ZVAL_COPY(return_value, param->arguments); + RETVAL_ZVAL(¶m->arguments, 1, 0); } /* }}} */ 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" + } } From 13db3565f59911c3bf7d3e46b60cff81c6af7319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sat, 4 Apr 2020 22:43:59 +0200 Subject: [PATCH 17/37] Implemented access to multi & param attributes. --- Zend/zend_ast.c | 46 -------- Zend/zend_ast.h | 1 - Zend/zend_compile.c | 4 + ext/reflection/php_reflection.c | 135 +++++++++++++++++++++--- ext/reflection/php_reflection.stub.php | 11 +- ext/reflection/php_reflection_arginfo.h | 4 +- 6 files changed, 135 insertions(+), 66 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index fdc6fc7346287..810b0afd981bd 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2147,49 +2147,3 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) return ast; } - -static int zend_ast_convert_to_object(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_VALUE(ret, &tmp); - } - - return SUCCESS; -} - -int zend_ast_convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) -{ - zval *val, tmp; - - array_init(ret); - - ZEND_HASH_FOREACH_VAL(attributes, val) { - if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - if (FAILURE == zend_ast_convert_to_object(&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, val); - } - } ZEND_HASH_FOREACH_END(); - - return SUCCESS; -} diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 4e6240c478b31..c8286f08cad69 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -358,6 +358,5 @@ static zend_always_inline zend_ast *zend_ast_list_rtrim(zend_ast *ast) { } zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr); -int zend_ast_convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce); #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c5da40daf35d0..d275a33358c50 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); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 5ee4e64b35f57..6b504cb633b40 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -257,6 +257,7 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ break; case REF_TYPE_ATTRIBUTE: attr_reference = (attribute_reference*)intern->ptr; + zend_string_release(attr_reference->name); zval_ptr_dtor(&attr_reference->arguments); efree(attr_reference); break; @@ -1047,38 +1048,112 @@ 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->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 int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) { - zend_string *attribute_name; - zval *attr; + Bucket *p; + zval *v; - zval converted_attributes; + zval result; zval obj; array_init(ret); - ZEND_HASH_FOREACH_STR_KEY_VAL(attributes, attribute_name, attr) { - if (!attribute_name) { + ZEND_HASH_FOREACH_BUCKET(attributes, p) { + if (!p->key) { + // Skip inlined parameter annotations. continue; } - if (FAILURE == zend_ast_convert_attributes(&converted_attributes, Z_ARRVAL_P(attr), ce)) { - zval_ptr_dtor(ret); - return FAILURE; - } + ZEND_ASSERT(Z_TYPE(p->val) == IS_ARRAY); - reflection_attribute_factory(&obj, attribute_name, &converted_attributes); - zval_ptr_dtor(&converted_attributes); + v = zend_hash_index_find(Z_ARRVAL(p->val), 0); - add_next_index_zval(ret, &obj); + 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; @@ -1701,7 +1776,6 @@ ZEND_METHOD(reflection_function, getAttributes) if (zend_parse_parameters_none() == FAILURE) { return; } - GET_REFLECTION_OBJECT_PTR(fptr); if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.attributes) { @@ -2640,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) @@ -5626,6 +5731,7 @@ ZEND_METHOD(reflection_property, getAttributes) return; } GET_REFLECTION_OBJECT_PTR(ref); + if (ref->prop->attributes) { zval ret; @@ -6614,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 From 97bcedeb0df0d126d777affcfcbaad04c4267e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sun, 5 Apr 2020 08:02:07 +0200 Subject: [PATCH 18/37] Adjusted Opcache to PHP 8. --- ext/opcache/zend_file_cache.c | 94 ++++++++++----------------------- ext/opcache/zend_persist.c | 59 +++++++++------------ ext/opcache/zend_persist_calc.c | 27 +++++++--- 3 files changed, 74 insertions(+), 106 deletions(-) diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index f336428ab0d85..a5d661c7a544f 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -158,6 +158,25 @@ static int zend_file_cache_flock(int fd, int type) } \ } while (0) +#define SERIALIZE_ATTRIBUTES(attr) do { \ + if ((attr) && !IS_SERIALIZED(attr)) { \ + HashTable *ht; \ + SERIALIZE_PTR(attr); \ + ht = (attr); \ + UNSERIALIZE_PTR(ht); \ + zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval); \ + } \ +} while (0) + +#define UNSERIALIZE_ATTRIBUTES(attr) do { \ + if ((attr) && !IS_UNSERIALIZED(attr)) { \ + HashTable *ht; \ + UNSERIALIZE_PTR(attr); \ + ht = (attr); \ + zend_file_cache_unserialize_hash(ht, script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR); \ + } \ +} while (0) + static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX}; @@ -421,14 +440,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_PTR(op_array->live_range); SERIALIZE_PTR(op_array->scope); SERIALIZE_STR(op_array->doc_comment); - if (op_array->attributes && !IS_SERIALIZED(op_array->attributes)) { - HashTable *ht; - - SERIALIZE_PTR(op_array->attributes); - ht = op_array->attributes; - UNSERIALIZE_PTR(ht); - zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval); - } + SERIALIZE_ATTRIBUTES(op_array->attributes); SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->prototype); return; @@ -555,6 +567,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_PTR(op_array->live_range); SERIALIZE_PTR(op_array->scope); SERIALIZE_STR(op_array->doc_comment); + SERIALIZE_ATTRIBUTES(op_array->attributes); SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->prototype); @@ -599,15 +612,7 @@ static void zend_file_cache_serialize_prop_info(zval *zv, if (prop->doc_comment) { SERIALIZE_STR(prop->doc_comment); } - - if (prop->attributes) { - HashTable *ht; - - SERIALIZE_PTR(prop->attributes); - ht = prop->attributes; - UNSERIALIZE_PTR(ht); - zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval); - } + SERIALIZE_ATTRIBUTES(prop->attributes); zend_file_cache_serialize_type(&prop->type, script, info, buf); } @@ -636,14 +641,7 @@ static void zend_file_cache_serialize_class_constant(zval *z SERIALIZE_STR(c->doc_comment); } - if (c->attributes) { - HashTable *ht; - - SERIALIZE_PTR(c->attributes); - ht = c->attributes; - UNSERIALIZE_PTR(ht); - zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval); - } + SERIALIZE_ATTRIBUTES(c->attributes); } } } @@ -701,14 +699,7 @@ static void zend_file_cache_serialize_class(zval *zv, zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_class_constant); SERIALIZE_STR(ce->info.user.filename); SERIALIZE_STR(ce->info.user.doc_comment); - if (ce->info.user.attributes && !IS_SERIALIZED(ce->info.user.attributes)) { - HashTable *ht; - - SERIALIZE_PTR(ce->info.user.attributes); - ht = ce->info.user.attributes; - UNSERIALIZE_PTR(ht); - zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval); - } + SERIALIZE_ATTRIBUTES(ce->info.user.attributes); zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info); if (ce->properties_info_table) { @@ -1174,6 +1165,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_PTR(op_array->live_range); UNSERIALIZE_PTR(op_array->scope); UNSERIALIZE_STR(op_array->doc_comment); + UNSERIALIZE_ATTRIBUTES(op_array->attributes); UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->prototype); return; @@ -1289,14 +1281,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_PTR(op_array->live_range); UNSERIALIZE_PTR(op_array->scope); UNSERIALIZE_STR(op_array->doc_comment); - if (op_array->attributes && !IS_UNSERIALIZED(op_array->attributes)) { - HashTable *ht; - - UNSERIALIZE_PTR(op_array->attributes); - ht = op_array->attributes; - zend_file_cache_unserialize_hash(ht, - script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR); - } + UNSERIALIZE_ATTRIBUTES(op_array->attributes); UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->prototype); @@ -1350,14 +1335,7 @@ static void zend_file_cache_unserialize_prop_info(zval *zv, if (prop->doc_comment) { UNSERIALIZE_STR(prop->doc_comment); } - if (prop->attributes && !IS_UNSERIALIZED(prop->attributes)) { - HashTable *ht; - - UNSERIALIZE_PTR(prop->attributes); - ht = prop->attributes; - zend_file_cache_unserialize_hash(ht, - script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR); - } + UNSERIALIZE_ATTRIBUTES(prop->attributes); zend_file_cache_unserialize_type(&prop->type, script, buf); } } @@ -1382,14 +1360,7 @@ static void zend_file_cache_unserialize_class_constant(zval * if (c->doc_comment) { UNSERIALIZE_STR(c->doc_comment); } - if (c->attributes && !IS_UNSERIALIZED(c->attributes)) { - HashTable *ht; - - UNSERIALIZE_PTR(c->attributes); - ht = c->attributes; - zend_file_cache_unserialize_hash(ht, - script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR); - } + UNSERIALIZE_ATTRIBUTES(c->attributes); } } } @@ -1444,14 +1415,7 @@ static void zend_file_cache_unserialize_class(zval *zv, script, buf, zend_file_cache_unserialize_class_constant, NULL); UNSERIALIZE_STR(ce->info.user.filename); UNSERIALIZE_STR(ce->info.user.doc_comment); - if (ce->info.user.attributes && !IS_UNSERIALIZED(ce->info.user.attributes)) { - HashTable *ht; - - UNSERIALIZE_PTR(ce->info.user.attributes); - ht = ce->info.user.attributes; - zend_file_cache_unserialize_hash(ht, - script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR); - } + UNSERIALIZE_ATTRIBUTES(ce->info.user.attributes); zend_file_cache_unserialize_hash(&ce->properties_info, script, buf, zend_file_cache_unserialize_prop_info, NULL); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 4f3e84cd19f36..7199aa35b514c 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -76,6 +76,25 @@ } \ } while (0) +#define zend_persist_attributes(attr) do { \ + HashTable *ptr = zend_shared_alloc_get_xlat_entry(attr); \ + if (ptr) { \ + (attr) = ptr; \ + } else { \ + Bucket *p; \ + zend_hash_persist(attr); \ + ZEND_HASH_FOREACH_BUCKET((attr), p) { \ + if (p->key) { \ + zend_accel_store_interned_string(p->key); \ + } \ + zend_persist_zval(&p->val); \ + } ZEND_HASH_FOREACH_END(); \ + (attr) = zend_shared_memdup_put_free((attr), sizeof(HashTable)); \ + GC_SET_REFCOUNT((attr), 2); \ + GC_TYPE_INFO(attr) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << GC_FLAGS_SHIFT); \ + } \ +} while (0) + typedef void (*zend_persist_func_t)(zval*); static void zend_persist_zval(zval *z); @@ -369,6 +388,9 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc op_array->doc_comment = NULL; } } + if (op_array->attributes) { + zend_persist_attributes(op_array->attributes); + } if (op_array->try_catch_array) { op_array->try_catch_array = zend_shared_alloc_get_xlat_entry(op_array->try_catch_array); @@ -551,18 +573,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc } if (op_array->attributes) { - if (already_stored) { - op_array->attributes = zend_shared_alloc_get_xlat_entry(op_array->attributes); - ZEND_ASSERT(op_array->attributes != NULL); - } else { - zend_hash_persist(op_array->attributes, zend_persist_zval); - zend_accel_store(op_array->attributes, sizeof(HashTable)); - /* make immutable array */ - GC_REFCOUNT(op_array->attributes) = 2; - GC_TYPE_INFO(op_array->attributes) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8); - op_array->attributes->u.flags |= HASH_FLAG_STATIC_KEYS; - op_array->attributes->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; - } + zend_persist_attributes(op_array->attributes); } if (op_array->try_catch_array) { @@ -700,13 +711,7 @@ static void zend_persist_property_info(zval *zv) } } if (prop->attributes) { - zend_hash_persist(prop->attributes, zend_persist_zval); - zend_accel_store(prop->attributes, sizeof(HashTable)); - /* make immutable array */ - GC_REFCOUNT(prop->attributes) = 2; - GC_TYPE_INFO(prop->attributes) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8); - prop->attributes->u.flags |= HASH_FLAG_STATIC_KEYS; - prop->attributes->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; + zend_persist_attributes(prop->attributes); } zend_persist_type(&prop->type); } @@ -748,13 +753,7 @@ static void zend_persist_class_constant(zval *zv) } } if (c->attributes) { - zend_hash_persist(c->attributes, zend_persist_zval); - zend_accel_store(c->attributes, sizeof(HashTable)); - /* make immutable array */ - GC_REFCOUNT(c->attributes) = 2; - GC_TYPE_INFO(c->attributes) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8); - c->attributes->u.flags |= HASH_FLAG_STATIC_KEYS; - c->attributes->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; + zend_persist_attributes(c->attributes); } } @@ -843,13 +842,7 @@ static void zend_persist_class_entry(zval *zv) } } if (ce->info.user.attributes) { - zend_hash_persist(ce->info.user.attributes, zend_persist_zval); - zend_accel_store(ce->info.user.attributes, sizeof(HashTable)); - /* make immutable array */ - GC_REFCOUNT(ce->info.user.attributes) = 2; - GC_TYPE_INFO(ce->info.user.attributes) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << 8); - ce->info.user.attributes->u.flags |= HASH_FLAG_STATIC_KEYS; - ce->info.user.attributes->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; + zend_persist_attributes(ce->info.user.attributes); } zend_hash_persist(&ce->properties_info); ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) { diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 7b0258f6da28e..ee880d53816c1 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -53,6 +53,21 @@ } \ } while (0) +#define zend_persist_attributes_calc(attr) do { \ + if (!zend_shared_alloc_get_xlat_entry(attr)) { \ + Bucket *p; \ + zend_shared_alloc_register_xlat_entry((attr), (attr)); \ + ADD_SIZE(sizeof(HashTable)); \ + zend_hash_persist_calc(attr); \ + ZEND_HASH_FOREACH_BUCKET((attr), p) { \ + if (p->key) { \ + ADD_INTERNED_STRING(p->key); \ + } \ + zend_persist_zval_calc(&p->val); \ + } ZEND_HASH_FOREACH_END(); \ + } \ +} while (0) + static void zend_persist_zval_calc(zval *z); static void zend_hash_persist_calc(HashTable *ht) @@ -256,8 +271,7 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) } if (op_array->attributes) { - ADD_DUP_SIZE(op_array->attributes, sizeof(HashTable)); - zend_hash_persist_calc(op_array->attributes, zend_persist_zval_calc); + zend_persist_attributes_calc(op_array->attributes); } if (op_array->try_catch_array) { @@ -329,8 +343,7 @@ static void zend_persist_property_info_calc(zval *zv) ADD_STRING(prop->doc_comment); } if (prop->attributes) { - ADD_DUP_SIZE(prop->attributes, sizeof(HashTable)); - zend_hash_persist_calc(prop->attributes, zend_persist_zval_calc); + zend_persist_attributes_calc(prop->attributes); } } } @@ -347,8 +360,7 @@ static void zend_persist_class_constant_calc(zval *zv) ADD_STRING(c->doc_comment); } if (c->attributes) { - ADD_DUP_SIZE(c->attributes, sizeof(HashTable)); - zend_hash_persist_calc(c->attributes, zend_persist_zval_calc); + zend_persist_attributes_calc(c->attributes); } } } @@ -436,8 +448,7 @@ static void zend_persist_class_entry_calc(zval *zv) ADD_STRING(ce->info.user.doc_comment); } if (ce->info.user.attributes) { - ADD_DUP_SIZE(ce->info.user.attributes, sizeof(HashTable)); - zend_hash_persist_calc(ce->info.user.attributes, zend_persist_zval_calc); + zend_persist_attributes_calc(ce->info.user.attributes); } zend_hash_persist_calc(&ce->properties_info); From 214c43593e7f73c74fcc8d6876d712b8f94da49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sun, 5 Apr 2020 08:30:23 +0200 Subject: [PATCH 19/37] Added support for empty arg list. Added AST evaluation test case. --- Zend/zend_language_parser.y | 2 + ext/reflection/tests/attributes/ast.phpt | 68 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 ext/reflection/tests/attributes/ast.phpt diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 0d0f77e073e46..6b1f01c7f5a2d 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -322,6 +322,8 @@ attribute_arguments: attribute_decl: 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); } ; 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) From 34a8904141fd23e75b6121b455e73d09e4662b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sun, 5 Apr 2020 10:32:43 +0200 Subject: [PATCH 20/37] Fixed property attributes. Added attribute placement a test case. --- Zend/zend_ast.c | 2 +- Zend/zend_ast.h | 2 +- Zend/zend_compile.c | 2 +- Zend/zend_language_parser.y | 2 +- .../tests/attributes/placement.phpt | 112 ++++++++++++++++++ 5 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 ext/reflection/tests/attributes/placement.phpt diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 810b0afd981bd..170cb6d260efe 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2142,7 +2142,7 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) ast->lineno = ast->child[0]->lineno; break; default: - zend_ast_destroy(attr); + zend_error_noreturn(E_COMPILE_ERROR, "Invalid use of attributes"); } return ast; diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index c8286f08cad69..97595b97b89d5 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -149,13 +149,13 @@ enum _zend_ast_kind { ZEND_AST_TRY, ZEND_AST_CATCH, + 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_GROUP, ZEND_AST_PARAM, }; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d275a33358c50..1c1f13d213c6c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6526,7 +6526,7 @@ void zend_compile_prop_group(zend_ast *list) /* {{{ */ zend_ast *type_ast = list->child[0]; zend_ast *prop_ast = list->child[1]; - attributes = list->child[3] ? zend_compile_attributes(list->child[3]) : NULL; + attributes = list->child[2] ? zend_compile_attributes(list->child[2]) : NULL; zend_compile_prop_decl(prop_ast, type_ast, list->attr, attributes); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 6b1f01c7f5a2d..37b5005e35c1c 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -782,7 +782,7 @@ class_statement_list: annotated_class_statement: variable_modifiers optional_type_without_static property_list ';' - { $$ = zend_ast_create(ZEND_AST_PROP_GROUP, $2, $3, NULL, NULL); + { $$ = zend_ast_create(ZEND_AST_PROP_GROUP, $2, $3, NULL); $$->attr = $1; } | method_modifiers T_CONST class_const_list ';' { $$ = $3; $$->attr = $1; } 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) +} From 09be41296a6847c207f82367539f378e022f20a4 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 5 Apr 2020 13:33:57 +0200 Subject: [PATCH 21/37] merge conflict --- ext/reflection/php_reflection_arginfo.h | 27 +++++-------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 3af54807de64f..9e3f80e6c671a 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -326,13 +326,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_getDefaultValue arginfo_class_ReflectionFunctionAbstract___clone -<<<<<<< HEAD #define arginfo_class_ReflectionProperty_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes -#define arginfo_class_ReflectionClassConstant___clone arginfo_class_Reflector___toString -======= #define arginfo_class_ReflectionClassConstant___clone arginfo_class_ReflectionFunctionAbstract___clone ->>>>>>> master #define arginfo_class_ReflectionClassConstant___construct arginfo_class_ReflectionProperty___construct @@ -354,13 +350,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassConstant_getDocComment arginfo_class_ReflectionFunctionAbstract___clone -<<<<<<< HEAD #define arginfo_class_ReflectionClassConstant_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes -#define arginfo_class_ReflectionParameter___clone arginfo_class_Reflector___toString -======= #define arginfo_class_ReflectionParameter___clone arginfo_class_ReflectionFunctionAbstract___clone ->>>>>>> master ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionParameter___construct, 0, 0, 2) ZEND_ARG_INFO(0, function) @@ -405,13 +397,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionParameter_isVariadic arginfo_class_ReflectionFunctionAbstract___clone -<<<<<<< HEAD #define arginfo_class_ReflectionParameter_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes -#define arginfo_class_ReflectionType___clone arginfo_class_Reflector___toString -======= #define arginfo_class_ReflectionType___clone arginfo_class_ReflectionFunctionAbstract___clone ->>>>>>> master #define arginfo_class_ReflectionType_allowsNull arginfo_class_ReflectionFunctionAbstract___clone @@ -473,24 +461,19 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionReference_fromArr ZEND_ARG_INFO(0, key) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionReference_getId, 0, 0, IS_STRING, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_ReflectionReference_getId arginfo_class_ReflectionFunction___toString #define arginfo_class_ReflectionReference___clone arginfo_class_ReflectionFunctionAbstract___clone -<<<<<<< HEAD -#define arginfo_class_ReflectionReference___construct arginfo_class_Reflector___toString +#define arginfo_class_ReflectionReference___construct arginfo_class_ReflectionFunctionAbstract___clone -#define arginfo_class_ReflectionAttribute_getName arginfo_class_ReflectionReference_getId +#define arginfo_class_ReflectionAttribute_getName arginfo_class_ReflectionFunction___toString #define arginfo_class_ReflectionAttribute_getArguments arginfo_class_ReflectionUnionType_getTypes ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionAttribute_getAsObject, 0, 0, IS_OBJECT, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ReflectionAttribute___clone arginfo_class_Reflector___toString +#define arginfo_class_ReflectionAttribute___clone arginfo_class_ReflectionFunctionAbstract___clone -#define arginfo_class_ReflectionAttribute___construct arginfo_class_Reflector___toString -======= -#define arginfo_class_ReflectionReference___construct arginfo_class_ReflectionFunctionAbstract___clone ->>>>>>> master +#define arginfo_class_ReflectionAttribute___construct arginfo_class_ReflectionFunctionAbstract___clone From 6efec1b4515f860e76d1e26ab563d8418c37378c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 5 Apr 2020 14:12:52 +0200 Subject: [PATCH 22/37] Fix tests and rename variable --- Zend/tests/attributes_001.phpt | 76 ++++++++++++++++++++++++--------- Zend/tests/attributes_004.phpt | 10 ----- Zend/tests/attributes_005.phpt | 12 ------ Zend/tests/attributes_007.phpt | 31 -------------- ext/reflection/php_reflection.c | 16 +++---- 5 files changed, 65 insertions(+), 80 deletions(-) delete mode 100644 Zend/tests/attributes_004.phpt delete mode 100644 Zend/tests/attributes_005.phpt delete mode 100644 Zend/tests/attributes_007.phpt diff --git a/Zend/tests/attributes_001.phpt b/Zend/tests/attributes_001.phpt index 97a383b59c525..a1ec90bbc1600 100644 --- a/Zend/tests/attributes_001.phpt +++ b/Zend/tests/attributes_001.phpt @@ -70,68 +70,106 @@ array(0) { } array(1) { ["TestFunction"]=> - array(0) { + array(1) { + [0]=> + array(0) { + } } } array(1) { ["TestClass"]=> - array(0) { + array(1) { + [0]=> + array(0) { + } } } array(1) { ["TestConst"]=> - array(0) { + array(1) { + [0]=> + array(0) { + } } } array(1) { ["TestProp"]=> - array(0) { + array(1) { + [0]=> + array(0) { + } } } array(3) { ["a1"]=> - array(0) { + array(1) { + [0]=> + array(0) { + } } ["a2"]=> array(1) { [0]=> - int(1) + array(1) { + [0]=> + int(1) + } } ["a3"]=> - array(2) { + array(1) { [0]=> - int(1) - [1]=> - int(2) + array(2) { + [0]=> + int(1) + [1]=> + int(2) + } } } array(4) { ["a1"]=> - array(0) { + array(1) { + [0]=> + array(0) { + } } ["a2"]=> array(1) { [0]=> - int(2) + array(1) { + [0]=> + int(2) + } } ["a3"]=> array(1) { [0]=> - int(4) + array(2) { + [0]=> + int(4) + [1]=> + int(4) + } } ["a4"]=> array(1) { [0]=> - array(2) { - ["a"]=> - int(1) - ["b"]=> - int(2) + array(1) { + [0]=> + array(2) { + ["a"]=> + int(1) + ["b"]=> + int(2) + } } } } array(1) { ["Foo\Bar"]=> - array(0) { + array(1) { + [0]=> + array(0) { + } } } diff --git a/Zend/tests/attributes_004.phpt b/Zend/tests/attributes_004.phpt deleted file mode 100644 index ea3605fc3cdba..0000000000000 --- a/Zend/tests/attributes_004.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -Redclare attribute errors ---FILE-- -> -<> -function foo() {} ---EXPECTF-- -Fatal error: Redeclared attribute A in %s on line %d diff --git a/Zend/tests/attributes_005.phpt b/Zend/tests/attributes_005.phpt deleted file mode 100644 index a294f8523f435..0000000000000 --- a/Zend/tests/attributes_005.phpt +++ /dev/null @@ -1,12 +0,0 @@ ---TEST-- -Redclare resolved attribute errors ---FILE-- -> -<<\Test\Attr>> -function foo() {} ---EXPECTF-- -Fatal error: Redeclared attribute Test\Attr in %s on line %d diff --git a/Zend/tests/attributes_007.phpt b/Zend/tests/attributes_007.phpt deleted file mode 100644 index 8469a9724bb4a..0000000000000 --- a/Zend/tests/attributes_007.phpt +++ /dev/null @@ -1,31 +0,0 @@ ---TEST-- -attributes: docblocks ---FILE-- -> -/** Bar */ -class Baz -{ -} - -/** foo */ -<> -class Boing -{ -} - -$reflection = new ReflectionClass(Baz::class); -var_dump(count($reflection->getAttributes())); -var_dump($reflection->getDocComment()); - -$reflection = new ReflectionClass(Boing::class); -var_dump(count($reflection->getAttributes())); -var_dump($reflection->getDocComment()); - ---EXPECTF-- -int(0) -string(10) "/** Bar */" -int(1) -string(10) "/** foo */" diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ef82e04890276..d3c311222cdb9 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1055,11 +1055,11 @@ static void reflection_attribute_factory(zval *object, zend_string *name, zval * } /* }}} */ -static int convert_ast_to_zval(zval *ret, zend_ast *ast, zend_class_entry *ce) +static int convert_ast_to_zval(zval *ret, zend_ast *ast, zend_class_entry *scope_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); + zval *zv = zend_get_constant_ex(name, scope_ce, ast->attr); if (UNEXPECTED(zv == NULL)) { return FAILURE; @@ -1069,7 +1069,7 @@ static int convert_ast_to_zval(zval *ret, zend_ast *ast, zend_class_entry *ce) } else { zval tmp; - if (UNEXPECTED(zend_ast_evaluate(&tmp, ast, ce) != SUCCESS)) { + if (UNEXPECTED(zend_ast_evaluate(&tmp, ast, scope_ce) != SUCCESS)) { return FAILURE; } @@ -1080,7 +1080,7 @@ static int convert_ast_to_zval(zval *ret, zend_ast *ast, zend_class_entry *ce) return SUCCESS; } -static int convert_ast_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) +static int convert_ast_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope_ce) { Bucket *p; zval tmp; @@ -1093,7 +1093,7 @@ static int convert_ast_attributes(zval *ret, HashTable *attributes, zend_class_e } if (Z_TYPE(p->val) == IS_CONSTANT_AST) { - if (FAILURE == convert_ast_to_zval(&tmp, Z_ASTVAL(p->val), ce)) { + if (FAILURE == convert_ast_to_zval(&tmp, Z_ASTVAL(p->val), scope_ce)) { return FAILURE; } @@ -1107,7 +1107,7 @@ static int convert_ast_attributes(zval *ret, HashTable *attributes, zend_class_e return SUCCESS; } -static int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *ce) +static int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope_ce) { Bucket *p; zval *v; @@ -1128,7 +1128,7 @@ static int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry 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)) { + if (FAILURE == convert_ast_attributes(&result, Z_ARRVAL(p->val), scope_ce)) { zval_ptr_dtor(ret); return FAILURE; } @@ -1144,7 +1144,7 @@ static int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry 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)) { + if (FAILURE == convert_ast_attributes(&result, Z_ARRVAL_P(zv), scope_ce)) { zval_ptr_dtor(ret); return FAILURE; } From 53371a5d07b25c9140024bfada3eb535c4f87813 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 5 Apr 2020 14:36:01 +0200 Subject: [PATCH 23/37] Add tests for attributes on all elements. --- Zend/tests/attributes_001.phpt | 173 ++++++++++++++++----------------- 1 file changed, 83 insertions(+), 90 deletions(-) diff --git a/Zend/tests/attributes_001.phpt b/Zend/tests/attributes_001.phpt index a1ec90bbc1600..793d64a78c2bf 100644 --- a/Zend/tests/attributes_001.phpt +++ b/Zend/tests/attributes_001.phpt @@ -1,10 +1,19 @@ --TEST-- -Basic attributes usage +Basic attributes usage on all elements --FILE-- element = $element; + } +} +class OtherAttr {} + +function dump_attributes($reflection) { $arr = []; - foreach ($attributes as $attribute) { + foreach ($reflection->getAttributes() as $attribute) { if (!isset($arr[$attribute->getName()])) { $arr[$attribute->getName()] = []; } @@ -12,164 +21,148 @@ function dump_attributes($attributes) { } var_dump($arr); } -// No attributes -function f0() { -} -$r = new ReflectionFunction("f0"); -dump_attributes($r->getAttributes()); -// Function attributes -<> -function foo() { -} -$r = new ReflectionFunction("foo"); -dump_attributes($r->getAttributes()); +function f0() {} +dump_attributes(new ReflectionFunction("f0")); + +<> +function foo() {} +dump_attributes(new ReflectionFunction("foo")); // Class attributes -<> +<> class Bar { - <> + <> const C = 2; - <> + <> public $x = 3; } -$r = new ReflectionClass("Bar"); -dump_attributes($r->getAttributes()); -$r1 = $r->getReflectionConstant("C"); -dump_attributes($r1->getAttributes()); -$r2 = $r->getProperty("x"); -dump_attributes($r2->getAttributes()); +$reflectionClass = new ReflectionClass("Bar"); +dump_attributes($reflectionClass); +dump_attributes($reflectionClass->getReflectionConstant("C")); +dump_attributes($reflectionClass->getProperty("x")); + +$foo = <>function () {}; +dump_attributes(new ReflectionFunction($foo)); + +$foo = <>fn ($x) => $x; +dump_attributes(new ReflectionFunction($foo)); + +$anonClass = new <>class {}; +dump_attributes(new ReflectionClass($anonClass)); // Multiple attributes with multiple values -<> -<> -<> +<> +<> function f1() {} -$r = new ReflectionFunction("f1"); -dump_attributes($r->getAttributes()); +dump_attributes(new ReflectionFunction("f1")); -// Attributes with AST -<> -<> -<> -<1,"b"=>2])>> +// multiple instances of same attribute +<> +<> function f2() {} -$r = new ReflectionFunction("f2"); -dump_attributes($r->getAttributes()); - -// Attributes with namespaces -<> -function f4() { -} -$r = new ReflectionFunction("f4"); -dump_attributes($r->getAttributes()); +dump_attributes(new ReflectionFunction("f2")); ?> --EXPECT-- array(0) { } array(1) { - ["TestFunction"]=> + ["Attr"]=> array(1) { [0]=> - array(0) { + array(1) { + [0]=> + string(8) "function" } } } array(1) { - ["TestClass"]=> + ["Attr"]=> array(1) { [0]=> - array(0) { + array(1) { + [0]=> + string(5) "class" } } } array(1) { - ["TestConst"]=> + ["Attr"]=> array(1) { [0]=> - array(0) { + array(1) { + [0]=> + string(5) "const" } } } array(1) { - ["TestProp"]=> + ["Attr"]=> array(1) { [0]=> - array(0) { + array(1) { + [0]=> + string(8) "property" } } } -array(3) { - ["a1"]=> +array(1) { + ["Attr"]=> array(1) { [0]=> - array(0) { + array(1) { + [0]=> + string(7) "closure" } } - ["a2"]=> +} +array(1) { + ["Attr"]=> array(1) { [0]=> array(1) { [0]=> - int(1) + string(13) "short closure" } } - ["a3"]=> +} +array(1) { + ["Attr"]=> array(1) { [0]=> - array(2) { + array(1) { [0]=> - int(1) - [1]=> - int(2) + string(10) "anon class" } } } -array(4) { - ["a1"]=> +array(2) { + ["Attr"]=> array(1) { [0]=> array(0) { } } - ["a2"]=> + ["OtherAttr"]=> array(1) { [0]=> - array(1) { - [0]=> - int(2) + array(0) { } } - ["a3"]=> - array(1) { +} +array(1) { + ["Attr"]=> + array(2) { [0]=> - array(2) { + array(1) { [0]=> - int(4) - [1]=> - int(4) + string(3) "foo" } - } - ["a4"]=> - array(1) { - [0]=> + [1]=> array(1) { [0]=> - array(2) { - ["a"]=> - int(1) - ["b"]=> - int(2) - } - } - } -} -array(1) { - ["Foo\Bar"]=> - array(1) { - [0]=> - array(0) { + string(3) "bar" } } } From ffaacd392c3fb8fde28ed3a60e824371cd6b7730 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 5 Apr 2020 14:38:34 +0200 Subject: [PATCH 24/37] Move tests from ext/reflection to Zend/tests/attributes* because its a language feature --- Zend/tests/attributes_001.phpt | 214 +++++++----------- .../tests/attributes_004.phpt | 0 .../tests/attributes/placement.phpt | 112 --------- 3 files changed, 79 insertions(+), 247 deletions(-) rename ext/reflection/tests/attributes/ast.phpt => Zend/tests/attributes_004.phpt (100%) delete mode 100644 ext/reflection/tests/attributes/placement.phpt diff --git a/Zend/tests/attributes_001.phpt b/Zend/tests/attributes_001.phpt index 793d64a78c2bf..a99500570a5df 100644 --- a/Zend/tests/attributes_001.phpt +++ b/Zend/tests/attributes_001.phpt @@ -1,168 +1,112 @@ --TEST-- -Basic attributes usage on all elements +Attributes can be placed on all supported elements. --FILE-- element = $element; - } +<> +class Foo +{ + <> + public const FOO = 'foo', BAR = 'bar'; + + <> + public $x, $y; + + <> + public function foo(<> $a, <> $b) { } } -class OtherAttr {} -function dump_attributes($reflection) { - $arr = []; - foreach ($reflection->getAttributes() as $attribute) { - if (!isset($arr[$attribute->getName()])) { - $arr[$attribute->getName()] = []; - } - $arr[$attribute->getName()][] = $attribute->getArguments(); - } - var_dump($arr); -} +$object = new <> class () { }; -function f0() {} -dump_attributes(new ReflectionFunction("f0")); +<> +function f1() { } -<> -function foo() {} -dump_attributes(new ReflectionFunction("foo")); +$f2 = <> function () { }; -// Class attributes -<> -class Bar { - <> - const C = 2; - <> - public $x = 3; +$f3 = <> fn () => 1; -} -$reflectionClass = new ReflectionClass("Bar"); -dump_attributes($reflectionClass); -dump_attributes($reflectionClass->getReflectionConstant("C")); -dump_attributes($reflectionClass->getProperty("x")); +$ref = new \ReflectionClass(Foo::class); -$foo = <>function () {}; -dump_attributes(new ReflectionFunction($foo)); +$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) +]; -$foo = <>fn ($x) => $x; -dump_attributes(new ReflectionFunction($foo)); - -$anonClass = new <>class {}; -dump_attributes(new ReflectionClass($anonClass)); - -// Multiple attributes with multiple values -<> -<> -function f1() {} -dump_attributes(new ReflectionFunction("f1")); +foreach ($sources as $r) { + foreach ($r->getAttributes() as $attr) { + var_dump($attr->getName(), $attr->getArguments()); + } +} -// multiple instances of same attribute -<> -<> -function f2() {} -dump_attributes(new ReflectionFunction("f2")); ?> --EXPECT-- -array(0) { +string(2) "A1" +array(1) { + [0]=> + int(1) } +string(2) "A1" array(1) { - ["Attr"]=> - array(1) { - [0]=> - array(1) { - [0]=> - string(8) "function" - } - } + [0]=> + int(2) } +string(2) "A1" array(1) { - ["Attr"]=> - array(1) { - [0]=> - array(1) { - [0]=> - string(5) "class" - } - } + [0]=> + int(2) } +string(2) "A1" array(1) { - ["Attr"]=> - array(1) { - [0]=> - array(1) { - [0]=> - string(5) "const" - } - } + [0]=> + int(3) } +string(2) "A1" array(1) { - ["Attr"]=> - array(1) { - [0]=> - array(1) { - [0]=> - string(8) "property" - } - } + [0]=> + int(3) } +string(2) "A1" array(1) { - ["Attr"]=> - array(1) { - [0]=> - array(1) { - [0]=> - string(7) "closure" - } - } + [0]=> + int(4) } +string(2) "A1" array(1) { - ["Attr"]=> - array(1) { - [0]=> - array(1) { - [0]=> - string(13) "short closure" - } - } + [0]=> + int(5) } +string(2) "A1" array(1) { - ["Attr"]=> - array(1) { - [0]=> - array(1) { - [0]=> - string(10) "anon class" - } - } + [0]=> + int(6) } -array(2) { - ["Attr"]=> - array(1) { - [0]=> - array(0) { - } - } - ["OtherAttr"]=> - array(1) { - [0]=> - array(0) { - } - } +string(2) "A1" +array(1) { + [0]=> + int(7) } +string(2) "A1" array(1) { - ["Attr"]=> - array(2) { - [0]=> - array(1) { - [0]=> - string(3) "foo" - } - [1]=> - array(1) { - [0]=> - string(3) "bar" - } - } + [0]=> + int(8) +} +string(2) "A1" +array(1) { + [0]=> + int(9) +} +string(2) "A1" +array(1) { + [0]=> + int(10) } diff --git a/ext/reflection/tests/attributes/ast.phpt b/Zend/tests/attributes_004.phpt similarity index 100% rename from ext/reflection/tests/attributes/ast.phpt rename to Zend/tests/attributes_004.phpt diff --git a/ext/reflection/tests/attributes/placement.phpt b/ext/reflection/tests/attributes/placement.phpt deleted file mode 100644 index a99500570a5df..0000000000000 --- a/ext/reflection/tests/attributes/placement.phpt +++ /dev/null @@ -1,112 +0,0 @@ ---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) -} From ed9906260f7824f6695bc3a62bd780b6fd3f70ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sun, 5 Apr 2020 19:37:56 +0200 Subject: [PATCH 25/37] Implemented ReflectionAttribute::getAsObject(). Moved tests to dir. --- .../annotations.phpt} | 0 .../ast-nodes.phpt} | 0 .../name-resolution.phpt} | 0 Zend/tests/attributes/objects.phpt | 102 +++++++++++++ .../placement.phpt} | 0 Zend/zend_compile.c | 12 +- ext/reflection/php_reflection.c | 137 ++++++++++++++++-- 7 files changed, 233 insertions(+), 18 deletions(-) rename Zend/tests/{attributes_002.phpt => attributes/annotations.phpt} (100%) rename Zend/tests/{attributes_004.phpt => attributes/ast-nodes.phpt} (100%) rename Zend/tests/{attributes_003.phpt => attributes/name-resolution.phpt} (100%) create mode 100644 Zend/tests/attributes/objects.phpt rename Zend/tests/{attributes_001.phpt => attributes/placement.phpt} (100%) diff --git a/Zend/tests/attributes_002.phpt b/Zend/tests/attributes/annotations.phpt similarity index 100% rename from Zend/tests/attributes_002.phpt rename to Zend/tests/attributes/annotations.phpt diff --git a/Zend/tests/attributes_004.phpt b/Zend/tests/attributes/ast-nodes.phpt similarity index 100% rename from Zend/tests/attributes_004.phpt rename to Zend/tests/attributes/ast-nodes.phpt diff --git a/Zend/tests/attributes_003.phpt b/Zend/tests/attributes/name-resolution.phpt similarity index 100% rename from Zend/tests/attributes_003.phpt rename to Zend/tests/attributes/name-resolution.phpt diff --git a/Zend/tests/attributes/objects.phpt b/Zend/tests/attributes/objects.phpt new file mode 100644 index 0000000000000..0008e1faa7980 --- /dev/null +++ b/Zend/tests/attributes/objects.phpt @@ -0,0 +1,102 @@ +--TEST-- +Attributes can be converted into objects. +--FILE-- +name = $name; + $this->ttl = $ttl; + } +} + +$ref = new \ReflectionFunction(<> function () { }); + +foreach ($ref->getAttributes() as $attr) { + $obj = $attr->getAsObject(); + + var_dump(get_class($obj), $obj->name, $obj->ttl); +} + +echo "\n"; + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->getAsObject(); +} catch (\ArgumentCountError $e) { + var_dump('ERROR 1', $e->getMessage()); +} + +echo "\n"; + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->getAsObject(); +} catch (\TypeError $e) { + var_dump('ERROR 2', $e->getMessage()); +} + +echo "\n"; + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->getAsObject(); +} catch (\Error $e) { + var_dump('ERROR 3', $e->getMessage()); +} + +echo "\n"; + +class A3 +{ + private function __construct() { } +} + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->getAsObject(); +} catch (\Error $e) { + var_dump('ERROR 4', $e->getMessage()); +} + +echo "\n"; + +class A4 { } + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->getAsObject(); +} catch (\Error $e) { + var_dump('ERROR 5', $e->getMessage()); +} + +?> +--EXPECT-- +string(2) "A1" +string(4) "test" +int(50) + +string(7) "ERROR 1" +string(81) "Too few arguments to function A1::__construct(), 0 passed and at least 1 expected" + +string(7) "ERROR 2" +string(74) "A1::__construct(): Argument #1 ($name) must be of type string, array given" + +string(7) "ERROR 3" +string(30) "Attribute class 'A2' not found" + +string(7) "ERROR 4" +string(50) "Attribute constructor of class 'A3' must be public" + +string(7) "ERROR 5" +string(72) "Annotation class 'A4' does not have a constructor, cannot pass arguments" diff --git a/Zend/tests/attributes_001.phpt b/Zend/tests/attributes/placement.phpt similarity index 100% rename from Zend/tests/attributes_001.phpt rename to Zend/tests/attributes/placement.phpt diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f6619b128aada..e48e68b67bb6b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5729,19 +5729,14 @@ static void zend_compile_attribute(zval *v, zend_ast *ast) /* {{{ */ 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); + zend_const_expr_to_zval(zend_hash_next_index_insert(Z_ARRVAL_P(v), &tmp), list->child[i]); } } } @@ -5782,14 +5777,13 @@ static HashTable *zend_compile_attributes(zend_ast *ast) /* {{{ */ 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); + Z_ADDREF_P(x); array_init(&array); - add_next_index_zval(&array, &ref); + add_next_index_zval(&array, x); add_next_index_zval(&array, &a); zend_hash_update(attr, name, &array); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index d3c311222cdb9..41c9e53857887 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6477,14 +6477,14 @@ ZEND_METHOD(reflection_reference, getId) ZEND_METHOD(reflection_attribute, getName) { reflection_object *intern; - attribute_reference *param; + attribute_reference *attr; if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); } - GET_REFLECTION_OBJECT_PTR(param); + GET_REFLECTION_OBJECT_PTR(attr); - RETVAL_STR_COPY(param->name); + RETURN_STR_COPY(attr->name); } /* }}} */ @@ -6492,19 +6492,86 @@ ZEND_METHOD(reflection_attribute, getName) * Returns the arguments passed to the attribute */ ZEND_METHOD(reflection_attribute, getArguments) { + reflection_object *intern; + attribute_reference *attr; + if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); } + GET_REFLECTION_OBJECT_PTR(attr); - reflection_object *intern; - attribute_reference *param; + RETURN_ZVAL(&attr->arguments, 1, 0); +} +/* }}} */ - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); +static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc) /* {{{ */ +{ + zend_fcall_info fci; + zend_fcall_info_cache fcc; + + zend_function *ctor; + zend_class_entry *scope; + + zval retval; + int ret; + + ctor = ce->constructor; + + ZEND_ASSERT(ctor != NULL); + + if (!(ctor->common.fn_flags & ZEND_ACC_PUBLIC)) { + zend_throw_error(NULL, "Attribute constructor of class '%s' must be public", ZSTR_VAL(ce->name)); + return FAILURE; } - GET_REFLECTION_OBJECT_PTR(param); - RETVAL_ZVAL(¶m->arguments, 1, 0); + fci.size = sizeof(fci); + ZVAL_UNDEF(&fci.function_name); + fci.object = obj; + fci.retval = &retval; + fci.params = args; + fci.param_count = argc; + fci.no_separation = 1; + + fcc.function_handler = ctor; + fcc.called_scope = ce; + fcc.object = obj; + + scope = EG(fake_scope); + EG(fake_scope) = NULL; + + ret = zend_call_function(&fci, &fcc); + + EG(fake_scope) = scope; + + if (EG(exception)) { + zend_object_store_ctor_failed(obj); + } + + zval_ptr_dtor(&retval); + + if (ret != SUCCESS) { + zend_throw_error(NULL, "Failed to invoke constructor of attribute class '%s'", ZSTR_VAL(ce->name)); + } + + return EG(exception) ? FAILURE : SUCCESS; +} +/* }}} */ + +static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{ */ +{ + if (obj) { + zval_ptr_dtor(obj); + } + + if (args) { + uint32_t i; + + for (i = 0; i < argc; i++) { + zval_ptr_dtor(&args[i]); + } + + efree(args); + } } /* }}} */ @@ -6512,6 +6579,58 @@ ZEND_METHOD(reflection_attribute, getArguments) * Returns the attribute as an object */ ZEND_METHOD(reflection_attribute, getAsObject) { + reflection_object *intern; + attribute_reference *attr; + + zend_class_entry *ce; + zval obj; + + zval *args = NULL; + uint32_t count; + uint32_t argc = 0; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(attr); + + if (NULL == (ce = zend_lookup_class(attr->name))) { + zend_throw_error(NULL, "Attribute class '%s' not found", ZSTR_VAL(attr->name)); + RETURN_THROWS(); + } + + if (SUCCESS != object_init_ex(&obj, ce)) { + RETURN_THROWS(); + } + + count = zend_hash_num_elements(Z_ARRVAL(attr->arguments)); + + if (count) { + Bucket *p; + + args = emalloc(count * sizeof(zval)); + + ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL(attr->arguments), p) { + ZVAL_COPY(&args[argc], &p->val); + argc++; + } ZEND_HASH_FOREACH_END(); + } + + if (ce->constructor) { + if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc)) { + attribute_ctor_cleanup(&obj, args, argc); + RETURN_THROWS(); + } + } else if (argc) { + attribute_ctor_cleanup(&obj, args, argc); + zend_throw_error(NULL, "Annotation class '%s' does not have a constructor, cannot pass arguments", ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + + attribute_ctor_cleanup(NULL, args, argc); + + RETURN_ZVAL(&obj, 1, 1); } /* }}} */ From a376173722168094daf4467dbdc30effba210e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sun, 5 Apr 2020 20:19:40 +0200 Subject: [PATCH 26/37] Implemented attribute filtering (name and instanceof). --- Zend/tests/attributes/filter.phpt | 89 ++++++++++ ext/reflection/php_reflection.c | 218 +++++++++++++++++++----- ext/reflection/php_reflection.stub.php | 10 +- ext/reflection/php_reflection_arginfo.h | 1 + 4 files changed, 270 insertions(+), 48 deletions(-) create mode 100644 Zend/tests/attributes/filter.phpt diff --git a/Zend/tests/attributes/filter.phpt b/Zend/tests/attributes/filter.phpt new file mode 100644 index 0000000000000..931c3dc79ae07 --- /dev/null +++ b/Zend/tests/attributes/filter.phpt @@ -0,0 +1,89 @@ +--TEST-- +Attributes can be filtered by name and base type. +--FILE-- +> <> function () { }); +$attr = $ref->getAttributes(A3::class); + +var_dump(count($attr)); + +$ref = new \ReflectionFunction(<> <> function () { }); +$attr = $ref->getAttributes(A2::class); + +var_dump(count($attr), $attr[0]->getName()); + +$ref = new \ReflectionFunction(<> <> <> function () { }); +$attr = $ref->getAttributes(A2::class); + +var_dump(count($attr), $attr[0]->getName(), $attr[1]->getName()); + +echo "\n"; + +interface Base { } +class A1 implements Base { } +class A2 implements Base { } +class A3 extends A2 { } + +$ref = new \ReflectionFunction(<> <> <> function () { }); +$attr = $ref->getAttributes(\stdClass::class, true); +var_dump(count($attr)); +print_r(array_map(fn ($a) => $a->getName(), $attr)); + +$ref = new \ReflectionFunction(<> <> function () { }); +$attr = $ref->getAttributes(A1::class, true); +var_dump(count($attr)); +print_r(array_map(fn ($a) => $a->getName(), $attr)); + +$ref = new \ReflectionFunction(<> <> function () { }); +$attr = $ref->getAttributes(Base::class, true); +var_dump(count($attr)); +print_r(array_map(fn ($a) => $a->getName(), $attr)); + +$ref = new \ReflectionFunction(<> <> <> function () { }); +$attr = $ref->getAttributes(A2::class, true); +var_dump(count($attr)); +print_r(array_map(fn ($a) => $a->getName(), $attr)); + +$ref = new \ReflectionFunction(<> <> <> function () { }); +$attr = $ref->getAttributes(Base::class, true); +var_dump(count($attr)); +print_r(array_map(fn ($a) => $a->getName(), $attr)); + +?> +--EXPECT-- +int(0) +int(1) +string(2) "A2" +int(2) +string(2) "A2" +string(2) "A2" + +int(0) +Array +( +) +int(1) +Array +( + [0] => A1 +) +int(2) +Array +( + [0] => A1 + [1] => A2 +) +int(2) +Array +( + [0] => A2 + [1] => A3 +) +int(3) +Array +( + [0] => A1 + [1] => A2 + [2] => A3 +) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 41c9e53857887..4e93e9f6859b5 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1107,54 +1107,101 @@ static int convert_ast_attributes(zval *ret, HashTable *attributes, zend_class_e return SUCCESS; } -static int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope_ce) +static int load_attributes(zval *ret, HashTable *attr, zend_class_entry *scope) { - Bucket *p; - zval *v; - - zval result; zval obj; + zval result; - array_init(ret); + zval *v = zend_hash_index_find(attr, 0); - ZEND_HASH_FOREACH_BUCKET(attributes, p) { - if (!p->key) { - // Skip inlined parameter annotations. - continue; + if (Z_TYPE_P(v) == IS_STRING) { + if (FAILURE == convert_ast_attributes(&result, attr, scope)) { + zval_ptr_dtor(ret); + return FAILURE; } - ZEND_ASSERT(Z_TYPE(p->val) == IS_ARRAY); + 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); - v = zend_hash_index_find(Z_ARRVAL(p->val), 0); + ZEND_HASH_FOREACH_VAL(attr, zv) { + ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY); - if (Z_TYPE_P(v) == IS_STRING) { - if (FAILURE == convert_ast_attributes(&result, Z_ARRVAL(p->val), scope_ce)) { + if (FAILURE == convert_ast_attributes(&result, Z_ARRVAL_P(zv), scope)) { zval_ptr_dtor(ret); return FAILURE; } - reflection_attribute_factory(&obj, Z_STR_P(v), &result); + 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); - } else { - zval *zv; + } ZEND_HASH_FOREACH_END(); + } - ZEND_ASSERT(Z_TYPE_P(v) == IS_ARRAY); + return SUCCESS; +} - ZEND_HASH_FOREACH_VAL(Z_ARRVAL(p->val), zv) { - ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY); +static int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope, + zend_string *name, zend_class_entry *base) +{ + Bucket *p; - if (FAILURE == convert_ast_attributes(&result, Z_ARRVAL_P(zv), scope_ce)) { - zval_ptr_dtor(ret); - return FAILURE; - } + array_init(ret); - 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(); + if (name) { + // Name based filtering using lowercased key. + zend_string *filter = zend_string_tolower(name); + zval *x = zend_hash_find(attributes, filter); + + zend_string_release(filter); + + if (x) { + ZEND_ASSERT(Z_TYPE_P(x) == IS_ARRAY); + + load_attributes(ret, Z_ARRVAL_P(x), scope); } - } ZEND_HASH_FOREACH_END(); + } else { + ZEND_HASH_FOREACH_BUCKET(attributes, p) { + if (!p->key) { + // Skip inlined parameter annotations. + continue; + } + + ZEND_ASSERT(Z_TYPE(p->val) == IS_ARRAY); + + if (base) { + // Base type filtering. + zval *x = zend_hash_index_find(Z_ARRVAL(p->val), 0); + zend_class_entry *ce; + + if (Z_TYPE_P(x) == IS_STRING) { + ce = zend_lookup_class_ex(Z_STR_P(x), p->key, 0); + } else { + ZEND_ASSERT(Z_TYPE_P(x) == IS_ARRAY); + ce = zend_lookup_class_ex(Z_STR_P(zend_hash_index_find(Z_ARRVAL_P(x), 0)), p->key, 0); + } + + if (ce == NULL) { + // Bailout on error, otherwise ignore unavailable class. + if (EG(exception)) { + return FAILURE; + } + + continue; + } + + if (!instanceof_function(ce, base)) { + continue; + } + } + + load_attributes(ret, Z_ARRVAL(p->val), scope); + } ZEND_HASH_FOREACH_END(); + } return SUCCESS; } @@ -1771,15 +1818,32 @@ ZEND_METHOD(reflection_function, getAttributes) reflection_object *intern; zend_function *fptr; - if (zend_parse_parameters_none() == FAILURE) { - return; + zend_string *name = NULL; + zend_bool inherited = 0; + zend_class_entry *base = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { + RETURN_THROWS(); + } + + if (name && inherited) { + if (NULL == (base = zend_lookup_class(name))) { + if (!EG(exception)) { + zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); + } + + RETURN_THROWS(); + } + + name = NULL; } + GET_REFLECTION_OBJECT_PTR(fptr); if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.attributes) { zval ret; - if (FAILURE == convert_attributes(&ret, fptr->op_array.attributes, fptr->common.scope)) { + if (FAILURE == convert_attributes(&ret, fptr->op_array.attributes, fptr->common.scope, name, base)) { RETURN_THROWS(); } @@ -2719,9 +2783,26 @@ ZEND_METHOD(reflection_parameter, getAttributes) reflection_object *intern; parameter_reference *param; - if (zend_parse_parameters_none() == FAILURE) { + zend_string *name = NULL; + zend_bool inherited = 0; + zend_class_entry *base = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { RETURN_THROWS(); } + + if (name && inherited) { + if (NULL == (base = zend_lookup_class(name))) { + if (!EG(exception)) { + zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); + } + + RETURN_THROWS(); + } + + name = NULL; + } + GET_REFLECTION_OBJECT_PTR(param); if (param->fptr->type == ZEND_USER_FUNCTION && param->fptr->op_array.attributes) { @@ -2732,7 +2813,7 @@ ZEND_METHOD(reflection_parameter, getAttributes) ZEND_ASSERT(Z_TYPE_P(attr) == IS_ARRAY); - if (FAILURE == convert_attributes(&ret, Z_ARRVAL_P(attr), param->fptr->common.scope)) { + if (FAILURE == convert_attributes(&ret, Z_ARRVAL_P(attr), param->fptr->common.scope, name, base)) { RETURN_THROWS(); } @@ -3812,15 +3893,32 @@ ZEND_METHOD(reflection_class_constant, getAttributes) reflection_object *intern; zend_class_constant *ref; - if (zend_parse_parameters_none() == FAILURE) { - return; + zend_string *name = NULL; + zend_bool inherited = 0; + zend_class_entry *base = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { + RETURN_THROWS(); } + + if (name && inherited) { + if (NULL == (base = zend_lookup_class(name))) { + if (!EG(exception)) { + zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); + } + + RETURN_THROWS(); + } + + name = NULL; + } + GET_REFLECTION_OBJECT_PTR(ref); if (ref->attributes) { zval ret; - if (FAILURE == convert_attributes(&ret, ref->attributes, ref->ce)) { + if (FAILURE == convert_attributes(&ret, ref->attributes, ref->ce, name, base)) { RETURN_THROWS(); } @@ -4200,15 +4298,32 @@ ZEND_METHOD(reflection_class, getAttributes) reflection_object *intern; zend_class_entry *ce; - if (zend_parse_parameters_none() == FAILURE) { - return; + zend_string *name = NULL; + zend_bool inherited = 0; + zend_class_entry *base = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { + RETURN_THROWS(); } + + if (name && inherited) { + if (NULL == (base = zend_lookup_class(name))) { + if (!EG(exception)) { + zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); + } + + RETURN_THROWS(); + } + + name = NULL; + } + GET_REFLECTION_OBJECT_PTR(ce); if (ce->type == ZEND_USER_CLASS && ce->info.user.attributes) { zval ret; - if (FAILURE == convert_attributes(&ret, ce->info.user.attributes, ce)) { + if (FAILURE == convert_attributes(&ret, ce->info.user.attributes, ce, name, base)) { RETURN_THROWS(); } @@ -5729,15 +5844,32 @@ ZEND_METHOD(reflection_property, getAttributes) reflection_object *intern; property_reference *ref; - if (zend_parse_parameters_none() == FAILURE) { - return; + zend_string *name = NULL; + zend_bool inherited = 0; + zend_class_entry *base = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { + RETURN_THROWS(); } + + if (name && inherited) { + if (NULL == (base = zend_lookup_class(name))) { + if (!EG(exception)) { + zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); + } + + RETURN_THROWS(); + } + + name = NULL; + } + GET_REFLECTION_OBJECT_PTR(ref); if (ref->prop->attributes) { zval ret; - if (FAILURE == convert_attributes(&ret, ref->prop->attributes, ref->prop->ce)) { + if (FAILURE == convert_attributes(&ret, ref->prop->attributes, ref->prop->ce, name, base)) { RETURN_THROWS(); } diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index d0456878b0653..cb04ac29cb96f 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -94,7 +94,7 @@ public function hasReturnType() {} public function getReturnType() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null) {} + public function getAttributes(?string $name = null, bool $inheritance = false) {} } class ReflectionFunction extends ReflectionFunctionAbstract @@ -353,7 +353,7 @@ public function getNamespaceName() {} public function getShortName() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null) {} + public function getAttributes(?string $name = null, bool $inheritance = false) {} } class ReflectionObject extends ReflectionClass @@ -420,7 +420,7 @@ public function hasDefaultValue(): bool {} public function getDefaultValue() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null) {} + public function getAttributes(?string $name = null, bool $inheritance = false) {} } class ReflectionClassConstant implements Reflector @@ -456,7 +456,7 @@ public function getDeclaringClass() {} public function getDocComment() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null) {} + public function getAttributes(?string $name = null, bool $inheritance = false) {} } class ReflectionParameter implements Reflector @@ -525,7 +525,7 @@ public function getDefaultValueConstantName() {} public function isVariadic() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null) {} + public function getAttributes(?string $name = null, bool $inheritance = false) {} } abstract class ReflectionType implements Stringable diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 9e3f80e6c671a..150b4ed2952e7 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -59,6 +59,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getAttributes, 0, 0, 0) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) + ZEND_ARG_TYPE_INFO(0, inheritance, _IS_BOOL, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunction___construct, 0, 0, 1) From 79eb3559579b98f3de660a29f96f17c925046dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sun, 5 Apr 2020 21:12:59 +0200 Subject: [PATCH 27/37] Fixed failing reflection test case. --- ext/reflection/tests/ReflectionClass_toString_001.phpt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index 74ba22f1a9b62..3bc4ad7399bf3 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -110,8 +110,9 @@ Class [ class ReflectionClass implements Reflector, String Method [ public method getAttributes ] { - - Parameters [1] { - Parameter #0 [ $name ] + - Parameters [2] { + Parameter #0 [ ?string $name ] + Parameter #1 [ bool $inheritance ] } } From 47adcc38960150a25b57c3a4955b8bc7da325277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Mon, 6 Apr 2020 08:07:36 +0200 Subject: [PATCH 28/37] Extracted duplicated code into a function. Fixed annotation type. --- Zend/tests/attributes/objects.phpt | 2 +- ext/reflection/php_reflection.c | 212 ++++++++++------------------- 2 files changed, 72 insertions(+), 142 deletions(-) diff --git a/Zend/tests/attributes/objects.phpt b/Zend/tests/attributes/objects.phpt index 0008e1faa7980..d5c81a3f10cae 100644 --- a/Zend/tests/attributes/objects.phpt +++ b/Zend/tests/attributes/objects.phpt @@ -99,4 +99,4 @@ string(7) "ERROR 4" string(50) "Attribute constructor of class 'A3' must be public" string(7) "ERROR 5" -string(72) "Annotation class 'A4' does not have a constructor, cannot pass arguments" +string(71) "Attribute class 'A4' does not have a constructor, cannot pass arguments" diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 4e93e9f6859b5..293ba4ea95490 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1055,7 +1055,7 @@ static void reflection_attribute_factory(zval *object, zend_string *name, zval * } /* }}} */ -static int convert_ast_to_zval(zval *ret, zend_ast *ast, zend_class_entry *scope_ce) +static int convert_ast_to_zval(zval *ret, zend_ast *ast, zend_class_entry *scope_ce) /* {{{ */ { if (ast->kind == ZEND_AST_CONSTANT) { zend_string *name = zend_ast_get_constant_name(ast); @@ -1079,8 +1079,9 @@ static int convert_ast_to_zval(zval *ret, zend_ast *ast, zend_class_entry *scope return SUCCESS; } +/* }}} */ -static int convert_ast_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope_ce) +static int convert_ast_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope_ce) /* {{{ */ { Bucket *p; zval tmp; @@ -1106,8 +1107,9 @@ static int convert_ast_attributes(zval *ret, HashTable *attributes, zend_class_e return SUCCESS; } +/* }}} */ -static int load_attributes(zval *ret, HashTable *attr, zend_class_entry *scope) +static int load_attributes(zval *ret, HashTable *attr, zend_class_entry *scope) /* {{{ */ { zval obj; zval result; @@ -1144,9 +1146,10 @@ static int load_attributes(zval *ret, HashTable *attr, zend_class_entry *scope) return SUCCESS; } +/* }}} */ static int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope, - zend_string *name, zend_class_entry *base) + zend_string *name, zend_class_entry *base) /* {{{ */ { Bucket *p; @@ -1167,7 +1170,7 @@ static int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry } else { ZEND_HASH_FOREACH_BUCKET(attributes, p) { if (!p->key) { - // Skip inlined parameter annotations. + // Skip inlined parameter attributes. continue; } @@ -1205,6 +1208,43 @@ static int convert_attributes(zval *ret, HashTable *attributes, zend_class_entry return SUCCESS; } +/* }}} */ + +static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attributes, zend_class_entry *scope) /* {{{ */ +{ + zval ret; + + zend_string *name = NULL; + zend_bool inherited = 0; + zend_class_entry *base = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { + RETURN_THROWS(); + } + + if (name && inherited) { + if (NULL == (base = zend_lookup_class(name))) { + if (!EG(exception)) { + zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); + } + + RETURN_THROWS(); + } + + name = NULL; + } + + if (!attributes) { + RETURN_EMPTY_ARRAY(); + } + + if (FAILURE == convert_attributes(&ret, attributes, scope, name, base)) { + RETURN_THROWS(); + } + + RETURN_ZVAL(&ret, 1, 1); +} +/* }}} */ static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent) /* {{{ */ { @@ -1818,39 +1858,17 @@ ZEND_METHOD(reflection_function, getAttributes) reflection_object *intern; zend_function *fptr; - zend_string *name = NULL; - zend_bool inherited = 0; - zend_class_entry *base = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { - RETURN_THROWS(); - } - - if (name && inherited) { - if (NULL == (base = zend_lookup_class(name))) { - if (!EG(exception)) { - zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); - } - - RETURN_THROWS(); - } - - name = NULL; - } + HashTable *attributes = NULL; + zend_class_entry *scope = NULL; GET_REFLECTION_OBJECT_PTR(fptr); if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.attributes) { - zval ret; - - if (FAILURE == convert_attributes(&ret, fptr->op_array.attributes, fptr->common.scope, name, base)) { - RETURN_THROWS(); - } - - RETURN_ZVAL(&ret, 1, 1); + attributes = fptr->op_array.attributes; + scope = fptr->common.scope; } - RETURN_EMPTY_ARRAY(); + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, attributes, scope); } /* }}} */ @@ -2783,25 +2801,8 @@ ZEND_METHOD(reflection_parameter, getAttributes) reflection_object *intern; parameter_reference *param; - zend_string *name = NULL; - zend_bool inherited = 0; - zend_class_entry *base = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { - RETURN_THROWS(); - } - - if (name && inherited) { - if (NULL == (base = zend_lookup_class(name))) { - if (!EG(exception)) { - zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); - } - - RETURN_THROWS(); - } - - name = NULL; - } + HashTable *attributes = NULL; + zend_class_entry *scope = NULL; GET_REFLECTION_OBJECT_PTR(param); @@ -2809,19 +2810,14 @@ ZEND_METHOD(reflection_parameter, getAttributes) 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, name, base)) { - RETURN_THROWS(); - } - - RETURN_ZVAL(&ret, 1, 1); + attributes = Z_ARRVAL_P(attr); + scope = param->fptr->common.scope; } } - RETURN_EMPTY_ARRAY(); + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, attributes, scope); } /* {{{ proto public bool ReflectionParameter::getPosition() @@ -3893,39 +3889,17 @@ ZEND_METHOD(reflection_class_constant, getAttributes) reflection_object *intern; zend_class_constant *ref; - zend_string *name = NULL; - zend_bool inherited = 0; - zend_class_entry *base = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { - RETURN_THROWS(); - } - - if (name && inherited) { - if (NULL == (base = zend_lookup_class(name))) { - if (!EG(exception)) { - zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); - } - - RETURN_THROWS(); - } - - name = NULL; - } + HashTable *attributes = NULL; + zend_class_entry *scope = NULL; GET_REFLECTION_OBJECT_PTR(ref); if (ref->attributes) { - zval ret; - - if (FAILURE == convert_attributes(&ret, ref->attributes, ref->ce, name, base)) { - RETURN_THROWS(); - } - - RETURN_ZVAL(&ret, 1, 1); + attributes = ref->attributes; + scope = ref->ce; } - RETURN_EMPTY_ARRAY(); + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, attributes, scope); } /* }}} */ @@ -4298,39 +4272,17 @@ ZEND_METHOD(reflection_class, getAttributes) reflection_object *intern; zend_class_entry *ce; - zend_string *name = NULL; - zend_bool inherited = 0; - zend_class_entry *base = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { - RETURN_THROWS(); - } - - if (name && inherited) { - if (NULL == (base = zend_lookup_class(name))) { - if (!EG(exception)) { - zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); - } - - RETURN_THROWS(); - } - - name = NULL; - } + HashTable *attributes = NULL; + zend_class_entry *scope = NULL; GET_REFLECTION_OBJECT_PTR(ce); if (ce->type == ZEND_USER_CLASS && ce->info.user.attributes) { - zval ret; - - if (FAILURE == convert_attributes(&ret, ce->info.user.attributes, ce, name, base)) { - RETURN_THROWS(); - } - - RETURN_ZVAL(&ret, 1, 1); + attributes = ce->info.user.attributes; + scope = ce; } - RETURN_EMPTY_ARRAY(); + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, attributes, scope); } /* }}} */ @@ -5844,39 +5796,17 @@ ZEND_METHOD(reflection_property, getAttributes) reflection_object *intern; property_reference *ref; - zend_string *name = NULL; - zend_bool inherited = 0; - zend_class_entry *base = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { - RETURN_THROWS(); - } - - if (name && inherited) { - if (NULL == (base = zend_lookup_class(name))) { - if (!EG(exception)) { - zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); - } - - RETURN_THROWS(); - } - - name = NULL; - } + HashTable *attributes = NULL; + zend_class_entry *scope = NULL; GET_REFLECTION_OBJECT_PTR(ref); if (ref->prop->attributes) { - zval ret; - - if (FAILURE == convert_attributes(&ret, ref->prop->attributes, ref->prop->ce, name, base)) { - RETURN_THROWS(); - } - - RETURN_ZVAL(&ret, 1, 1); + attributes = ref->prop->attributes; + scope = ref->prop->ce; } - RETURN_EMPTY_ARRAY(); + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, attributes, scope); } /* }}} */ @@ -6756,7 +6686,7 @@ ZEND_METHOD(reflection_attribute, getAsObject) } } else if (argc) { attribute_ctor_cleanup(&obj, args, argc); - zend_throw_error(NULL, "Annotation class '%s' does not have a constructor, cannot pass arguments", ZSTR_VAL(ce->name)); + zend_throw_error(NULL, "Attribute class '%s' does not have a constructor, cannot pass arguments", ZSTR_VAL(ce->name)); RETURN_THROWS(); } From 551c7d5f810f950218bf8cc0a091a6175fdd6f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Mon, 6 Apr 2020 08:24:32 +0200 Subject: [PATCH 29/37] Changed boolean argument of getAttributes() into a flags type. --- Zend/tests/attributes/filter.phpt | 33 ++++++++++++++++--- ext/reflection/php_reflection.c | 15 +++++++-- ext/reflection/php_reflection.stub.php | 10 +++--- ext/reflection/php_reflection_arginfo.h | 2 +- .../tests/ReflectionClass_toString_001.phpt | 2 +- 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/Zend/tests/attributes/filter.phpt b/Zend/tests/attributes/filter.phpt index 931c3dc79ae07..713c982cef59b 100644 --- a/Zend/tests/attributes/filter.phpt +++ b/Zend/tests/attributes/filter.phpt @@ -26,30 +26,48 @@ class A2 implements Base { } class A3 extends A2 { } $ref = new \ReflectionFunction(<> <> <> function () { }); -$attr = $ref->getAttributes(\stdClass::class, true); +$attr = $ref->getAttributes(\stdClass::class, \ReflectionAttribute::FILTER_INSTANCEOF); var_dump(count($attr)); print_r(array_map(fn ($a) => $a->getName(), $attr)); $ref = new \ReflectionFunction(<> <> function () { }); -$attr = $ref->getAttributes(A1::class, true); +$attr = $ref->getAttributes(A1::class, \ReflectionAttribute::FILTER_INSTANCEOF); var_dump(count($attr)); print_r(array_map(fn ($a) => $a->getName(), $attr)); $ref = new \ReflectionFunction(<> <> function () { }); -$attr = $ref->getAttributes(Base::class, true); +$attr = $ref->getAttributes(Base::class, \ReflectionAttribute::FILTER_INSTANCEOF); var_dump(count($attr)); print_r(array_map(fn ($a) => $a->getName(), $attr)); $ref = new \ReflectionFunction(<> <> <> function () { }); -$attr = $ref->getAttributes(A2::class, true); +$attr = $ref->getAttributes(A2::class, \ReflectionAttribute::FILTER_INSTANCEOF); var_dump(count($attr)); print_r(array_map(fn ($a) => $a->getName(), $attr)); $ref = new \ReflectionFunction(<> <> <> function () { }); -$attr = $ref->getAttributes(Base::class, true); +$attr = $ref->getAttributes(Base::class, \ReflectionAttribute::FILTER_INSTANCEOF); var_dump(count($attr)); print_r(array_map(fn ($a) => $a->getName(), $attr)); +echo "\n"; + +$ref = new \ReflectionFunction(function () { }); + +try { + $ref->getAttributes(A1::class, 3); +} catch (\Error $e) { + var_dump('ERROR 1', $e->getMessage()); +} + +$ref = new \ReflectionFunction(function () { }); + +try { + $ref->getAttributes(SomeMissingClass::class, \ReflectionAttribute::FILTER_INSTANCEOF); +} catch (\Error $e) { + var_dump('ERROR 2', $e->getMessage()); +} + ?> --EXPECT-- int(0) @@ -87,3 +105,8 @@ Array [1] => A2 [2] => A3 ) + +string(7) "ERROR 1" +string(39) "Invalid attribute filter flag specified" +string(7) "ERROR 2" +string(34) "Class 'SomeMissingClass' not found" diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 293ba4ea95490..486709e84bcf0 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -111,6 +111,8 @@ PHPAPI zend_class_entry *reflection_attribute_ptr; #define REGISTER_REFLECTION_CLASS_CONST_LONG(class_name, const_name, value) \ zend_declare_class_constant_long(reflection_ ## class_name ## _ptr, const_name, sizeof(const_name)-1, (zend_long)value); +#define REFLECTION_ATTRIBUTE_FILTER_INSTANCEOF (1 << 1) + /* {{{ Object structure */ /* Struct for properties */ @@ -1215,14 +1217,19 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut zval ret; zend_string *name = NULL; - zend_bool inherited = 0; + zend_long flags = 0; zend_class_entry *base = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sb", &name, &inherited) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sl", &name, &flags) == FAILURE) { RETURN_THROWS(); } - if (name && inherited) { + if (flags & ~REFLECTION_ATTRIBUTE_FILTER_INSTANCEOF) { + zend_throw_error(NULL, "Invalid attribute filter flag specified"); + RETURN_THROWS(); + } + + if (name && (flags & REFLECTION_ATTRIBUTE_FILTER_INSTANCEOF)) { if (NULL == (base = zend_lookup_class(name))) { if (!EG(exception)) { zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); @@ -7138,6 +7145,8 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ _reflection_entry.ce_flags |= ZEND_ACC_FINAL; reflection_attribute_ptr = zend_register_internal_class(&_reflection_entry); + REGISTER_REFLECTION_CLASS_CONST_LONG(attribute, "FILTER_INSTANCEOF", REFLECTION_ATTRIBUTE_FILTER_INSTANCEOF); + REFLECTION_G(key_initialized) = 0; return SUCCESS; diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index cb04ac29cb96f..a708361e51681 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -94,7 +94,7 @@ public function hasReturnType() {} public function getReturnType() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null, bool $inheritance = false) {} + public function getAttributes(?string $name = null, int $flags = 0) {} } class ReflectionFunction extends ReflectionFunctionAbstract @@ -353,7 +353,7 @@ public function getNamespaceName() {} public function getShortName() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null, bool $inheritance = false) {} + public function getAttributes(?string $name = null, int $flags = 0) {} } class ReflectionObject extends ReflectionClass @@ -420,7 +420,7 @@ public function hasDefaultValue(): bool {} public function getDefaultValue() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null, bool $inheritance = false) {} + public function getAttributes(?string $name = null, int $flags = 0) {} } class ReflectionClassConstant implements Reflector @@ -456,7 +456,7 @@ public function getDeclaringClass() {} public function getDocComment() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null, bool $inheritance = false) {} + public function getAttributes(?string $name = null, int $flags = 0) {} } class ReflectionParameter implements Reflector @@ -525,7 +525,7 @@ public function getDefaultValueConstantName() {} public function isVariadic() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null, bool $inheritance = false) {} + public function getAttributes(?string $name = null, int $flags = 0) {} } abstract class ReflectionType implements Stringable diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 150b4ed2952e7..ce4e5d85ee8e5 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -59,7 +59,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getAttributes, 0, 0, 0) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) - ZEND_ARG_TYPE_INFO(0, inheritance, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunction___construct, 0, 0, 1) diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index 3bc4ad7399bf3..47a9e214fd5f8 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -112,7 +112,7 @@ Class [ class ReflectionClass implements Reflector, String - Parameters [2] { Parameter #0 [ ?string $name ] - Parameter #1 [ bool $inheritance ] + Parameter #1 [ int $flags ] } } From b6d392d93b46e795f2a2b70f486de7b35bfff5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Mon, 6 Apr 2020 08:36:10 +0200 Subject: [PATCH 30/37] Renamed const to IS_INSTANCE to be consistent with getMethods() filter. --- Zend/tests/attributes/filter.phpt | 12 ++++++------ ext/reflection/php_reflection.c | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Zend/tests/attributes/filter.phpt b/Zend/tests/attributes/filter.phpt index 713c982cef59b..78f5b9709f50b 100644 --- a/Zend/tests/attributes/filter.phpt +++ b/Zend/tests/attributes/filter.phpt @@ -26,27 +26,27 @@ class A2 implements Base { } class A3 extends A2 { } $ref = new \ReflectionFunction(<> <> <> function () { }); -$attr = $ref->getAttributes(\stdClass::class, \ReflectionAttribute::FILTER_INSTANCEOF); +$attr = $ref->getAttributes(\stdClass::class, \ReflectionAttribute::IS_INSTANCEOF); var_dump(count($attr)); print_r(array_map(fn ($a) => $a->getName(), $attr)); $ref = new \ReflectionFunction(<> <> function () { }); -$attr = $ref->getAttributes(A1::class, \ReflectionAttribute::FILTER_INSTANCEOF); +$attr = $ref->getAttributes(A1::class, \ReflectionAttribute::IS_INSTANCEOF); var_dump(count($attr)); print_r(array_map(fn ($a) => $a->getName(), $attr)); $ref = new \ReflectionFunction(<> <> function () { }); -$attr = $ref->getAttributes(Base::class, \ReflectionAttribute::FILTER_INSTANCEOF); +$attr = $ref->getAttributes(Base::class, \ReflectionAttribute::IS_INSTANCEOF); var_dump(count($attr)); print_r(array_map(fn ($a) => $a->getName(), $attr)); $ref = new \ReflectionFunction(<> <> <> function () { }); -$attr = $ref->getAttributes(A2::class, \ReflectionAttribute::FILTER_INSTANCEOF); +$attr = $ref->getAttributes(A2::class, \ReflectionAttribute::IS_INSTANCEOF); var_dump(count($attr)); print_r(array_map(fn ($a) => $a->getName(), $attr)); $ref = new \ReflectionFunction(<> <> <> function () { }); -$attr = $ref->getAttributes(Base::class, \ReflectionAttribute::FILTER_INSTANCEOF); +$attr = $ref->getAttributes(Base::class, \ReflectionAttribute::IS_INSTANCEOF); var_dump(count($attr)); print_r(array_map(fn ($a) => $a->getName(), $attr)); @@ -63,7 +63,7 @@ try { $ref = new \ReflectionFunction(function () { }); try { - $ref->getAttributes(SomeMissingClass::class, \ReflectionAttribute::FILTER_INSTANCEOF); + $ref->getAttributes(SomeMissingClass::class, \ReflectionAttribute::IS_INSTANCEOF); } catch (\Error $e) { var_dump('ERROR 2', $e->getMessage()); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 486709e84bcf0..0d04e535acb6f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -111,7 +111,7 @@ PHPAPI zend_class_entry *reflection_attribute_ptr; #define REGISTER_REFLECTION_CLASS_CONST_LONG(class_name, const_name, value) \ zend_declare_class_constant_long(reflection_ ## class_name ## _ptr, const_name, sizeof(const_name)-1, (zend_long)value); -#define REFLECTION_ATTRIBUTE_FILTER_INSTANCEOF (1 << 1) +#define REFLECTION_ATTRIBUTE_IS_INSTANCEOF (1 << 1) /* {{{ Object structure */ @@ -1224,12 +1224,12 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut RETURN_THROWS(); } - if (flags & ~REFLECTION_ATTRIBUTE_FILTER_INSTANCEOF) { + if (flags & ~REFLECTION_ATTRIBUTE_IS_INSTANCEOF) { zend_throw_error(NULL, "Invalid attribute filter flag specified"); RETURN_THROWS(); } - if (name && (flags & REFLECTION_ATTRIBUTE_FILTER_INSTANCEOF)) { + if (name && (flags & REFLECTION_ATTRIBUTE_IS_INSTANCEOF)) { if (NULL == (base = zend_lookup_class(name))) { if (!EG(exception)) { zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); @@ -7145,7 +7145,7 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ _reflection_entry.ce_flags |= ZEND_ACC_FINAL; reflection_attribute_ptr = zend_register_internal_class(&_reflection_entry); - REGISTER_REFLECTION_CLASS_CONST_LONG(attribute, "FILTER_INSTANCEOF", REFLECTION_ATTRIBUTE_FILTER_INSTANCEOF); + REGISTER_REFLECTION_CLASS_CONST_LONG(attribute, "IS_INSTANCEOF", REFLECTION_ATTRIBUTE_IS_INSTANCEOF); REFLECTION_G(key_initialized) = 0; From 205bd8ed0a08de72e1cdd35b03b0bb5a7b4b371e Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 13 Apr 2020 16:49:47 +0200 Subject: [PATCH 31/37] Add PhpCompilerAttribute and PhpAttribute classes. --- .../tests/attributes/compiler_attributes.phpt | 15 +++++++ Zend/tests/attributes/rfcexample.phpt | 40 +++++++++++++++++++ Zend/zend_default_classes.c | 23 +++++++++++ 3 files changed, 78 insertions(+) create mode 100644 Zend/tests/attributes/compiler_attributes.phpt create mode 100644 Zend/tests/attributes/rfcexample.phpt diff --git a/Zend/tests/attributes/compiler_attributes.phpt b/Zend/tests/attributes/compiler_attributes.phpt new file mode 100644 index 0000000000000..de4849ca19d9e --- /dev/null +++ b/Zend/tests/attributes/compiler_attributes.phpt @@ -0,0 +1,15 @@ +--TEST-- +attributes: Add PhpCompilerAttribute +--FILE-- +> +class Foo +{ +} + +$ref = new ReflectionClass(Foo::class); +var_dump($ref->getAttributes()[0]->getAsObject()); +--EXPECTF-- +object(PhpCompilerAttribute)#3 (0) { +} diff --git a/Zend/tests/attributes/rfcexample.phpt b/Zend/tests/attributes/rfcexample.phpt new file mode 100644 index 0000000000000..3c18785f7d526 --- /dev/null +++ b/Zend/tests/attributes/rfcexample.phpt @@ -0,0 +1,40 @@ +--TEST-- +Attributes: RFC Example +--FILE-- +> + class SingleArgument { + public $argumentValue; + + public function __construct($argumentValue) { + $this->argumentValue = $argumentValue; + } + } +} + +namespace { + use My\Attributes\SingleArgument; + + <> + class Foo { + } + + $reflectionClass = new \ReflectionClass(Foo::class); + $attributes = $reflectionClass->getAttributes(); + + var_dump($attributes[0]->getName()); + var_dump($attributes[0]->getArguments()); + var_dump($attributes[0]->getAsObject()); +} +--EXPECTF-- +string(28) "My\Attributes\SingleArgument" +array(1) { + [0]=> + string(11) "Hello World" +} +object(My\Attributes\SingleArgument)#3 (1) { + ["argumentValue"]=> + string(11) "Hello World" +} diff --git a/Zend/zend_default_classes.c b/Zend/zend_default_classes.c index 2c42763f0c5e5..22cbc86fb73eb 100644 --- a/Zend/zend_default_classes.c +++ b/Zend/zend_default_classes.c @@ -26,6 +26,28 @@ #include "zend_generators.h" #include "zend_weakrefs.h" +zend_class_entry *zend_ce_php_attribute; +zend_class_entry *zend_ce_php_compiler_attribute; + +static void zend_register_attribute_ce(void) +{ + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, "PhpAttribute", NULL); + zend_ce_php_attribute = zend_register_internal_class(&ce); + zend_ce_php_attribute->ce_flags |= ZEND_ACC_FINAL; + + //zend_hash_init_ex(zend_ce_php_attribute->attributes, 8, NULL, NULL, 1, 0); + //zend_hash_str_add_empty_element(zend_ce_php_attribute->attributes, "phpattribute", sizeof("phpattribute")-1); + + INIT_CLASS_ENTRY(ce, "PhpCompilerAttribute", NULL); + zend_ce_php_compiler_attribute = zend_register_internal_class(&ce); + zend_ce_php_compiler_attribute->ce_flags |= ZEND_ACC_FINAL; + + //zend_hash_init_ex(zend_ce_php_attribute->attributes, 8, NULL, NULL, 1, 0); + //zend_hash_str_add_empty_element(zend_ce_php_attribute->attributes, "phpattribute", sizeof("phpattribute")-1); +} + ZEND_API void zend_register_default_classes(void) { zend_register_interfaces(); @@ -34,4 +56,5 @@ ZEND_API void zend_register_default_classes(void) zend_register_closure_ce(); zend_register_generator_ce(); zend_register_weakref_ce(); + zend_register_attribute_ce(); } From 13e53adb96baf2002a740cc35594bc634464f404 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 13 Apr 2020 18:09:50 +0200 Subject: [PATCH 32/37] Add very simple compiler attribute validation for now (no constant ast evaluation). --- .../tests/attributes/compiler_attributes.phpt | 3 +-- Zend/zend_attributes.h | 16 ++++++++++++++ Zend/zend_compile.c | 11 ++++++++++ Zend/zend_default_classes.c | 22 ++++++++++++++----- 4 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 Zend/zend_attributes.h diff --git a/Zend/tests/attributes/compiler_attributes.phpt b/Zend/tests/attributes/compiler_attributes.phpt index de4849ca19d9e..437c292b67dee 100644 --- a/Zend/tests/attributes/compiler_attributes.phpt +++ b/Zend/tests/attributes/compiler_attributes.phpt @@ -11,5 +11,4 @@ class Foo $ref = new ReflectionClass(Foo::class); var_dump($ref->getAttributes()[0]->getAsObject()); --EXPECTF-- -object(PhpCompilerAttribute)#3 (0) { -} +Fatal error: The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead in %s diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h new file mode 100644 index 0000000000000..15f02a7544c57 --- /dev/null +++ b/Zend/zend_attributes.h @@ -0,0 +1,16 @@ +#ifndef ZEND_ATTRIBUTES_H +#define ZEND_ATTRIBUTES_H + +zend_class_entry *zend_ce_php_attribute; +zend_class_entry *zend_ce_php_compiler_attribute; + +typedef void (*zend_attributes_internal_validator)(zval *attribute); +HashTable zend_attributes_internal_validators; + +static void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator *validator) +{ + zend_string *attribute_name = zend_string_tolower_ex(ce->name, 1); + + zend_hash_update_mem(&zend_attributes_internal_validators, attribute_name, validator, sizeof(zend_attributes_internal_validator)); +} +#endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e48e68b67bb6b..3f5bb06727ba1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -20,6 +20,7 @@ #include #include "zend.h" +#include "zend_attributes.h" #include "zend_compile.h" #include "zend_constants.h" #include "zend_llist.h" @@ -5750,6 +5751,8 @@ static HashTable *zend_compile_attributes(zend_ast *ast) /* {{{ */ uint32_t i; zval tmp; + zend_attributes_internal_validator *validator = NULL; + zend_attributes_internal_validator cb; ZVAL_NULL(&tmp); @@ -5770,6 +5773,14 @@ static HashTable *zend_compile_attributes(zend_ast *ast) /* {{{ */ name = zend_string_tolower(Z_STR_P(zend_hash_index_find(Z_ARRVAL(a), 0))); x = zend_hash_find(attr, name); + // validate internal attribute + validator = (zend_attributes_internal_validator*)zend_hash_find_ptr(&zend_attributes_internal_validators, name); + + if (validator != NULL) { + cb = *validator; + cb(&a); + } + if (x) { ZEND_ASSERT(Z_TYPE_P(x) == IS_ARRAY); diff --git a/Zend/zend_default_classes.c b/Zend/zend_default_classes.c index 22cbc86fb73eb..70b0c905c98d6 100644 --- a/Zend/zend_default_classes.c +++ b/Zend/zend_default_classes.c @@ -19,6 +19,7 @@ #include "zend.h" #include "zend_API.h" +#include "zend_attributes.h" #include "zend_builtin_functions.h" #include "zend_interfaces.h" #include "zend_exceptions.h" @@ -26,26 +27,35 @@ #include "zend_generators.h" #include "zend_weakrefs.h" -zend_class_entry *zend_ce_php_attribute; -zend_class_entry *zend_ce_php_compiler_attribute; +void zend_attribute_validate_phpattribute(zval *attribute) +{ +} + +void zend_attribute_validate_phpcompilerattribute(zval *attribute) +{ + zend_error(E_COMPILE_ERROR, "The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead"); +} static void zend_register_attribute_ce(void) { + zend_hash_init(&zend_attributes_internal_validators, 8, NULL, NULL, 1); + zend_class_entry ce; + zend_attributes_internal_validator cb; INIT_CLASS_ENTRY(ce, "PhpAttribute", NULL); zend_ce_php_attribute = zend_register_internal_class(&ce); zend_ce_php_attribute->ce_flags |= ZEND_ACC_FINAL; - //zend_hash_init_ex(zend_ce_php_attribute->attributes, 8, NULL, NULL, 1, 0); - //zend_hash_str_add_empty_element(zend_ce_php_attribute->attributes, "phpattribute", sizeof("phpattribute")-1); + cb = zend_attribute_validate_phpattribute; + zend_compiler_attribute_register(zend_ce_php_attribute, &cb); INIT_CLASS_ENTRY(ce, "PhpCompilerAttribute", NULL); zend_ce_php_compiler_attribute = zend_register_internal_class(&ce); zend_ce_php_compiler_attribute->ce_flags |= ZEND_ACC_FINAL; - //zend_hash_init_ex(zend_ce_php_attribute->attributes, 8, NULL, NULL, 1, 0); - //zend_hash_str_add_empty_element(zend_ce_php_attribute->attributes, "phpattribute", sizeof("phpattribute")-1); + cb = zend_attribute_validate_phpcompilerattribute; + zend_compiler_attribute_register(zend_ce_php_compiler_attribute, &cb); } ZEND_API void zend_register_default_classes(void) From db7f5fce4eac6ba9e4881a8670bd40e58d772bbf Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 13 Apr 2020 18:26:08 +0200 Subject: [PATCH 33/37] Validate that class attributes are PhpAttribute or PhpCompilerAttribute. --- Zend/tests/attributes/rfcexample.phpt | 1 + ext/reflection/php_reflection.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Zend/tests/attributes/rfcexample.phpt b/Zend/tests/attributes/rfcexample.phpt index 3c18785f7d526..8251eee80af2b 100644 --- a/Zend/tests/attributes/rfcexample.phpt +++ b/Zend/tests/attributes/rfcexample.phpt @@ -3,6 +3,7 @@ Attributes: RFC Example --FILE-- > class SingleArgument { diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0d04e535acb6f..dce031c976451 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -32,6 +32,7 @@ #include "zend.h" #include "zend_API.h" #include "zend_ast.h" +#include "zend_attributes.h" #include "zend_exceptions.h" #include "zend_operators.h" #include "zend_constants.h" @@ -6673,6 +6674,20 @@ ZEND_METHOD(reflection_attribute, getAsObject) RETURN_THROWS(); } + zend_string *lower_name = zend_string_tolower_ex(ce->name, 1); + + if (ce->type == ZEND_USER_CLASS && ce->info.user.attributes && zend_hash_str_exists(ce->info.user.attributes, "phpattribute", sizeof("phpattribute")-1) == 0) { + zend_string_release(lower_name); + zend_throw_error(NULL, "Attempting to use class '%s' as attribute that does not have <>.", ZSTR_VAL(attr->name)); + RETURN_THROWS(); + } else if (ce->type == ZEND_INTERNAL_CLASS && zend_hash_exists(&zend_attributes_internal_validators, lower_name) == 0) { + zend_string_release(lower_name); + zend_throw_error(NULL, "Attempting to use internal class '%s' as attribute that does not have <>.", ZSTR_VAL(attr->name)); + RETURN_THROWS(); + } + + zend_string_release(lower_name); + count = zend_hash_num_elements(Z_ARRVAL(attr->arguments)); if (count) { From 0bb4d66f82b45685eb3f9ac3118a0cae14776fda Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 13 Apr 2020 19:21:24 +0200 Subject: [PATCH 34/37] Move code from zend_default_classes.c to zend_attributes.c --- Zend/zend_attributes.c | 41 +++++++++++++++++++++++++++++++++++++ Zend/zend_attributes.h | 8 ++------ Zend/zend_default_classes.c | 31 ---------------------------- configure.ac | 2 +- win32/build/config.w32 | 2 +- 5 files changed, 45 insertions(+), 39 deletions(-) create mode 100644 Zend/zend_attributes.c diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c new file mode 100644 index 0000000000000..f2f04ee4848d0 --- /dev/null +++ b/Zend/zend_attributes.c @@ -0,0 +1,41 @@ +#include "zend.h" +#include "zend_API.h" +#include "zend_attributes.h" + +void zend_attribute_validate_phpattribute(zval *attribute) +{ +} + +void zend_attribute_validate_phpcompilerattribute(zval *attribute) +{ + zend_error(E_COMPILE_ERROR, "The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead"); +} + +void zend_register_attribute_ce(void) +{ + zend_hash_init(&zend_attributes_internal_validators, 8, NULL, NULL, 1); + + zend_class_entry ce; + zend_attributes_internal_validator cb; + + INIT_CLASS_ENTRY(ce, "PhpAttribute", NULL); + zend_ce_php_attribute = zend_register_internal_class(&ce); + zend_ce_php_attribute->ce_flags |= ZEND_ACC_FINAL; + + cb = zend_attribute_validate_phpattribute; + zend_compiler_attribute_register(zend_ce_php_attribute, &cb); + + INIT_CLASS_ENTRY(ce, "PhpCompilerAttribute", NULL); + zend_ce_php_compiler_attribute = zend_register_internal_class(&ce); + zend_ce_php_compiler_attribute->ce_flags |= ZEND_ACC_FINAL; + + cb = zend_attribute_validate_phpcompilerattribute; + zend_compiler_attribute_register(zend_ce_php_compiler_attribute, &cb); +} + +void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator *validator) +{ + zend_string *attribute_name = zend_string_tolower_ex(ce->name, 1); + + zend_hash_update_mem(&zend_attributes_internal_validators, attribute_name, validator, sizeof(zend_attributes_internal_validator)); +} diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 15f02a7544c57..d47fc657f506b 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -7,10 +7,6 @@ zend_class_entry *zend_ce_php_compiler_attribute; typedef void (*zend_attributes_internal_validator)(zval *attribute); HashTable zend_attributes_internal_validators; -static void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator *validator) -{ - zend_string *attribute_name = zend_string_tolower_ex(ce->name, 1); - - zend_hash_update_mem(&zend_attributes_internal_validators, attribute_name, validator, sizeof(zend_attributes_internal_validator)); -} +void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator *validator); +void zend_register_attribute_ce(void); #endif diff --git a/Zend/zend_default_classes.c b/Zend/zend_default_classes.c index 70b0c905c98d6..63cdf66d580e8 100644 --- a/Zend/zend_default_classes.c +++ b/Zend/zend_default_classes.c @@ -27,37 +27,6 @@ #include "zend_generators.h" #include "zend_weakrefs.h" -void zend_attribute_validate_phpattribute(zval *attribute) -{ -} - -void zend_attribute_validate_phpcompilerattribute(zval *attribute) -{ - zend_error(E_COMPILE_ERROR, "The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead"); -} - -static void zend_register_attribute_ce(void) -{ - zend_hash_init(&zend_attributes_internal_validators, 8, NULL, NULL, 1); - - zend_class_entry ce; - zend_attributes_internal_validator cb; - - INIT_CLASS_ENTRY(ce, "PhpAttribute", NULL); - zend_ce_php_attribute = zend_register_internal_class(&ce); - zend_ce_php_attribute->ce_flags |= ZEND_ACC_FINAL; - - cb = zend_attribute_validate_phpattribute; - zend_compiler_attribute_register(zend_ce_php_attribute, &cb); - - INIT_CLASS_ENTRY(ce, "PhpCompilerAttribute", NULL); - zend_ce_php_compiler_attribute = zend_register_internal_class(&ce); - zend_ce_php_compiler_attribute->ce_flags |= ZEND_ACC_FINAL; - - cb = zend_attribute_validate_phpcompilerattribute; - zend_compiler_attribute_register(zend_ce_php_compiler_attribute, &cb); -} - ZEND_API void zend_register_default_classes(void) { zend_register_interfaces(); diff --git a/configure.ac b/configure.ac index 651fc9c194fb0..c5eb05dfe33ea 100644 --- a/configure.ac +++ b/configure.ac @@ -1459,7 +1459,7 @@ PHP_ADD_SOURCES(Zend, \ zend_execute_API.c zend_highlight.c zend_llist.c \ zend_vm_opcodes.c zend_opcode.c zend_operators.c zend_ptr_stack.c zend_stack.c \ zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \ - zend_list.c zend_builtin_functions.c \ + zend_list.c zend_builtin_functions.c zend_attributes.c \ zend_ini.c zend_sort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \ zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c \ zend_closures.c zend_weakrefs.c zend_float.c zend_string.c zend_signal.c zend_generators.c \ diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 62dbeb40d9b08..d90b757a798b5 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -230,7 +230,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_execute_API.c zend_highlight.c \ zend_llist.c zend_vm_opcodes.c zend_opcode.c zend_operators.c zend_ptr_stack.c \ zend_stack.c zend_variables.c zend.c zend_API.c zend_extensions.c \ - zend_hash.c zend_list.c zend_builtin_functions.c \ + zend_hash.c zend_list.c zend_builtin_functions.c zend_attributes.c \ zend_ini.c zend_sort.c zend_multibyte.c zend_ts_hash.c \ zend_stream.c zend_iterators.c zend_interfaces.c zend_objects.c \ zend_object_handlers.c zend_objects_API.c \ From 72aea5a10879bb3fe6260842395ca0e1686f9f66 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 13 Apr 2020 19:30:27 +0200 Subject: [PATCH 35/37] Allow internal compiler attribute validators to check for attributed target. --- Zend/tests/attributes/wrong_atttribution.phpt | 9 +++++++++ Zend/zend_attributes.c | 7 +++++-- Zend/zend_attributes.h | 10 +++++++++- Zend/zend_compile.c | 18 +++++++++++------- 4 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 Zend/tests/attributes/wrong_atttribution.phpt diff --git a/Zend/tests/attributes/wrong_atttribution.phpt b/Zend/tests/attributes/wrong_atttribution.phpt new file mode 100644 index 0000000000000..dcb0b6b51d0fe --- /dev/null +++ b/Zend/tests/attributes/wrong_atttribution.phpt @@ -0,0 +1,9 @@ +--TEST-- +Attributes: Prevent PhpAttribute on non classes +--FILE-- +> +function foo() {} +--EXPECTF-- +Fatal error: Only classes can be marked with <> in %s diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index f2f04ee4848d0..62a62eaae4059 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -2,11 +2,14 @@ #include "zend_API.h" #include "zend_attributes.h" -void zend_attribute_validate_phpattribute(zval *attribute) +void zend_attribute_validate_phpattribute(zval *attribute, int target) { + if (target != ZEND_ATTRIBUTE_TARGET_CLASS) { + zend_error(E_COMPILE_ERROR, "Only classes can be marked with <>"); + } } -void zend_attribute_validate_phpcompilerattribute(zval *attribute) +void zend_attribute_validate_phpcompilerattribute(zval *attribute, int target) { zend_error(E_COMPILE_ERROR, "The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead"); } diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index d47fc657f506b..2312e3c13ebf0 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -1,10 +1,18 @@ #ifndef ZEND_ATTRIBUTES_H #define ZEND_ATTRIBUTES_H +#define ZEND_ATTRIBUTE_TARGET_CLASS 1 +#define ZEND_ATTRIBUTE_TARGET_FUNCTION 2 +#define ZEND_ATTRIBUTE_TARGET_METHOD 4 +#define ZEND_ATTRIBUTE_TARGET_PROPERTY 8 +#define ZEND_ATTRIBUTE_TARGET_CLASS_CONST 16 +#define ZEND_ATTRIBUTE_TARGET_PARAMETER 32 +#define ZEND_ATTRIBUTE_TARGET_ALL 63 + zend_class_entry *zend_ce_php_attribute; zend_class_entry *zend_ce_php_compiler_attribute; -typedef void (*zend_attributes_internal_validator)(zval *attribute); +typedef void (*zend_attributes_internal_validator)(zval *attribute, int target); HashTable zend_attributes_internal_validators; void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator *validator); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 3f5bb06727ba1..00c0c99dbf601 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5743,7 +5743,7 @@ static void zend_compile_attribute(zval *v, zend_ast *ast) /* {{{ */ } /* }}} */ -static HashTable *zend_compile_attributes(zend_ast *ast) /* {{{ */ +static HashTable *zend_compile_attributes(zend_ast *ast, int target) /* {{{ */ { HashTable *attr; @@ -5778,7 +5778,7 @@ static HashTable *zend_compile_attributes(zend_ast *ast) /* {{{ */ if (validator != NULL) { cb = *validator; - cb(&a); + cb(&a, target); } if (x) { @@ -5921,7 +5921,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall zend_hash_init(op_array->attributes, 8, NULL, ZVAL_PTR_DTOR, 0); } - ZVAL_ARR(&attr, zend_compile_attributes(attributes_ast)); + ZVAL_ARR(&attr, zend_compile_attributes(attributes_ast, ZEND_ATTRIBUTE_TARGET_PARAMETER)); zend_hash_index_add(op_array->attributes, i, &attr); } @@ -6381,7 +6381,11 @@ 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_attributes(decl->attributes); + int target = ZEND_ATTRIBUTE_TARGET_FUNCTION; + if (is_method) { + target = ZEND_ATTRIBUTE_TARGET_METHOD; + } + op_array->attributes = zend_compile_attributes(decl->attributes, target); } if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) { op_array->fn_flags |= ZEND_ACC_CLOSURE; @@ -6555,7 +6559,7 @@ void zend_compile_prop_group(zend_ast *list) /* {{{ */ zend_ast *type_ast = list->child[0]; zend_ast *prop_ast = list->child[1]; - attributes = list->child[2] ? zend_compile_attributes(list->child[2]) : NULL; + attributes = list->child[2] ? zend_compile_attributes(list->child[2], ZEND_ATTRIBUTE_TARGET_PROPERTY) : NULL; zend_compile_prop_decl(prop_ast, type_ast, list->attr, attributes); @@ -6589,7 +6593,7 @@ void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */ return; } - attributes = attr_ast ? zend_compile_attributes(attr_ast) : NULL; + attributes = attr_ast ? zend_compile_attributes(attr_ast, ZEND_ATTRIBUTE_TARGET_CLASS_CONST) : NULL; for (i = 0; i < list->children; ++i) { zend_ast *const_ast = list->child[i]; @@ -6820,7 +6824,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_attributes(decl->attributes); + ce->info.user.attributes = zend_compile_attributes(decl->attributes, ZEND_ATTRIBUTE_TARGET_CLASS); } if (UNEXPECTED((decl->flags & ZEND_ACC_ANON_CLASS))) { From 2ef898af03ef51353087073aa622756c16ebb44d Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 14 Apr 2020 11:25:25 +0200 Subject: [PATCH 36/37] Rename tests for ordering of features from basic to complex --- Zend/tests/attributes/{placement.phpt => 001_placement.phpt} | 0 Zend/tests/attributes/{rfcexample.phpt => 002_rfcexample.phpt} | 0 Zend/tests/attributes/{ast-nodes.phpt => 003_ast_nodes.phpt} | 0 .../attributes/{name-resolution.phpt => 004_name_resolution.phpt} | 0 Zend/tests/attributes/{objects.phpt => 005_objects.phpt} | 0 Zend/tests/attributes/{filter.phpt => 006_filter.phpt} | 0 ...ompiler_attributes.phpt => 007_wrong_compiler_attributes.phpt} | 0 .../{wrong_atttribution.phpt => 008_wrong_attribution.phpt} | 0 .../{annotations.phpt => 009_doctrine_annotations_example.phpt} | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename Zend/tests/attributes/{placement.phpt => 001_placement.phpt} (100%) rename Zend/tests/attributes/{rfcexample.phpt => 002_rfcexample.phpt} (100%) rename Zend/tests/attributes/{ast-nodes.phpt => 003_ast_nodes.phpt} (100%) rename Zend/tests/attributes/{name-resolution.phpt => 004_name_resolution.phpt} (100%) rename Zend/tests/attributes/{objects.phpt => 005_objects.phpt} (100%) rename Zend/tests/attributes/{filter.phpt => 006_filter.phpt} (100%) rename Zend/tests/attributes/{compiler_attributes.phpt => 007_wrong_compiler_attributes.phpt} (100%) rename Zend/tests/attributes/{wrong_atttribution.phpt => 008_wrong_attribution.phpt} (100%) rename Zend/tests/attributes/{annotations.phpt => 009_doctrine_annotations_example.phpt} (100%) diff --git a/Zend/tests/attributes/placement.phpt b/Zend/tests/attributes/001_placement.phpt similarity index 100% rename from Zend/tests/attributes/placement.phpt rename to Zend/tests/attributes/001_placement.phpt diff --git a/Zend/tests/attributes/rfcexample.phpt b/Zend/tests/attributes/002_rfcexample.phpt similarity index 100% rename from Zend/tests/attributes/rfcexample.phpt rename to Zend/tests/attributes/002_rfcexample.phpt diff --git a/Zend/tests/attributes/ast-nodes.phpt b/Zend/tests/attributes/003_ast_nodes.phpt similarity index 100% rename from Zend/tests/attributes/ast-nodes.phpt rename to Zend/tests/attributes/003_ast_nodes.phpt diff --git a/Zend/tests/attributes/name-resolution.phpt b/Zend/tests/attributes/004_name_resolution.phpt similarity index 100% rename from Zend/tests/attributes/name-resolution.phpt rename to Zend/tests/attributes/004_name_resolution.phpt diff --git a/Zend/tests/attributes/objects.phpt b/Zend/tests/attributes/005_objects.phpt similarity index 100% rename from Zend/tests/attributes/objects.phpt rename to Zend/tests/attributes/005_objects.phpt diff --git a/Zend/tests/attributes/filter.phpt b/Zend/tests/attributes/006_filter.phpt similarity index 100% rename from Zend/tests/attributes/filter.phpt rename to Zend/tests/attributes/006_filter.phpt diff --git a/Zend/tests/attributes/compiler_attributes.phpt b/Zend/tests/attributes/007_wrong_compiler_attributes.phpt similarity index 100% rename from Zend/tests/attributes/compiler_attributes.phpt rename to Zend/tests/attributes/007_wrong_compiler_attributes.phpt diff --git a/Zend/tests/attributes/wrong_atttribution.phpt b/Zend/tests/attributes/008_wrong_attribution.phpt similarity index 100% rename from Zend/tests/attributes/wrong_atttribution.phpt rename to Zend/tests/attributes/008_wrong_attribution.phpt diff --git a/Zend/tests/attributes/annotations.phpt b/Zend/tests/attributes/009_doctrine_annotations_example.phpt similarity index 100% rename from Zend/tests/attributes/annotations.phpt rename to Zend/tests/attributes/009_doctrine_annotations_example.phpt From a5fc69f7e4f7e068aff134625100e1182dff50eb Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 15 Apr 2020 12:48:20 +0200 Subject: [PATCH 37/37] Simplify validator function pointer wiggling. --- Zend/zend_attributes.c | 13 ++++++------- Zend/zend_attributes.h | 2 +- Zend/zend_compile.c | 8 +++----- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 62a62eaae4059..842ac0384b663 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -19,26 +19,25 @@ void zend_register_attribute_ce(void) zend_hash_init(&zend_attributes_internal_validators, 8, NULL, NULL, 1); zend_class_entry ce; - zend_attributes_internal_validator cb; INIT_CLASS_ENTRY(ce, "PhpAttribute", NULL); zend_ce_php_attribute = zend_register_internal_class(&ce); zend_ce_php_attribute->ce_flags |= ZEND_ACC_FINAL; - cb = zend_attribute_validate_phpattribute; - zend_compiler_attribute_register(zend_ce_php_attribute, &cb); + zend_compiler_attribute_register(zend_ce_php_attribute, zend_attribute_validate_phpattribute); INIT_CLASS_ENTRY(ce, "PhpCompilerAttribute", NULL); zend_ce_php_compiler_attribute = zend_register_internal_class(&ce); zend_ce_php_compiler_attribute->ce_flags |= ZEND_ACC_FINAL; - cb = zend_attribute_validate_phpcompilerattribute; - zend_compiler_attribute_register(zend_ce_php_compiler_attribute, &cb); + zend_compiler_attribute_register(zend_ce_php_compiler_attribute, zend_attribute_validate_phpcompilerattribute); } -void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator *validator) +void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator) { zend_string *attribute_name = zend_string_tolower_ex(ce->name, 1); - zend_hash_update_mem(&zend_attributes_internal_validators, attribute_name, validator, sizeof(zend_attributes_internal_validator)); + zend_hash_update_ptr(&zend_attributes_internal_validators, attribute_name, validator); + + zend_string_release(attribute_name); } diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 2312e3c13ebf0..fdee54c32a8da 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -15,6 +15,6 @@ zend_class_entry *zend_ce_php_compiler_attribute; typedef void (*zend_attributes_internal_validator)(zval *attribute, int target); HashTable zend_attributes_internal_validators; -void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator *validator); +void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator); void zend_register_attribute_ce(void); #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 00c0c99dbf601..ed379cc79c279 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5751,8 +5751,7 @@ static HashTable *zend_compile_attributes(zend_ast *ast, int target) /* {{{ */ uint32_t i; zval tmp; - zend_attributes_internal_validator *validator = NULL; - zend_attributes_internal_validator cb; + zend_attributes_internal_validator validator = NULL; ZVAL_NULL(&tmp); @@ -5774,11 +5773,10 @@ static HashTable *zend_compile_attributes(zend_ast *ast, int target) /* {{{ */ x = zend_hash_find(attr, name); // validate internal attribute - validator = (zend_attributes_internal_validator*)zend_hash_find_ptr(&zend_attributes_internal_validators, name); + validator = (zend_attributes_internal_validator)zend_hash_find_ptr(&zend_attributes_internal_validators, name); if (validator != NULL) { - cb = *validator; - cb(&a, target); + validator(&a, target); } if (x) {