diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2020-04-06 12:46:52 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-07-31 15:53:36 +0200 |
commit | d92229d8c78aac25925284e23aa7903dca9ed005 (patch) | |
tree | ec01712dbe44a11cb7368492388cb39ab04dac17 /ext/reflection/php_reflection.c | |
parent | 9a71d47d7334b9e2f83db48498047750c04cd192 (diff) | |
download | php-git-d92229d8c78aac25925284e23aa7903dca9ed005.tar.gz |
Implement named parameters
From an engine perspective, named parameters mainly add three
concepts:
* The SEND_* opcodes now accept a CONST op2, which is the
argument name. For now, it is looked up by linear scan and
runtime cached.
* This may leave UNDEF arguments on the stack. To avoid having
to deal with them in other places, a CHECK_UNDEF_ARGS opcode
is used to either replace them with defaults, or error.
* For variadic functions, EX(extra_named_params) are collected
and need to be freed based on ZEND_CALL_HAS_EXTRA_NAMED_PARAMS.
RFC: https://wiki.php.net/rfc/named_params
Closes GH-5357.
Diffstat (limited to 'ext/reflection/php_reflection.c')
-rw-r--r-- | ext/reflection/php_reflection.c | 180 |
1 files changed, 74 insertions, 106 deletions
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index decb8d5e0a..48367c7deb 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1796,18 +1796,19 @@ 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(), "*", ¶ms, &num_args) == FAILURE) { - RETURN_THROWS(); - } + GET_REFLECTION_OBJECT_PTR(fptr); fci.size = sizeof(fci); ZVAL_UNDEF(&fci.function_name); @@ -1815,6 +1816,7 @@ ZEND_METHOD(ReflectionFunction, invoke) fci.retval = &retval; fci.param_count = num_args; fci.params = params; + fci.named_params = named_params; fcc.function_handler = fptr; fcc.called_scope = NULL; @@ -1846,36 +1848,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", ¶m_array) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", ¶ms) == 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(¶ms[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.param_count = 0; + fci.params = NULL; + fci.named_params = params; fcc.function_handler = fptr; fcc.called_scope = NULL; @@ -1888,11 +1880,6 @@ ZEND_METHOD(ReflectionFunction, invokeArgs) result = zend_call_function(&fci, &fcc); - for (i = 0; i < argc; i++) { - zval_ptr_dtor(¶ms[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)); @@ -3127,14 +3114,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); @@ -3155,22 +3142,14 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic) } if (variadic) { - if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!*", &object, ¶ms, &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, ¶m_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(¶ms[argc], val); - argc++; - } ZEND_HASH_FOREACH_END(); } /* In case this is a static method, we shouldn't pass an object_ptr @@ -3207,6 +3186,7 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic) fci.retval = &retval; fci.param_count = argc; fci.params = params; + fci.named_params = named_params; fcc.function_handler = mptr; fcc.called_scope = intern->ce; @@ -3221,13 +3201,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(¶ms[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)); @@ -4676,8 +4649,9 @@ 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)); @@ -4685,20 +4659,13 @@ ZEND_METHOD(ReflectionClass, newInstance) RETURN_NULL(); } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", ¶ms, &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(¶ms[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)); @@ -4734,11 +4701,10 @@ 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; - HashTable *args; + int argc = 0; + HashTable *args = NULL; zend_function *constructor; GET_REFLECTION_OBJECT_PTR(ce); @@ -4747,8 +4713,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)) { @@ -4762,31 +4728,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(¶ms[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(¶ms[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)); @@ -6273,12 +6222,17 @@ ZEND_METHOD(ReflectionAttribute, getArguments) RETURN_THROWS(); } - add_next_index_zval(return_value, &tmp); + if (attr->data->args[i].name) { + /* We ensured at compile-time that there are no duplicate parameter names. */ + zend_hash_add_new(Z_ARRVAL_P(return_value), attr->data->args[i].name, &tmp); + } else { + add_next_index_zval(return_value, &tmp); + } } } /* }}} */ -static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc) /* {{{ */ +static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc, HashTable *named_params) /* {{{ */ { zend_function *ctor = ce->constructor; ZEND_ASSERT(ctor != NULL); @@ -6288,7 +6242,8 @@ static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zv return FAILURE; } - zend_call_known_instance_method(ctor, obj, NULL, argc, args); + zend_call_known_function(ctor, obj, obj->ce, NULL, argc, args, named_params); + if (EG(exception)) { zend_object_store_ctor_failed(obj); return FAILURE; @@ -6298,7 +6253,8 @@ static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zv } /* }}} */ -static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{ */ +static void attribute_ctor_cleanup( + zval *obj, zval *args, uint32_t argc, HashTable *named_params) /* {{{ */ { if (obj) { zval_ptr_dtor(obj); @@ -6313,6 +6269,10 @@ static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{ efree(args); } + + if (named_params) { + zend_array_destroy(named_params); + } } /* }}} */ @@ -6327,8 +6287,7 @@ ZEND_METHOD(ReflectionAttribute, newInstance) zval obj; zval *args = NULL; - uint32_t count; - uint32_t argc = 0; + HashTable *named_params = NULL; if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); @@ -6385,31 +6344,40 @@ ZEND_METHOD(ReflectionAttribute, newInstance) RETURN_THROWS(); } - count = attr->data->argc; - - if (count) { - args = emalloc(count * sizeof(zval)); + uint32_t argc = 0; + if (attr->data->argc) { + args = emalloc(attr->data->argc * sizeof(zval)); - for (argc = 0; argc < attr->data->argc; argc++) { - if (FAILURE == zend_get_attribute_value(&args[argc], attr->data, argc, attr->scope)) { - attribute_ctor_cleanup(&obj, args, argc); + for (uint32_t i = 0; i < attr->data->argc; i++) { + zval val; + if (FAILURE == zend_get_attribute_value(&val, attr->data, i, attr->scope)) { + attribute_ctor_cleanup(&obj, args, i, named_params); RETURN_THROWS(); } + if (attr->data->args[i].name) { + if (!named_params) { + named_params = zend_new_array(0); + } + zend_hash_add_new(named_params, attr->data->args[i].name, &val); + } else { + ZVAL_COPY_VALUE(&args[i], &val); + argc++; + } } } if (ce->constructor) { - if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc)) { - attribute_ctor_cleanup(&obj, args, argc); + if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc, named_params)) { + attribute_ctor_cleanup(&obj, args, argc, named_params); RETURN_THROWS(); } - } else if (argc) { - attribute_ctor_cleanup(&obj, args, argc); + } else if (argc || named_params) { + attribute_ctor_cleanup(&obj, args, argc, named_params); zend_throw_error(NULL, "Attribute class %s does not have a constructor, cannot pass arguments", ZSTR_VAL(ce->name)); RETURN_THROWS(); } - attribute_ctor_cleanup(NULL, args, argc); + attribute_ctor_cleanup(NULL, args, argc, named_params); RETURN_COPY_VALUE(&obj); } |