Skip to content

Commit 27b4453

Browse files
authored
Merge pull request #6 from beberlei/AttributesValidator
Add PhpAttribute, PhpCompilerAttribute and simple validator
2 parents 5d06c35 + 72aea5a commit 27b4453

File tree

10 files changed

+168
-8
lines changed

10 files changed

+168
-8
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
attributes: Add PhpCompilerAttribute
3+
--FILE--
4+
<?php
5+
6+
<<PhpCompilerAttribute>>
7+
class Foo
8+
{
9+
}
10+
11+
$ref = new ReflectionClass(Foo::class);
12+
var_dump($ref->getAttributes()[0]->getAsObject());
13+
--EXPECTF--
14+
Fatal error: The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead in %s

Zend/tests/attributes/rfcexample.phpt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
Attributes: RFC Example
3+
--FILE--
4+
<?php
5+
namespace My\Attributes {
6+
use PhpAttribute;
7+
8+
<<PhpAttribute>>
9+
class SingleArgument {
10+
public $argumentValue;
11+
12+
public function __construct($argumentValue) {
13+
$this->argumentValue = $argumentValue;
14+
}
15+
}
16+
}
17+
18+
namespace {
19+
use My\Attributes\SingleArgument;
20+
21+
<<SingleArgument("Hello World")>>
22+
class Foo {
23+
}
24+
25+
$reflectionClass = new \ReflectionClass(Foo::class);
26+
$attributes = $reflectionClass->getAttributes();
27+
28+
var_dump($attributes[0]->getName());
29+
var_dump($attributes[0]->getArguments());
30+
var_dump($attributes[0]->getAsObject());
31+
}
32+
--EXPECTF--
33+
string(28) "My\Attributes\SingleArgument"
34+
array(1) {
35+
[0]=>
36+
string(11) "Hello World"
37+
}
38+
object(My\Attributes\SingleArgument)#3 (1) {
39+
["argumentValue"]=>
40+
string(11) "Hello World"
41+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
Attributes: Prevent PhpAttribute on non classes
3+
--FILE--
4+
<?php
5+
6+
<<PhpAttribute>>
7+
function foo() {}
8+
--EXPECTF--
9+
Fatal error: Only classes can be marked with <<PhpAttribute>> in %s

Zend/zend_attributes.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include "zend.h"
2+
#include "zend_API.h"
3+
#include "zend_attributes.h"
4+
5+
void zend_attribute_validate_phpattribute(zval *attribute, int target)
6+
{
7+
if (target != ZEND_ATTRIBUTE_TARGET_CLASS) {
8+
zend_error(E_COMPILE_ERROR, "Only classes can be marked with <<PhpAttribute>>");
9+
}
10+
}
11+
12+
void zend_attribute_validate_phpcompilerattribute(zval *attribute, int target)
13+
{
14+
zend_error(E_COMPILE_ERROR, "The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead");
15+
}
16+
17+
void zend_register_attribute_ce(void)
18+
{
19+
zend_hash_init(&zend_attributes_internal_validators, 8, NULL, NULL, 1);
20+
21+
zend_class_entry ce;
22+
zend_attributes_internal_validator cb;
23+
24+
INIT_CLASS_ENTRY(ce, "PhpAttribute", NULL);
25+
zend_ce_php_attribute = zend_register_internal_class(&ce);
26+
zend_ce_php_attribute->ce_flags |= ZEND_ACC_FINAL;
27+
28+
cb = zend_attribute_validate_phpattribute;
29+
zend_compiler_attribute_register(zend_ce_php_attribute, &cb);
30+
31+
INIT_CLASS_ENTRY(ce, "PhpCompilerAttribute", NULL);
32+
zend_ce_php_compiler_attribute = zend_register_internal_class(&ce);
33+
zend_ce_php_compiler_attribute->ce_flags |= ZEND_ACC_FINAL;
34+
35+
cb = zend_attribute_validate_phpcompilerattribute;
36+
zend_compiler_attribute_register(zend_ce_php_compiler_attribute, &cb);
37+
}
38+
39+
void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator *validator)
40+
{
41+
zend_string *attribute_name = zend_string_tolower_ex(ce->name, 1);
42+
43+
zend_hash_update_mem(&zend_attributes_internal_validators, attribute_name, validator, sizeof(zend_attributes_internal_validator));
44+
}

