Skip to content

Commit 42e8df6

Browse files
committed
Support for reflection invoke etc
1 parent dbbf0c8 commit 42e8df6

File tree

5 files changed

+81
-101
lines changed

5 files changed

+81
-101
lines changed

Zend/tests/named_params/call_user_func.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@ $test_ref = function(&$ref) {
1313
$ref++;
1414
};
1515

16+
class Test {
17+
public function __construct($a = 'a', $b = 'b', $c = 'c') {
18+
if (func_num_args() != 0) {
19+
echo "a = $a, b = $b, c = $c\n";
20+
}
21+
}
22+
23+
public function method($a = 'a', $b = 'b', $c = 'c') {
24+
echo "a = $a, b = $b, c = $c\n";
25+
}
26+
}
27+
1628
call_user_func($test, 'A', c: 'C');
1729
call_user_func($test, c: 'C', a: 'A');
1830
call_user_func($test, c: 'C');
@@ -26,6 +38,17 @@ $test->__invoke('A', c: 'C');
2638
$test_variadic->__invoke('A', c: 'C');
2739
$test->call(new class {}, 'A', c: 'C');
2840
$test_variadic->call(new class {}, 'A', c: 'C');
41+
echo "\n";
42+
43+
$rf = new ReflectionFunction($test);
44+
$rf->invoke('A', c: 'C');
45+
$rf->invokeArgs(['A', 'c' => 'C']);
46+
$rm = new ReflectionMethod(Test::class, 'method');
47+
$rm->invoke(new Test, 'A', c: 'C');
48+
$rm->invokeArgs(new Test, ['A', 'c' => 'C']);
49+
$rc = new ReflectionClass(Test::class);
50+
$rc->newInstance('A', c: 'C');
51+
$rc->newInstanceArgs(['A', 'c' => 'C']);
2952

3053
?>
3154
--EXPECTF--
@@ -63,3 +86,10 @@ array(2) {
6386
["c"]=>
6487
string(1) "C"
6588
}
89+
90+
a = A, b = b, c = C
91+
a = A, b = b, c = C
92+
a = A, b = b, c = C
93+
a = A, b = b, c = C
94+
a = A, b = b, c = C
95+
a = A, b = b, c = C

Zend/zend_execute.c

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3618,7 +3618,7 @@ ZEND_API void zend_init_execute_data(zend_execute_data *execute_data, zend_op_ar
36183618
}
36193619
/* }}} */
36203620

3621-
static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call, uint32_t passed_args, uint32_t additional_args) /* {{{ */
3621+
zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call, uint32_t passed_args, uint32_t additional_args) /* {{{ */
36223622
{
36233623
zend_execute_data *new_call;
36243624
int used_stack = (EG(vm_stack_top) - (zval*)call) + additional_args;
@@ -3654,16 +3654,6 @@ static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call,
36543654
}
36553655
/* }}} */
36563656

3657-
static zend_always_inline void zend_vm_stack_extend_call_frame(zend_execute_data **call, uint32_t passed_args, uint32_t additional_args) /* {{{ */
3658-
{
3659-
if (EXPECTED((uint32_t)(EG(vm_stack_end) - EG(vm_stack_top)) > additional_args)) {
3660-
EG(vm_stack_top) += additional_args;
3661-
} else {
3662-
*call = zend_vm_stack_copy_call_frame(*call, passed_args, additional_args);
3663-
}
3664-
}
3665-
/* }}} */
3666-
36673657
static zend_always_inline zend_generator *zend_get_running_generator(EXECUTE_DATA_D) /* {{{ */
36683658
{
36693659
/* The generator object is stored in EX(return_value) */

Zend/zend_execute.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,19 @@ static zend_always_inline void zend_vm_stack_free_call_frame(zend_execute_data *
289289
zend_vm_stack_free_call_frame_ex(ZEND_CALL_INFO(call), call);
290290
}
291291

292+
zend_execute_data *zend_vm_stack_copy_call_frame(
293+
zend_execute_data *call, uint32_t passed_args, uint32_t additional_args);
294+
295+
static zend_always_inline void zend_vm_stack_extend_call_frame(
296+
zend_execute_data **call, uint32_t passed_args, uint32_t additional_args)
297+
{
298+
if (EXPECTED((uint32_t)(EG(vm_stack_end) - EG(vm_stack_top)) > additional_args)) {
299+
EG(vm_stack_top) += additional_args;
300+
} else {
301+
*call = zend_vm_stack_copy_call_frame(*call, passed_args, additional_args);
302+
}
303+
}
304+
292305
/* services */
293306
ZEND_API const char *get_active_class_name(const char **space);
294307
ZEND_API const char *get_active_function_name(void);

Zend/zend_execute_API.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
797797
goto cleanup_args;
798798
}
799799

800+
zend_vm_stack_extend_call_frame(&call, arg_num - 1, 1);
800801
target = ZEND_CALL_ARG(call, arg_num);
801802
}
802803

ext/reflection/php_reflection.c

