Skip to content

Commit 8a2b596

Browse files
committed
Properly support named params in attributes
1 parent 6aa02f5 commit 8a2b596

File tree

7 files changed

+105
-24
lines changed

7 files changed

+105
-24
lines changed

Zend/tests/named_params/attributes.phpt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,23 @@ class MyAttribute {
1313
}
1414

1515
<<MyAttribute('A', c: 'C')>>
16-
class Test {}
16+
class Test1 {}
1717

18-
$attr = (new ReflectionClass(Test::class))->getAttributes()[0];
18+
<<MyAttribute('A', a: 'C')>>
19+
class Test2 {}
20+
21+
$attr = (new ReflectionClass(Test1::class))->getAttributes()[0];
1922
var_dump($attr->getName());
2023
var_dump($attr->getArguments());
2124
var_dump($attr->newInstance());
2225

26+
$attr = (new ReflectionClass(Test2::class))->getAttributes()[0];
27+
try {
28+
var_dump($attr->newInstance());
29+
} catch (Error $e) {
30+
echo $e->getMessage(), "\n";
31+
}
32+
2333
?>
2434
--EXPECT--
2535
string(11) "MyAttribute"
@@ -33,7 +43,8 @@ object(MyAttribute)#1 (3) {
3343
["a"]=>
3444
string(1) "A"
3545
["b"]=>
36-
string(1) "C"
46+
string(1) "b"
3747
["c"]=>
38-
string(1) "c"
48+
string(1) "C"
3949
}
50+
Named parameter $a overwrites previous argument

Zend/tests/named_params/internal_variadics.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Named params on internal functions: Variadic functions
2+
Named params on internal functions: Variadic functions that don't support extra named args
33
--FILE--
44
<?php
55

Zend/zend_API.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ typedef struct _zend_fcall_info {
5050
uint32_t param_count;
5151
} zend_fcall_info;
5252

53+
typedef struct _zend_fcall_info_named {
54+
zend_fcall_info fci;
55+
HashTable *named_params;
56+
} zend_fcall_info_named;
57+
5358
typedef struct _zend_fcall_info_cache {
5459
zend_function *function_handler;
5560
zend_class_entry *calling_scope;

Zend/zend_execute.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4337,7 +4337,7 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
43374337
return (uint32_t) -1;
43384338
}
43394339

4340-
static zval * ZEND_FASTCALL zend_handle_named_arg(
4340+
zval * ZEND_FASTCALL zend_handle_named_arg(
43414341
zend_execute_data **call_ptr, zend_string *arg_name,
43424342
uint32_t *arg_num_ptr, void **cache_slot) {
43434343
zend_execute_data *call = *call_ptr;

Zend/zend_execute.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,10 @@ ZEND_API void zend_clean_and_cache_symbol_table(zend_array *symbol_table);
330330
ZEND_API void zend_free_compiled_variables(zend_execute_data *execute_data);
331331
ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num);
332332

333+
zval * ZEND_FASTCALL zend_handle_named_arg(
334+
zend_execute_data **call_ptr, zend_string *arg_name,
335+
uint32_t *arg_num_ptr, void **cache_slot);
336+
333337
#define CACHE_ADDR(num) \
334338
((void**)((char*)EX(run_time_cache) + (num)))
335339

Zend/zend_execute_API.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,8 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
658658
return FAILURE; /* we would result in an instable executor otherwise */
659659
}
660660

661-
ZEND_ASSERT(fci->size == sizeof(zend_fcall_info));
661+
ZEND_ASSERT(fci->size == sizeof(zend_fcall_info)
662+
|| fci->size == sizeof(zend_fcall_info_named));
662663

663664
/* Initialize execute_data */
664665
if (!EG(current_execute_data)) {
@@ -781,6 +782,33 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
781782
ZVAL_COPY(param, arg);
782783
}
783784

785+
if (fci->size == sizeof(zend_fcall_info_named)) {
786+
HashTable *named_params = ((zend_fcall_info_named *) fci)->named_params;
787+
if (named_params) {
788+
zend_string *name;
789+
zval *val;
790+
ZEND_HASH_FOREACH_STR_KEY_VAL(named_params, name, val) {
791+
ZEND_ASSERT(name && "named_params may only contain named params");
792+
793+
void *cache_slot = NULL;
794+
uint32_t arg_num;
795+
zval *target = zend_handle_named_arg(&call, name, &arg_num, &cache_slot);
796+
if (!target) {
797+
zend_vm_stack_free_args(call);
798+
zend_vm_stack_free_call_frame(call);
799+
if (EG(current_execute_data) == &dummy_execute_data) {
800+
EG(current_execute_data) = dummy_execute_data.prev_execute_data;
801+
}
802+
return FAILURE;
803+
}
804+
805+
/*if (ARG_SHOULD_BE_SENT_BY_REF(func, arg_num)) {
806+
}*/
807+
ZVAL_COPY(target, val);
808+
} ZEND_HASH_FOREACH_END();
809+
}
810+
}
811+
784812
if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
785813
uint32_t call_info;
786814

