Skip to content

Commit

Permalink
Support for reflection invoke etc
Browse files Browse the repository at this point in the history
  • Loading branch information
nikic committed Jul 6, 2020
1 parent dbbf0c8 commit 42e8df6
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 101 deletions.
30 changes: 30 additions & 0 deletions Zend/tests/named_params/call_user_func.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ $test_ref = function(&$ref) {
$ref++;
};

class Test {
public function __construct($a = 'a', $b = 'b', $c = 'c') {
if (func_num_args() != 0) {
echo "a = $a, b = $b, c = $c\n";
}
}

public function method($a = 'a', $b = 'b', $c = 'c') {
echo "a = $a, b = $b, c = $c\n";
}
}

call_user_func($test, 'A', c: 'C');
call_user_func($test, c: 'C', a: 'A');
call_user_func($test, c: 'C');
Expand All @@ -26,6 +38,17 @@ $test->__invoke('A', c: 'C');
$test_variadic->__invoke('A', c: 'C');
$test->call(new class {}, 'A', c: 'C');
$test_variadic->call(new class {}, 'A', c: 'C');
echo "\n";

$rf = new ReflectionFunction($test);
$rf->invoke('A', c: 'C');
$rf->invokeArgs(['A', 'c' => 'C']);
$rm = new ReflectionMethod(Test::class, 'method');
$rm->invoke(new Test, 'A', c: 'C');
$rm->invokeArgs(new Test, ['A', 'c' => 'C']);
$rc = new ReflectionClass(Test::class);
$rc->newInstance('A', c: 'C');
$rc->newInstanceArgs(['A', 'c' => 'C']);

?>
--EXPECTF--
Expand Down Expand Up @@ -63,3 +86,10 @@ array(2) {
["c"]=>
string(1) "C"
}

a = A, b = b, c = C
a = A, b = b, c = C
a = A, b = b, c = C
a = A, b = b, c = C
a = A, b = b, c = C
a = A, b = b, c = C
12 changes: 1 addition & 11 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -3618,7 +3618,7 @@ ZEND_API void zend_init_execute_data(zend_execute_data *execute_data, zend_op_ar
}
/* }}} */

static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call, uint32_t passed_args, uint32_t additional_args) /* {{{ */
zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call, uint32_t passed_args, uint32_t additional_args) /* {{{ */
{
zend_execute_data *new_call;
int used_stack = (EG(vm_stack_top) - (zval*)call) + additional_args;
Expand Down Expand Up @@ -3654,16 +3654,6 @@ static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call,
}
/* }}} */

static zend_always_inline void zend_vm_stack_extend_call_frame(zend_execute_data **call, uint32_t passed_args, uint32_t additional_args) /* {{{ */
{
if (EXPECTED((uint32_t)(EG(vm_stack_end) - EG(vm_stack_top)) > additional_args)) {
EG(vm_stack_top) += additional_args;
} else {
*call = zend_vm_stack_copy_call_frame(*call, passed_args, additional_args);
}
}
/* }}} */

static zend_always_inline zend_generator *zend_get_running_generator(EXECUTE_DATA_D) /* {{{ */
{
/* The generator object is stored in EX(return_value) */
Expand Down
13 changes: 13 additions & 0 deletions Zend/zend_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,19 @@ static zend_always_inline void zend_vm_stack_free_call_frame(zend_execute_data *
zend_vm_stack_free_call_frame_ex(ZEND_CALL_INFO(call), call);
}

zend_execute_data *zend_vm_stack_copy_call_frame(
zend_execute_data *call, uint32_t passed_args, uint32_t additional_args);

static zend_always_inline void zend_vm_stack_extend_call_frame(
zend_execute_data **call, uint32_t passed_args, uint32_t additional_args)
{
if (EXPECTED((uint32_t)(EG(vm_stack_end) - EG(vm_stack_top)) > additional_args)) {
EG(vm_stack_top) += additional_args;
} else {
*call = zend_vm_stack_copy_call_frame(*call, passed_args, additional_args);
}
}

/* services */
ZEND_API const char *get_active_class_name(const char **space);
ZEND_API const char *get_active_function_name(void);
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
goto cleanup_args;
}

zend_vm_stack_extend_call_frame(&call, arg_num - 1, 1);
target = ZEND_CALL_ARG(call, arg_num);
}

Expand Down
126 changes: 36 additions & 90 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -1815,26 +1815,27 @@ ZEND_METHOD(ReflectionFunctionAbstract, getStaticVariables)
ZEND_METHOD(ReflectionFunction, invoke)
{
zval retval;
zval *params = NULL;
int result, num_args = 0;
zval *params;
int result, num_args;
HashTable *named_params;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
reflection_object *intern;
zend_function *fptr;

GET_REFLECTION_OBJECT_PTR(fptr);
ZEND_PARSE_PARAMETERS_START(0, -1)
Z_PARAM_VARIADIC_WITH_NAMED(params, num_args, named_params)
ZEND_PARSE_PARAMETERS_END();

if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &params, &num_args) == FAILURE) {
RETURN_THROWS();
}
GET_REFLECTION_OBJECT_PTR(fptr);

fci.size = sizeof(fci);
ZVAL_UNDEF(&fci.function_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = num_args;
fci.params = params;
fci.named_params = NULL;
fci.named_params = named_params;
fci.no_separation = 1;

fcc.function_handler = fptr;
Expand Down Expand Up @@ -1868,37 +1869,26 @@ ZEND_METHOD(ReflectionFunction, invoke)
ZEND_METHOD(ReflectionFunction, invokeArgs)
{
zval retval;
zval *params, *val;
int result;
int i, argc;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
reflection_object *intern;
zend_function *fptr;
zval *param_array;
HashTable *params;

GET_REFLECTION_OBJECT_PTR(fptr);

if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &param_array) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &params) == FAILURE) {
RETURN_THROWS();
}