Zend/zend_attributes.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef ZEND_ATTRIBUTES_H
2+
#define ZEND_ATTRIBUTES_H
3+
4+
#define ZEND_ATTRIBUTE_TARGET_CLASS 1
5+
#define ZEND_ATTRIBUTE_TARGET_FUNCTION 2
6+
#define ZEND_ATTRIBUTE_TARGET_METHOD 4
7+
#define ZEND_ATTRIBUTE_TARGET_PROPERTY 8
8+
#define ZEND_ATTRIBUTE_TARGET_CLASS_CONST 16
9+
#define ZEND_ATTRIBUTE_TARGET_PARAMETER 32
10+
#define ZEND_ATTRIBUTE_TARGET_ALL 63
11+
12+
zend_class_entry *zend_ce_php_attribute;
13+
zend_class_entry *zend_ce_php_compiler_attribute;
14+
15+
typedef void (*zend_attributes_internal_validator)(zval *attribute, int target);
16+
HashTable zend_attributes_internal_validators;
17+
18+
void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator *validator);
19+
void zend_register_attribute_ce(void);
20+
#endif

Zend/zend_compile.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <zend_language_parser.h>
2222
#include "zend.h"
23+
#include "zend_attributes.h"
2324
#include "zend_compile.h"
2425
#include "zend_constants.h"
2526
#include "zend_llist.h"
@@ -5742,14 +5743,16 @@ static void zend_compile_attribute(zval *v, zend_ast *ast) /* {{{ */
57425743
}
57435744
/* }}} */
57445745

5745-
static HashTable *zend_compile_attributes(zend_ast *ast) /* {{{ */
5746+
static HashTable *zend_compile_attributes(zend_ast *ast, int target) /* {{{ */
57465747
{
57475748
HashTable *attr;
57485749

57495750
zend_ast_list *list;
57505751
uint32_t i;
57515752

57525753
zval tmp;
5754+
zend_attributes_internal_validator *validator = NULL;
5755+
zend_attributes_internal_validator cb;
57535756

57545757
ZVAL_NULL(&tmp);
57555758

@@ -5770,6 +5773,14 @@ static HashTable *zend_compile_attributes(zend_ast *ast) /* {{{ */
57705773
name = zend_string_tolower(Z_STR_P(zend_hash_index_find(Z_ARRVAL(a), 0)));
57715774
x = zend_hash_find(attr, name);
57725775

5776+
// validate internal attribute
5777+
validator = (zend_attributes_internal_validator*)zend_hash_find_ptr(&zend_attributes_internal_validators, name);
5778+
5779+
if (validator != NULL) {
5780+
cb = *validator;
5781+
cb(&a, target);
5782+
}
5783+
57735784
if (x) {
57745785
ZEND_ASSERT(Z_TYPE_P(x) == IS_ARRAY);
57755786

@@ -5910,7 +5921,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
59105921
zend_hash_init(op_array->attributes, 8, NULL, ZVAL_PTR_DTOR, 0);
59115922
}
59125923

5913-
ZVAL_ARR(&attr, zend_compile_attributes(attributes_ast));
5924+
ZVAL_ARR(&attr, zend_compile_attributes(attributes_ast, ZEND_ATTRIBUTE_TARGET_PARAMETER));
59145925
zend_hash_index_add(op_array->attributes, i, &attr);
59155926
}
59165927

@@ -6370,7 +6381,11 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /*
63706381
op_array->doc_comment = zend_string_copy(decl->doc_comment);
63716382
}
63726383
if (decl->attributes) {
6373-
op_array->attributes = zend_compile_attributes(decl->attributes);
6384+
int target = ZEND_ATTRIBUTE_TARGET_FUNCTION;
6385+
if (is_method) {
6386+
target = ZEND_ATTRIBUTE_TARGET_METHOD;
6387+
}
6388+
op_array->attributes = zend_compile_attributes(decl->attributes, target);
63746389
}
63756390
if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) {
63766391
op_array->fn_flags |= ZEND_ACC_CLOSURE;
@@ -6544,7 +6559,7 @@ void zend_compile_prop_group(zend_ast *list) /* {{{ */
65446559
zend_ast *type_ast = list->child[0];
65456560
zend_ast *prop_ast = list->child[1];
65466561

6547-
attributes = list->child[2] ? zend_compile_attributes(list->child[2]) : NULL;
6562+
attributes = list->child[2] ? zend_compile_attributes(list->child[2], ZEND_ATTRIBUTE_TARGET_PROPERTY) : NULL;
65486563

65496564
zend_compile_prop_decl(prop_ast, type_ast, list->attr, attributes);
65506565

@@ -6578,7 +6593,7 @@ void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */
65786593
return;
65796594
}
65806595

