Skip to content

Unify PhpCompilerAttribute + PhpAttribute; allow attributes on internal classes + fns #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Zend/tests/attributes/007_self_reflect_attribute.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
Attributes: attributes on PhpAttribute return itself
--FILE--
<?php

$reflection = new \ReflectionClass(PhpAttribute::class);
$attributes = $reflection->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) {
}
14 changes: 0 additions & 14 deletions Zend/tests/attributes/007_wrong_compiler_attributes.phpt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Attributes: Compiler Attributes can check for target declarations
--SKIPIF--
<?php
if (!extension_loaded('zend-test')) {
echo "skip requires zend-test extension\n";
}
--FILE--
<?php

<<ZendTestAttribute>>
function foo() {
}
--EXPECTF--
Fatal error: Only classes can be marked with <<ZendTestAttribute>> in %s
2 changes: 1 addition & 1 deletion Zend/zend.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,14 @@ struct _zend_class_entry {
zend_class_name *trait_names;
zend_trait_alias **trait_aliases;
zend_trait_precedence **trait_precedences;
HashTable *attributes;

union {
struct {
zend_string *filename;
uint32_t line_start;
uint32_t line_end;
zend_string *doc_comment;
HashTable *attributes;
} user;
struct {
const struct _zend_function_entry *builtin_functions;
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
38 changes: 26 additions & 12 deletions Zend/zend_attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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);
}
1 change: 0 additions & 1 deletion Zend/zend_attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))) {
Expand Down
4 changes: 3 additions & 1 deletion Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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*) */
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions ext/opcache/zend_file_cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions ext/opcache/zend_persist.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions ext/opcache/zend_persist_calc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 4 additions & 14 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}
/* }}} */

Expand Down Expand Up @@ -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 <<PhpAttribute>>.", 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 <<PhpCompilerAttribute>>.", ZSTR_VAL(attr->data->name));
RETURN_THROWS();
}

if (SUCCESS != object_init_ex(&obj, ce)) {
Expand Down Expand Up @@ -6623,7 +6614,6 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
RETURN_ZVAL(&obj, 1, 1);
}
/* }}} */

static const zend_function_entry reflection_ext_functions[] = { /* {{{ */
PHP_FE_END
}; /* }}} */
Expand Down
15 changes: 15 additions & 0 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 <<ZendTestAttribute>>");
}
}

ZEND_METHOD(_ZendTestClass, __toString) /* {{{ */ {
RETURN_EMPTY_STRING();
}
Expand Down Expand Up @@ -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;
}

Expand Down