Skip to content

Commit ed99062

Browse files
committed
Implemented ReflectionAttribute::getAsObject(). Moved tests to dir.
1 parent ffaacd3 commit ed99062

File tree

7 files changed

+233
-18
lines changed

7 files changed

+233
-18
lines changed
File renamed without changes.

Zend/tests/attributes/objects.phpt

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
--TEST--
2+
Attributes can be converted into objects.
3+
--FILE--
4+
<?php
5+
6+
class A1
7+
{
8+
public string $name;
9+
public int $ttl;
10+
11+
public function __construct(string $name, int $ttl = 50)
12+
{
13+
$this->name = $name;
14+
$this->ttl = $ttl;
15+
}
16+
}
17+
18+
$ref = new \ReflectionFunction(<<A1('test')>> function () { });
19+
20+
foreach ($ref->getAttributes() as $attr) {
21+
$obj = $attr->getAsObject();
22+
23+
var_dump(get_class($obj), $obj->name, $obj->ttl);
24+
}
25+
26+
echo "\n";
27+
28+
$ref = new \ReflectionFunction(<<A1>> function () { });
29+
30+
try {
31+
$ref->getAttributes()[0]->getAsObject();
32+
} catch (\ArgumentCountError $e) {
33+
var_dump('ERROR 1', $e->getMessage());
34+
}
35+
36+
echo "\n";
37+
38+
$ref = new \ReflectionFunction(<<A1([])>> function () { });
39+
40+
try {
41+
$ref->getAttributes()[0]->getAsObject();
42+
} catch (\TypeError $e) {
43+
var_dump('ERROR 2', $e->getMessage());
44+
}
45+
46+
echo "\n";
47+
48+
$ref = new \ReflectionFunction(<<A2>> function () { });
49+
50+
try {
51+
$ref->getAttributes()[0]->getAsObject();
52+
} catch (\Error $e) {
53+
var_dump('ERROR 3', $e->getMessage());
54+
}
55+
56+
echo "\n";
57+
58+
class A3
59+
{
60+
private function __construct() { }
61+
}
62+
63+
$ref = new \ReflectionFunction(<<A3>> function () { });
64+
65+
try {
66+
$ref->getAttributes()[0]->getAsObject();
67+
} catch (\Error $e) {
68+
var_dump('ERROR 4', $e->getMessage());
69+
}
70+
71+
echo "\n";
72+
73+
class A4 { }
74+
75+
$ref = new \ReflectionFunction(<<A4(1)>> function () { });
76+
77+
try {
78+
$ref->getAttributes()[0]->getAsObject();
79+
} catch (\Error $e) {
80+
var_dump('ERROR 5', $e->getMessage());
81+
}
82+
83+
?>
84+
--EXPECT--
85+
string(2) "A1"
86+
string(4) "test"
87+
int(50)
88+
89+
string(7) "ERROR 1"
90+
string(81) "Too few arguments to function A1::__construct(), 0 passed and at least 1 expected"
91+
92+
string(7) "ERROR 2"
93+
string(74) "A1::__construct(): Argument #1 ($name) must be of type string, array given"
94+
95+
string(7) "ERROR 3"
96+
string(30) "Attribute class 'A2' not found"
97+
98+
string(7) "ERROR 4"
99+
string(50) "Attribute constructor of class 'A3' must be public"
100+
101+
string(7) "ERROR 5"
102+
string(72) "Annotation class 'A4' does not have a constructor, cannot pass arguments"
File renamed without changes.

Zend/zend_compile.c

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5729,19 +5729,14 @@ static void zend_compile_attribute(zval *v, zend_ast *ast) /* {{{ */
57295729
if (ast->child[1]) {
57305730
zend_ast_list *list;
57315731
uint32_t i;
5732-
57335732
zval tmp;
5734-
zval *x;
57355733

57365734
ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_ARG_LIST);
57375735

57385736
ZVAL_NULL(&tmp);
57395737

57405738
for (list = zend_ast_get_list(ast->child[1]), i = 0; i < list->children; i++) {
5741-
zend_ast *el = list->child[i];
5742-
5743-
x = zend_hash_next_index_insert(Z_ARRVAL_P(v), &tmp);
5744-
zend_const_expr_to_zval(x, el);
5739+
zend_const_expr_to_zval(zend_hash_next_index_insert(Z_ARRVAL_P(v), &tmp), list->child[i]);
57455740
}
57465741
}
57475742
}
@@ -5782,14 +5777,13 @@ static HashTable *zend_compile_attributes(zend_ast *ast) /* {{{ */
57825777
add_next_index_zval(x, &a);
57835778
} else {
57845779
zval array;
5785-
zval ref;
57865780

57875781
ZEND_ASSERT(Z_TYPE_P(zend_hash_index_find(Z_ARRVAL_P(x), 0)) == IS_STRING);
57885782

5789-
ZVAL_COPY(&ref, x);
5783+
Z_ADDREF_P(x);
57905784

57915785
array_init(&array);
5792-
add_next_index_zval(&array, &ref);
5786+
add_next_index_zval(&array, x);
57935787
add_next_index_zval(&array, &a);
57945788
zend_hash_update(attr, name, &array);
57955789
}

ext/reflection/php_reflection.c

Lines changed: 128 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6477,41 +6477,160 @@ ZEND_METHOD(reflection_reference, getId)
64776477
ZEND_METHOD(reflection_attribute, getName)
64786478
{
64796479
reflection_object *intern;
6480-
attribute_reference *param;
6480+
attribute_reference *attr;
64816481

64826482
if (zend_parse_parameters_none() == FAILURE) {
64836483
RETURN_THROWS();
64846484
}
6485-
GET_REFLECTION_OBJECT_PTR(param);
6485+
GET_REFLECTION_OBJECT_PTR(attr);
64866486

6487-
RETVAL_STR_COPY(param->name);
6487+
RETURN_STR_COPY(attr->name);
64886488
}
64896489
/* }}} */
64906490