6581-
attributes = attr_ast ? zend_compile_attributes(attr_ast) : NULL;
6596+
attributes = attr_ast ? zend_compile_attributes(attr_ast, ZEND_ATTRIBUTE_TARGET_CLASS_CONST) : NULL;
65826597

65836598
for (i = 0; i < list->children; ++i) {
65846599
zend_ast *const_ast = list->child[i];
@@ -6809,7 +6824,7 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
68096824
ce->info.user.doc_comment = zend_string_copy(decl->doc_comment);
68106825
}
68116826
if (decl->attributes) {
6812-
ce->info.user.attributes = zend_compile_attributes(decl->attributes);
6827+
ce->info.user.attributes = zend_compile_attributes(decl->attributes, ZEND_ATTRIBUTE_TARGET_CLASS);
68136828
}
68146829

68156830
if (UNEXPECTED((decl->flags & ZEND_ACC_ANON_CLASS))) {

Zend/zend_default_classes.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "zend.h"
2121
#include "zend_API.h"
22+
#include "zend_attributes.h"
2223
#include "zend_builtin_functions.h"
2324
#include "zend_interfaces.h"
2425
#include "zend_exceptions.h"
@@ -34,4 +35,5 @@ ZEND_API void zend_register_default_classes(void)
3435
zend_register_closure_ce();
3536
zend_register_generator_ce();
3637
zend_register_weakref_ce();
38+
zend_register_attribute_ce();
3739
}

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1459,7 +1459,7 @@ PHP_ADD_SOURCES(Zend, \
14591459
zend_execute_API.c zend_highlight.c zend_llist.c \
14601460
zend_vm_opcodes.c zend_opcode.c zend_operators.c zend_ptr_stack.c zend_stack.c \
14611461
zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \
1462-
zend_list.c zend_builtin_functions.c \
1462+
zend_list.c zend_builtin_functions.c zend_attributes.c \
14631463
zend_ini.c zend_sort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \
14641464
zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c \
14651465
zend_closures.c zend_weakrefs.c zend_float.c zend_string.c zend_signal.c zend_generators.c \

ext/reflection/php_reflection.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "zend.h"
3333
#include "zend_API.h"
3434
#include "zend_ast.h"
35+
#include "zend_attributes.h"
3536
#include "zend_exceptions.h"
3637
#include "zend_operators.h"
3738
#include "zend_constants.h"
@@ -6673,6 +6674,20 @@ ZEND_METHOD(reflection_attribute, getAsObject)
66736674
RETURN_THROWS();
66746675
}
66756676

6677+
zend_string *lower_name = zend_string_tolower_ex(ce->name, 1);
6678+
6679+
if (ce->type == ZEND_USER_CLASS && ce->info.user.attributes && zend_hash_str_exists(ce->info.user.attributes, "phpattribute", sizeof("phpattribute")-1) == 0) {
6680+
zend_string_release(lower_name);
6681+
zend_throw_error(NULL, "Attempting to use class '%s' as attribute that does not have <<PhpAttribute>>.", ZSTR_VAL(attr->name));
6682+
RETURN_THROWS();
6683+
} else if (ce->type == ZEND_INTERNAL_CLASS && zend_hash_exists(&zend_attributes_internal_validators, lower_name) == 0) {
6684+
zend_string_release(lower_name);
6685+
zend_throw_error(NULL, "Attempting to use internal class '%s' as attribute that does not have <<PhpCompilerAttribute>>.", ZSTR_VAL(attr->name));
6686+
RETURN_THROWS();
6687+
}
6688+
6689+
zend_string_release(lower_name);
6690+
66766691
count = zend_hash_num_elements(Z_ARRVAL(attr->arguments));
66776692

66786693
if (count) {

win32/build/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \
230230
zend_execute_API.c zend_highlight.c \
231231
zend_llist.c zend_vm_opcodes.c zend_opcode.c zend_operators.c zend_ptr_stack.c \
232232
zend_stack.c zend_variables.c zend.c zend_API.c zend_extensions.c \
233-
zend_hash.c zend_list.c zend_builtin_functions.c \
233+
zend_hash.c zend_list.c zend_builtin_functions.c zend_attributes.c \
234234
zend_ini.c zend_sort.c zend_multibyte.c zend_ts_hash.c \
235235
zend_stream.c zend_iterators.c zend_interfaces.c zend_objects.c \
236236
zend_object_handlers.c zend_objects_API.c \

0 commit comments

Comments
 (0)