argc = zend_hash_num_elements(Z_ARRVAL_P(param_array));

params = safe_emalloc(sizeof(zval), argc, 0);
argc = 0;
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(param_array), val) {
ZVAL_COPY(&params[argc], val);
argc++;
} ZEND_HASH_FOREACH_END();

fci.size = sizeof(fci);
ZVAL_UNDEF(&fci.function_name);
fci.object = NULL;
fci.retval = &retval;
fci.param_count = argc;
fci.params = params;
fci.named_params = NULL;
fci.param_count = 0;
fci.params = NULL;
fci.named_params = params;
fci.no_separation = 1;

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

result = zend_call_function(&fci, &fcc);

for (i = 0; i < argc; i++) {
zval_ptr_dtor(&params[i]);
}
efree(params);

if (result == FAILURE) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
"Invocation of function %s() failed", ZSTR_VAL(fptr->common.function_name));
Expand Down Expand Up @@ -3183,14 +3168,14 @@ ZEND_METHOD(ReflectionMethod, getClosure)
static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
{
zval retval;
zval *params = NULL, *val, *object;
zval *params = NULL, *object;
HashTable *named_params = NULL;
reflection_object *intern;
zend_function *mptr;
int i, argc = 0, result;
int argc = 0, result;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
zend_class_entry *obj_ce;
zval *param_array;

GET_REFLECTION_OBJECT_PTR(mptr);

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

if (variadic) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!*", &object, &params, &argc) == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_START(1, -1)
Z_PARAM_OBJECT_OR_NULL(object)
Z_PARAM_VARIADIC_WITH_NAMED(params, argc, named_params)
ZEND_PARSE_PARAMETERS_END();
} else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!a", &object, &param_array) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!h", &object, &named_params) == FAILURE) {
RETURN_THROWS();
}

argc = zend_hash_num_elements(Z_ARRVAL_P(param_array));

params = safe_emalloc(sizeof(zval), argc, 0);
argc = 0;
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(param_array), val) {
ZVAL_COPY(&params[argc], val);
argc++;
} ZEND_HASH_FOREACH_END();
}

/* In case this is a static method, we shouldn't pass an object_ptr
Expand Down Expand Up @@ -3263,7 +3240,7 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
fci.retval = &retval;
fci.param_count = argc;
fci.params = params;
fci.named_params = NULL;
fci.named_params = named_params;
fci.no_separation = 1;

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

result = zend_call_function(&fci, &fcc);

if (!variadic) {
for (i = 0; i < argc; i++) {
zval_ptr_dtor(&params[i]);
}
efree(params);
}

if (result == FAILURE) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
"Invocation of method %s::%s() failed", ZSTR_VAL(mptr->common.scope->name), ZSTR_VAL(mptr->common.function_name));
Expand Down Expand Up @@ -4811,29 +4781,23 @@ ZEND_METHOD(ReflectionClass, newInstance)

/* Run the constructor if there is one */
if (constructor) {
zval *params = NULL;
int i, num_args = 0;
zval *params;
int num_args;
HashTable *named_params;

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

if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &params, &num_args) == FAILURE) {
zval_ptr_dtor(return_value);
RETURN_THROWS();
}

for (i = 0; i < num_args; i++) {
Z_TRY_ADDREF(params[i]);
}

zend_call_known_instance_method(constructor, Z_OBJ_P(return_value), NULL, num_args, params);
ZEND_PARSE_PARAMETERS_START(0, -1)
Z_PARAM_VARIADIC_WITH_NAMED(params, num_args, named_params)
ZEND_PARSE_PARAMETERS_END();

for (i = 0; i < num_args; i++) {
zval_ptr_dtor(&params[i]);
}
zend_call_known_function(
constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL,
num_args, params, named_params);

if (EG(exception)) {
zend_object_store_ctor_failed(Z_OBJ_P(return_value));
Expand Down Expand Up @@ -4871,10 +4835,9 @@ ZEND_METHOD(ReflectionClass, newInstanceWithoutConstructor)
Returns an instance of this class */
ZEND_METHOD(ReflectionClass, newInstanceArgs)
{
zval *val;
reflection_object *intern;
zend_class_entry *ce, *old_scope;
int i, argc = 0;
int argc = 0;
HashTable *args;
zend_function *constructor;

Expand All @@ -4884,8 +4847,8 @@ ZEND_METHOD(ReflectionClass, newInstanceArgs)
RETURN_THROWS();
}

if (ZEND_NUM_ARGS() > 0) {
argc = args->nNumOfElements;
if (args) {
argc = zend_hash_num_elements(args);
}

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

/* Run the constructor if there is one */
if (constructor) {
zval *params = NULL;

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

if (argc) {
params = safe_emalloc(sizeof(zval), argc, 0);
argc = 0;
ZEND_HASH_FOREACH_VAL(args, val) {
ZVAL_COPY(&params[argc], val);
argc++;
} ZEND_HASH_FOREACH_END();
}

zend_call_known_instance_method(constructor, Z_OBJ_P(return_value), NULL, argc, params);

if (params) {
for (i = 0; i < argc; i++) {
zval_ptr_dtor(&params[i]);
}
efree(params);
}
zend_call_known_function(
constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL, 0, NULL, args);

if (EG(exception)) {
zend_object_store_ctor_failed(Z_OBJ_P(return_value));
Expand Down

0 comments on commit 42e8df6

Please sign in to comment.