diff --git a/Zend/tests/attributes/007_self_reflect_attribute.phpt b/Zend/tests/attributes/007_self_reflect_attribute.phpt new file mode 100644 index 0000000000000..ae19665dcb628 --- /dev/null +++ b/Zend/tests/attributes/007_self_reflect_attribute.phpt @@ -0,0 +1,19 @@ +--TEST-- +Attributes: attributes on PhpAttribute return itself +--FILE-- +getAttributes(); + +foreach ($attributes as $attribute) { + var_dump($attribute->getName()); + var_dump($attribute->getArguments()); + var_dump($attribute->newInstance()); +} +--EXPECTF-- +string(12) "PhpAttribute" +array(0) { +} +object(PhpAttribute)#3 (0) { +} diff --git a/Zend/tests/attributes/007_wrong_compiler_attributes.phpt b/Zend/tests/attributes/007_wrong_compiler_attributes.phpt deleted file mode 100644 index 2be4271c10e84..0000000000000 --- a/Zend/tests/attributes/007_wrong_compiler_attributes.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -attributes: Add PhpCompilerAttribute ---FILE-- -> -class Foo -{ -} - -$ref = new ReflectionClass(Foo::class); -var_dump($ref->getAttributes()[0]->newInstance()); ---EXPECTF-- -Fatal error: The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead in %s diff --git a/Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt b/Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt new file mode 100644 index 0000000000000..c96b49cbd7fc1 --- /dev/null +++ b/Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt @@ -0,0 +1,15 @@ +--TEST-- +Attributes: Compiler Attributes can check for target declarations +--SKIPIF-- +> +function foo() { +} +--EXPECTF-- +Fatal error: Only classes can be marked with <> in %s diff --git a/Zend/zend.h b/Zend/zend.h index 52a8ebcd15d9c..48908e1530551 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -170,6 +170,7 @@ struct _zend_class_entry { zend_class_name *trait_names; zend_trait_alias **trait_aliases; zend_trait_precedence **trait_precedences; + HashTable *attributes; union { struct { @@ -177,7 +178,6 @@ 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 b0a5570623708..d58fb7633a7e1 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2583,6 +2583,7 @@ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class zend_initialize_class_data(class_entry, 0); class_entry->ce_flags = ce_flags | ZEND_ACC_CONSTANTS_UPDATED | ZEND_ACC_LINKED | ZEND_ACC_RESOLVED_PARENT | ZEND_ACC_RESOLVED_INTERFACES; class_entry->info.internal.module = EG(current_module); + class_entry->attributes = NULL; if (class_entry->info.internal.builtin_functions) { zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type); diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index ee93a74a83f2d..cf4481e44e857 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -3,7 +3,6 @@ #include "zend_attributes.h" ZEND_API zend_class_entry *zend_ce_php_attribute; -ZEND_API zend_class_entry *zend_ce_php_compiler_attribute; static HashTable internal_validators; @@ -14,11 +13,6 @@ void zend_attribute_validate_phpattribute(zend_attribute *attr, int target) } } -void zend_attribute_validate_phpcompilerattribute(zend_attribute *attr, int target) -{ - zend_error(E_COMPILE_ERROR, "The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead"); -} - ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname) { return zend_hash_find_ptr(&internal_validators, lcname); @@ -90,12 +84,38 @@ ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, return get_attribute_str(attributes, str, len, offset + 1); } +static void attribute_ptr_dtor(zval *v) /* {{{ */ +{ + zend_attribute_free((zend_attribute *) Z_PTR_P(v)); +} +/* }}} */ + ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator) { + if (ce->type != ZEND_INTERNAL_CLASS) { + zend_error_noreturn(E_ERROR, "Only internal classes can be registered as compiler attribute"); + } + zend_string *lcname = zend_string_tolower_ex(ce->name, 1); + zval tmp; zend_hash_update_ptr(&internal_validators, lcname, validator); zend_string_release(lcname); + + if (ce->attributes == NULL) { + ce->attributes = pemalloc(sizeof(HashTable), 1); + zend_hash_init(ce->attributes, 8, NULL, attribute_ptr_dtor, 1); + } + + zend_attribute *attr = pemalloc(ZEND_ATTRIBUTE_SIZE(0), 1); + + attr->name = zend_string_copy(zend_ce_php_attribute->name); + attr->lcname = zend_string_tolower_ex(attr->name, 1); + attr->offset = 0; + attr->argc = 0; + + ZVAL_PTR(&tmp, attr); + zend_hash_next_index_insert(ce->attributes, &tmp); } void zend_register_attribute_ce(void) @@ -109,10 +129,4 @@ void zend_register_attribute_ce(void) zend_ce_php_attribute->ce_flags |= ZEND_ACC_FINAL; 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; - - zend_compiler_attribute_register(zend_ce_php_compiler_attribute, zend_attribute_validate_phpcompilerattribute); } diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 62a132681efbb..979e2682b1d34 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -14,7 +14,6 @@ BEGIN_EXTERN_C() extern ZEND_API zend_class_entry *zend_ce_php_attribute; -extern ZEND_API zend_class_entry *zend_ce_php_compiler_attribute; typedef struct _zend_attribute { zend_string *name; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index bdfea39fe9f69..7e672abde29a5 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1813,7 +1813,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->attributes = NULL; } ce->default_properties_count = 0; @@ -6800,8 +6800,8 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) / ce->info.user.doc_comment = zend_string_copy(decl->doc_comment); } if (decl->attributes) { - ce->info.user.attributes = create_attribute_array(); - zend_compile_attributes(ce->info.user.attributes, decl->attributes, 0, ZEND_ATTRIBUTE_TARGET_CLASS); + ce->attributes = create_attribute_array(); + zend_compile_attributes(ce->attributes, decl->attributes, 0, ZEND_ATTRIBUTE_TARGET_CLASS); } if (UNEXPECTED((decl->flags & ZEND_ACC_ANON_CLASS))) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index aa6af25360bc1..4d47d0da5debc 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -406,6 +406,7 @@ struct _zend_op_array { uint32_t num_args; uint32_t required_num_args; zend_arg_info *arg_info; + HashTable *attributes; /* END of common elements */ int cache_size; /* number of run_time_cache_slots * sizeof(void*) */ @@ -430,7 +431,6 @@ struct _zend_op_array { uint32_t line_start; uint32_t line_end; zend_string *doc_comment; - HashTable *attributes; int last_literal; zval *literals; @@ -456,6 +456,7 @@ typedef struct _zend_internal_function { uint32_t num_args; uint32_t required_num_args; zend_internal_arg_info *arg_info; + HashTable *attributes; /* END of common elements */ zif_handler handler; @@ -479,6 +480,7 @@ union _zend_function { uint32_t num_args; uint32_t required_num_args; zend_arg_info *arg_info; /* index -1 represents the return value info, if any */ + HashTable *attributes; } common; zend_op_array op_array; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 3a4b9a7f66f30..5c71d884474b8 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -357,8 +357,8 @@ 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_release(ce->info.user.attributes); + if (ce->attributes) { + zend_array_release(ce->attributes); } if (ce->num_traits > 0) { diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 8e6dd30c93672..74aeab2391646 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -726,7 +726,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); - SERIALIZE_ATTRIBUTES(ce->info.user.attributes); + SERIALIZE_ATTRIBUTES(ce->attributes); zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info); if (ce->properties_info_table) { @@ -1465,7 +1465,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); - UNSERIALIZE_ATTRIBUTES(ce->info.user.attributes); + UNSERIALIZE_ATTRIBUTES(ce->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 d8f063b5b2c13..080b2bd763d6f 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -866,8 +866,8 @@ static void zend_persist_class_entry(zval *zv) ce->info.user.doc_comment = NULL; } } - if (ce->info.user.attributes) { - ce->info.user.attributes = zend_persist_attributes(ce->info.user.attributes); + if (ce->attributes) { + ce->attributes = zend_persist_attributes(ce->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 9821aca4d5f98..6e44bf864224c 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -457,8 +457,8 @@ 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) { - zend_persist_attributes_calc(ce->info.user.attributes); + if (ce->attributes) { + zend_persist_attributes_calc(ce->attributes); } zend_hash_persist_calc(&ce->properties_info); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index cf6972d9542d2..6303fb6017b77 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1092,6 +1092,8 @@ static void reflection_attribute_factory(zval *object, zend_attribute *data, zen static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope, uint32_t offset, zend_string *name, zend_class_entry *base) /* {{{ */ { + ZEND_ASSERT(attributes != NULL); + zend_attribute *attr; zval tmp; @@ -4176,17 +4178,9 @@ ZEND_METHOD(ReflectionClass, getAttributes) reflection_object *intern; zend_class_entry *ce; - HashTable *attributes = NULL; - zend_class_entry *scope = NULL; - GET_REFLECTION_OBJECT_PTR(ce); - if (ce->type == ZEND_USER_CLASS && ce->info.user.attributes) { - attributes = ce->info.user.attributes; - scope = ce; - } - - reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, attributes, 0, scope); + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, ce->attributes, 0, ce); } /* }}} */ @@ -6582,12 +6576,9 @@ ZEND_METHOD(ReflectionAttribute, newInstance) RETURN_THROWS(); } - if (ce->type == ZEND_USER_CLASS && !zend_get_attribute_str(ce->info.user.attributes, ZEND_STRL("phpattribute"))) { + if (!zend_get_attribute_str(ce->attributes, ZEND_STRL("phpattribute"))) { zend_throw_error(NULL, "Attempting to use class '%s' as attribute that does not have <>.", ZSTR_VAL(attr->data->name)); RETURN_THROWS(); - } else if (ce->type == ZEND_INTERNAL_CLASS && !zend_attribute_get_validator(attr->data->lcname)) { - zend_throw_error(NULL, "Attempting to use internal class '%s' as attribute that does not have <>.", ZSTR_VAL(attr->data->name)); - RETURN_THROWS(); } if (SUCCESS != object_init_ex(&obj, ce)) { @@ -6623,7 +6614,6 @@ ZEND_METHOD(ReflectionAttribute, newInstance) RETURN_ZVAL(&obj, 1, 1); } /* }}} */ - static const zend_function_entry reflection_ext_functions[] = { /* {{{ */ PHP_FE_END }; /* }}} */ diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 88e8109e7ac7f..726cc14c041a3 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -23,11 +23,13 @@ #include "ext/standard/info.h" #include "php_test.h" #include "test_arginfo.h" +#include "zend_attributes.h" static zend_class_entry *zend_test_interface; static zend_class_entry *zend_test_class; static zend_class_entry *zend_test_child_class; static zend_class_entry *zend_test_trait; +static zend_class_entry *zend_test_attribute; static zend_object_handlers zend_test_class_handlers; ZEND_FUNCTION(zend_test_func) @@ -181,6 +183,13 @@ static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, ze } /* }}} */ +void zend_attribute_validate_zendtestattribute(zend_attribute *attr, int target) +{ + if (target != ZEND_ATTRIBUTE_TARGET_CLASS) { + zend_error(E_COMPILE_ERROR, "Only classes can be marked with <>"); + } +} + ZEND_METHOD(_ZendTestClass, __toString) /* {{{ */ { RETURN_EMPTY_STRING(); } @@ -272,6 +281,12 @@ PHP_MINIT_FUNCTION(zend_test) zend_register_class_alias("_ZendTestClassAlias", zend_test_class); REGISTER_LONG_CONSTANT("ZEND_TEST_DEPRECATED", 42, CONST_PERSISTENT | CONST_DEPRECATED); + + INIT_CLASS_ENTRY(class_entry, "ZendTestAttribute", NULL); + zend_test_attribute = zend_register_internal_class(&class_entry); + zend_test_attribute->ce_flags |= ZEND_ACC_FINAL; + + zend_compiler_attribute_register(zend_test_attribute, zend_attribute_validate_zendtestattribute); return SUCCESS; }