ext/reflection/php_reflection.c

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6504,7 +6504,7 @@ ZEND_METHOD(ReflectionAttribute, getArguments)
65046504
}
65056505
/* }}} */
65066506

6507-
static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc) /* {{{ */
6507+
static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc, HashTable *named_params) /* {{{ */
65086508
{
65096509
zend_function *ctor = ce->constructor;
65106510
ZEND_ASSERT(ctor != NULL);
@@ -6514,7 +6514,27 @@ static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zv
65146514
return FAILURE;
65156515
}
65166516

6517-
zend_call_known_instance_method(ctor, obj, NULL, argc, args);
6517+
{
6518+
zval retval;
6519+
zend_fcall_info_named fci_named;
6520+
zend_fcall_info_cache fcic;
6521+
6522+
fci_named.fci.size = sizeof(zend_fcall_info_named);
6523+
fci_named.fci.object = obj;
6524+
fci_named.fci.retval = &retval;
6525+
fci_named.fci.param_count = argc;
6526+
fci_named.fci.params = args;
6527+
fci_named.fci.no_separation = 1;
6528+
fci_named.named_params = named_params;
6529+
ZVAL_UNDEF(&fci_named.fci.function_name); /* Unused */
6530+
6531+
fcic.function_handler = ctor;
6532+
fcic.object = obj;
6533+
fcic.called_scope = obj->ce;
6534+
6535+
zend_call_function(&fci_named.fci, &fcic);
6536+
}
6537+
65186538
if (EG(exception)) {
65196539
zend_object_store_ctor_failed(obj);
65206540
return FAILURE;
@@ -6524,7 +6544,8 @@ static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zv
65246544
}
65256545
/* }}} */
65266546

6527-
static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{ */
6547+
static void attribute_ctor_cleanup(
6548+
zval *obj, zval *args, uint32_t argc, HashTable *named_params) /* {{{ */
65286549
{
65296550
if (obj) {
65306551
zval_ptr_dtor(obj);
@@ -6539,6 +6560,10 @@ static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{
65396560

65406561
efree(args);
65416562
}
6563+
6564+
if (named_params) {
6565+
zend_array_destroy(named_params);
6566+
}
65426567
}
65436568
/* }}} */
65446569

@@ -6554,8 +6579,7 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
65546579
zval obj;
65556580

65566581
zval *args = NULL;
6557-
uint32_t count;
6558-
uint32_t argc = 0;
6582+
HashTable *named_params = NULL;
65596583

65606584
if (zend_parse_parameters_none() == FAILURE) {
65616585
RETURN_THROWS();
@@ -6612,31 +6636,40 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
66126636
RETURN_THROWS();
66136637
}
66146638

6615-
count = attr->data->argc;
6616-
6617-
if (count) {
6618-
args = emalloc(count * sizeof(zval));
6639+
uint32_t argc = 0;
6640+
if (attr->data->argc) {
6641+
args = emalloc(attr->data->argc * sizeof(zval));
66196642

6620-
for (argc = 0; argc < attr->data->argc; argc++) {
6621-
if (FAILURE == zend_get_attribute_value(&args[argc], attr->data, argc, attr->scope)) {
6622-
attribute_ctor_cleanup(&obj, args, argc);
6643+
for (uint32_t i = 0; i < attr->data->argc; i++) {
6644+
zval val;
6645+
if (FAILURE == zend_get_attribute_value(&val, attr->data, i, attr->scope)) {
6646+
attribute_ctor_cleanup(&obj, args, i, named_params);
66236647
RETURN_THROWS();
66246648
}
6649+
if (attr->data->args[i].name) {
6650+
if (!named_params) {
6651+
named_params = zend_new_array(0);
6652+
}
6653+
zend_hash_add_new(named_params, attr->data->args[i].name, &val);
6654+
} else {
6655+
ZVAL_COPY_VALUE(&args[i], &val);
6656+
argc++;
6657+
}
66256658
}
66266659
}
66276660

66286661
if (ce->constructor) {
6629-
if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc)) {
6630-
attribute_ctor_cleanup(&obj, args, argc);
6662+
if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc, named_params)) {
6663+
attribute_ctor_cleanup(&obj, args, argc, named_params);
66316664
RETURN_THROWS();
66326665
}
6633-
} else if (argc) {
6634-
attribute_ctor_cleanup(&obj, args, argc);
6666+
} else if (argc || named_params) {
6667+
attribute_ctor_cleanup(&obj, args, argc, named_params);
66356668
zend_throw_error(NULL, "Attribute class '%s' does not have a constructor, cannot pass arguments", ZSTR_VAL(ce->name));
66366669
RETURN_THROWS();
66376670
}
66386671

6639-
attribute_ctor_cleanup(NULL, args, argc);
6672+
attribute_ctor_cleanup(NULL, args, argc, named_params);
66406673

66416674
RETURN_COPY_VALUE(&obj);
66426675
}

0 commit comments

Comments
 (0)