Lines changed: 36 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,26 +1815,27 @@ ZEND_METHOD(ReflectionFunctionAbstract, getStaticVariables)
18151815
ZEND_METHOD(ReflectionFunction, invoke)
18161816
{
18171817
zval retval;
1818-
zval *params = NULL;
1819-
int result, num_args = 0;
1818+
zval *params;
1819+
int result, num_args;
1820+
HashTable *named_params;
18201821
zend_fcall_info fci;
18211822
zend_fcall_info_cache fcc;
18221823
reflection_object *intern;
18231824
zend_function *fptr;
18241825

1825-
GET_REFLECTION_OBJECT_PTR(fptr);
1826+
ZEND_PARSE_PARAMETERS_START(0, -1)
1827+
Z_PARAM_VARIADIC_WITH_NAMED(params, num_args, named_params)
1828+
ZEND_PARSE_PARAMETERS_END();
18261829

1827-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &params, &num_args) == FAILURE) {
1828-
RETURN_THROWS();
1829-
}
1830+
GET_REFLECTION_OBJECT_PTR(fptr);
18301831

18311832
fci.size = sizeof(fci);
18321833
ZVAL_UNDEF(&fci.function_name);
18331834
fci.object = NULL;
18341835
fci.retval = &retval;
18351836
fci.param_count = num_args;
18361837
fci.params = params;
1837-
fci.named_params = NULL;
1838+
fci.named_params = named_params;
18381839
fci.no_separation = 1;
18391840

18401841
fcc.function_handler = fptr;
@@ -1868,37 +1869,26 @@ ZEND_METHOD(ReflectionFunction, invoke)
18681869
ZEND_METHOD(ReflectionFunction, invokeArgs)
18691870
{
18701871
zval retval;
1871-
zval *params, *val;
18721872
int result;
1873-
int i, argc;
18741873
zend_fcall_info fci;
18751874
zend_fcall_info_cache fcc;
18761875
reflection_object *intern;
18771876
zend_function *fptr;
1878-
zval *param_array;
1877+
HashTable *params;
18791878

18801879
GET_REFLECTION_OBJECT_PTR(fptr);
18811880

1882-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &param_array) == FAILURE) {
1881+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &params) == FAILURE) {
18831882
RETURN_THROWS();
18841883
}
18851884

1886-
argc = zend_hash_num_elements(Z_ARRVAL_P(param_array));
1887-
1888-
params = safe_emalloc(sizeof(zval), argc, 0);
1889-
argc = 0;
1890-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(param_array), val) {
1891-
ZVAL_COPY(&params[argc], val);
1892-
argc++;
1893-
} ZEND_HASH_FOREACH_END();
1894-
18951885
fci.size = sizeof(fci);
18961886
ZVAL_UNDEF(&fci.function_name);
18971887
fci.object = NULL;
18981888
fci.retval = &retval;
1899-
fci.param_count = argc;
1900-
fci.params = params;
1901-
fci.named_params = NULL;
1889+
fci.param_count = 0;
1890+
fci.params = NULL;
1891+
fci.named_params = params;
19021892
fci.no_separation = 1;
19031893

19041894
fcc.function_handler = fptr;
@@ -1912,11 +1902,6 @@ ZEND_METHOD(ReflectionFunction, invokeArgs)
19121902

19131903
result = zend_call_function(&fci, &fcc);
19141904

1915-
for (i = 0; i < argc; i++) {
1916-
zval_ptr_dtor(&params[i]);
1917-
}
1918-
efree(params);
1919-
19201905
if (result == FAILURE) {
19211906
zend_throw_exception_ex(reflection_exception_ptr, 0,
19221907
"Invocation of function %s() failed", ZSTR_VAL(fptr->common.function_name));
@@ -3183,14 +3168,14 @@ ZEND_METHOD(ReflectionMethod, getClosure)
31833168
static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
31843169
{
31853170
zval retval;
3186-
zval *params = NULL, *val, *object;
3171+
zval *params = NULL, *object;
3172+
HashTable *named_params = NULL;
31873173
reflection_object *intern;
31883174
zend_function *mptr;
3189-
int i, argc = 0, result;
3175+
int argc = 0, result;
31903176
zend_fcall_info fci;
31913177
zend_fcall_info_cache fcc;
31923178
zend_class_entry *obj_ce;
3193-
zval *param_array;
31943179

31953180
GET_REFLECTION_OBJECT_PTR(mptr);
31963181

@@ -3211,22 +3196,14 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
32113196
}
32123197

32133198
if (variadic) {
3214-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!*", &object, &params, &argc) == FAILURE) {
3215-
RETURN_THROWS();
3216-
}
3199+
ZEND_PARSE_PARAMETERS_START(1, -1)
3200+
Z_PARAM_OBJECT_OR_NULL(object)
3201+
Z_PARAM_VARIADIC_WITH_NAMED(params, argc, named_params)
3202+
ZEND_PARSE_PARAMETERS_END();
32173203
} else {
3218-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!a", &object, &param_array) == FAILURE) {
3204+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!h", &object, &named_params) == FAILURE) {
32193205
RETURN_THROWS();
32203206
}
3221-
3222-
argc = zend_hash_num_elements(Z_ARRVAL_P(param_array));
3223-
3224-
params = safe_emalloc(sizeof(zval), argc, 0);
3225-
argc = 0;
3226-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(param_array), val) {
3227-
ZVAL_COPY(&params[argc], val);
3228-
argc++;
3229-
} ZEND_HASH_FOREACH_END();
32303207
}
32313208