64916491
/* {{{ proto public string ReflectionAttribute::getArguments()
64926492
* Returns the arguments passed to the attribute */
64936493
ZEND_METHOD(reflection_attribute, getArguments)
64946494
{
6495+
reflection_object *intern;
6496+
attribute_reference *attr;
6497+
64956498
if (zend_parse_parameters_none() == FAILURE) {
64966499
RETURN_THROWS();
64976500
}
6501+
GET_REFLECTION_OBJECT_PTR(attr);
64986502

6499-
reflection_object *intern;
6500-
attribute_reference *param;
6503+
RETURN_ZVAL(&attr->arguments, 1, 0);
6504+
}
6505+
/* }}} */
65016506

6502-
if (zend_parse_parameters_none() == FAILURE) {
6503-
RETURN_THROWS();
6507+
static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc) /* {{{ */
6508+
{
6509+
zend_fcall_info fci;
6510+
zend_fcall_info_cache fcc;
6511+
6512+
zend_function *ctor;
6513+
zend_class_entry *scope;
6514+
6515+
zval retval;
6516+
int ret;
6517+
6518+
ctor = ce->constructor;
6519+
6520+
ZEND_ASSERT(ctor != NULL);
6521+
6522+
if (!(ctor->common.fn_flags & ZEND_ACC_PUBLIC)) {
6523+
zend_throw_error(NULL, "Attribute constructor of class '%s' must be public", ZSTR_VAL(ce->name));
6524+
return FAILURE;
65046525
}
6505-
GET_REFLECTION_OBJECT_PTR(param);
65066526

6507-
RETVAL_ZVAL(&param->arguments, 1, 0);
6527+
fci.size = sizeof(fci);
6528+
ZVAL_UNDEF(&fci.function_name);
6529+
fci.object = obj;
6530+
fci.retval = &retval;
6531+
fci.params = args;
6532+
fci.param_count = argc;
6533+
fci.no_separation = 1;
6534+
6535+
fcc.function_handler = ctor;
6536+
fcc.called_scope = ce;
6537+
fcc.object = obj;
6538+
6539+
scope = EG(fake_scope);
6540+
EG(fake_scope) = NULL;
6541+
6542+
ret = zend_call_function(&fci, &fcc);
6543+
6544+
EG(fake_scope) = scope;
6545+
6546+
if (EG(exception)) {
6547+
zend_object_store_ctor_failed(obj);
6548+
}
6549+
6550+
zval_ptr_dtor(&retval);
6551+
6552+
if (ret != SUCCESS) {
6553+
zend_throw_error(NULL, "Failed to invoke constructor of attribute class '%s'", ZSTR_VAL(ce->name));
6554+
}
6555+
6556+
return EG(exception) ? FAILURE : SUCCESS;
6557+
}
6558+
/* }}} */
6559+
6560+
static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{ */
6561+
{
6562+
if (obj) {
6563+
zval_ptr_dtor(obj);
6564+
}
6565+
6566+
if (args) {
6567+
uint32_t i;
6568+
6569+
for (i = 0; i < argc; i++) {
6570+
zval_ptr_dtor(&args[i]);
6571+
}
6572+
6573+
efree(args);
6574+
}
65086575
}
65096576
/* }}} */
65106577

65116578
/* {{{ proto public string ReflectionAttribute::getAsObject()
65126579
* Returns the attribute as an object */
65136580
ZEND_METHOD(reflection_attribute, getAsObject)
65146581
{
6582+
reflection_object *intern;
6583+
attribute_reference *attr;
6584+
6585+
zend_class_entry *ce;
6586+
zval obj;
6587+
6588+
zval *args = NULL;
6589+
uint32_t count;
6590+
uint32_t argc = 0;
6591+
6592+
if (zend_parse_parameters_none() == FAILURE) {
6593+
RETURN_THROWS();
6594+
}
6595+
6596+
GET_REFLECTION_OBJECT_PTR(attr);
6597+
6598+
if (NULL == (ce = zend_lookup_class(attr->name))) {
6599+
zend_throw_error(NULL, "Attribute class '%s' not found", ZSTR_VAL(attr->name));
6600+
RETURN_THROWS();
6601+
}
6602+
6603+
if (SUCCESS != object_init_ex(&obj, ce)) {
6604+
RETURN_THROWS();
6605+
}
6606+
6607+
count = zend_hash_num_elements(Z_ARRVAL(attr->arguments));
6608+
6609+
if (count) {
6610+
Bucket *p;
6611+
6612+
args = emalloc(count * sizeof(zval));
6613+
6614+
ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL(attr->arguments), p) {
6615+
ZVAL_COPY(&args[argc], &p->val);
6616+
argc++;
6617+
} ZEND_HASH_FOREACH_END();
6618+
}
6619+
6620+
if (ce->constructor) {
6621+
if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc)) {
6622+
attribute_ctor_cleanup(&obj, args, argc);
6623+
RETURN_THROWS();
6624+
}
6625+
} else if (argc) {
6626+
attribute_ctor_cleanup(&obj, args, argc);
6627+
zend_throw_error(NULL, "Annotation class '%s' does not have a constructor, cannot pass arguments", ZSTR_VAL(ce->name));
6628+
RETURN_THROWS();
6629+
}
6630+
6631+
attribute_ctor_cleanup(NULL, args, argc);
6632+
6633+
RETURN_ZVAL(&obj, 1, 1);
65156634
}
65166635
/* }}} */
65176636

0 commit comments

Comments
 (0)