32323209
/* In case this is a static method, we shouldn't pass an object_ptr
@@ -3263,7 +3240,7 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
32633240
fci.retval = &retval;
32643241
fci.param_count = argc;
32653242
fci.params = params;
3266-
fci.named_params = NULL;
3243+
fci.named_params = named_params;
32673244
fci.no_separation = 1;
32683245

32693246
fcc.function_handler = mptr;
@@ -3279,13 +3256,6 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
32793256

32803257
result = zend_call_function(&fci, &fcc);
32813258

3282-
if (!variadic) {
3283-
for (i = 0; i < argc; i++) {
3284-
zval_ptr_dtor(&params[i]);
3285-
}
3286-
efree(params);
3287-
}
3288-
32893259
if (result == FAILURE) {
32903260
zend_throw_exception_ex(reflection_exception_ptr, 0,
32913261
"Invocation of method %s::%s() failed", ZSTR_VAL(mptr->common.scope->name), ZSTR_VAL(mptr->common.function_name));
@@ -4811,29 +4781,23 @@ ZEND_METHOD(ReflectionClass, newInstance)
48114781

48124782
/* Run the constructor if there is one */
48134783
if (constructor) {
4814-
zval *params = NULL;
4815-
int i, num_args = 0;
4784+
zval *params;
4785+
int num_args;
4786+
HashTable *named_params;
48164787

48174788
if (!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)) {
48184789
zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name));
48194790
zval_ptr_dtor(return_value);
48204791
RETURN_NULL();
48214792
}
48224793

4823-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &params, &num_args) == FAILURE) {
4824-
zval_ptr_dtor(return_value);
4825-
RETURN_THROWS();
4826-
}
4827-
4828-
for (i = 0; i < num_args; i++) {
4829-
Z_TRY_ADDREF(params[i]);
4830-
}
4831-
4832-
zend_call_known_instance_method(constructor, Z_OBJ_P(return_value), NULL, num_args, params);
4794+
ZEND_PARSE_PARAMETERS_START(0, -1)
4795+
Z_PARAM_VARIADIC_WITH_NAMED(params, num_args, named_params)
4796+
ZEND_PARSE_PARAMETERS_END();
48334797

4834-
for (i = 0; i < num_args; i++) {
4835-
zval_ptr_dtor(&params[i]);
4836-
}
4798+
zend_call_known_function(
4799+
constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL,
4800+
num_args, params, named_params);
48374801

48384802
if (EG(exception)) {
48394803
zend_object_store_ctor_failed(Z_OBJ_P(return_value));
@@ -4871,10 +4835,9 @@ ZEND_METHOD(ReflectionClass, newInstanceWithoutConstructor)
48714835
Returns an instance of this class */
48724836
ZEND_METHOD(ReflectionClass, newInstanceArgs)
48734837
{
4874-
zval *val;
48754838
reflection_object *intern;
48764839
zend_class_entry *ce, *old_scope;
4877-
int i, argc = 0;
4840+
int argc = 0;
48784841
HashTable *args;
48794842
zend_function *constructor;
48804843

@@ -4884,8 +4847,8 @@ ZEND_METHOD(ReflectionClass, newInstanceArgs)
48844847
RETURN_THROWS();
48854848
}
48864849

4887-
if (ZEND_NUM_ARGS() > 0) {
4888-
argc = args->nNumOfElements;
4850+
if (args) {
4851+
argc = zend_hash_num_elements(args);
48894852
}
48904853

48914854
if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) {
@@ -4899,31 +4862,14 @@ ZEND_METHOD(ReflectionClass, newInstanceArgs)
48994862

49004863
/* Run the constructor if there is one */
49014864
if (constructor) {
4902-
zval *params = NULL;
4903-
49044865
if (!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)) {
49054866
zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name));
49064867
zval_ptr_dtor(return_value);
49074868
RETURN_NULL();
49084869
}
49094870

4910-
if (argc) {
4911-
params = safe_emalloc(sizeof(zval), argc, 0);
4912-
argc = 0;
4913-
ZEND_HASH_FOREACH_VAL(args, val) {
4914-
ZVAL_COPY(&params[argc], val);
4915-
argc++;
4916-
} ZEND_HASH_FOREACH_END();
4917-
}
4918-
4919-
zend_call_known_instance_method(constructor, Z_OBJ_P(return_value), NULL, argc, params);
4920-
4921-
if (params) {
4922-
for (i = 0; i < argc; i++) {
4923-
zval_ptr_dtor(&params[i]);
4924-
}
4925-
efree(params);
4926-
}
4871+
zend_call_known_function(
4872+
constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL, 0, NULL, args);
49274873

49284874
if (EG(exception)) {
49294875
zend_object_store_ctor_failed(Z_OBJ_P(return_value));

0 commit comments

Comments
 (0)