Commit 626f3c3c7c6 for php.net
commit 626f3c3c7c6834f884286e454dbe5a870d125299
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date: Wed Jun 18 18:01:57 2025 +0200
Unify arg info representation for internal and user functions
The arg_info member of zend_function is now always a zend_arg_info*. Before,
it was a zend_internal_arg_info* on internal functions, unless the
ZEND_ACC_USER_ARG_INFO flag was set.
Closes GH-19022
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index 71b6e1fd01e..3ec54f49391 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -49,6 +49,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES
removed. Call zend_wrong_param_count(); followed by RETURN_THROWS();
instead.
. PHP_HAVE_STREAMS macro removed from <php.h>.
+ . zend_function.arg_info is now always a zend_arg_info*. Before, it was a
+ zend_internal_arg_info on internal functions, unless the
+ ZEND_ACC_USER_ARG_INFO flag was set.
========================
2. Build system changes
diff --git a/Zend/tests/closures/gh19653_3.phpt b/Zend/tests/closures/gh19653_3.phpt
new file mode 100644
index 00000000000..7a9ac589182
--- /dev/null
+++ b/Zend/tests/closures/gh19653_3.phpt
@@ -0,0 +1,21 @@
+--TEST--
+GH-19653 (Closure named argument unpacking between temporary closures can cause a crash) - temporary method variation
+--EXTENSIONS--
+zend_test
+--FILE--
+<?php
+
+function usage1($f) {
+ $f(tmpMethodParamName: null);
+}
+
+usage1([new _ZendTestClass(), 'testTmpMethodWithArgInfo']);
+usage1(eval('return function (string $a, string $b): string { return $a.$b; };'));
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Unknown named parameter $tmpMethodParamName in %s:%d
+Stack trace:
+#0 %s(%d): usage1(Object(Closure))
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/unified_arg_infos_001.phpt b/Zend/tests/unified_arg_infos_001.phpt
new file mode 100644
index 00000000000..d6699aa8d8c
--- /dev/null
+++ b/Zend/tests/unified_arg_infos_001.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Declaring non persistent method with arg info
+--EXTENSIONS--
+zend_test
+--FILE--
+<?php
+
+$o = new _ZendTestClass();
+$o->testTmpMethodWithArgInfo(null);
+
+echo new ReflectionFunction($o->testTmpMethodWithArgInfo(...));
+
+?>
+--EXPECT--
+Closure [ <internal> public method testTmpMethodWithArgInfo ] {
+
+ - Parameters [2] {
+ Parameter #0 [ <optional> Foo|Bar|null $tmpMethodParamName = null ]
+ Parameter #1 [ <optional> string $tmpMethodParamWithStringDefaultValue = "tmpMethodParamWithStringDefaultValue" ]
+ }
+}
diff --git a/Zend/zend.c b/Zend/zend.c
index 4d024444a4b..c46c8e9ada8 100644
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@ -38,6 +38,8 @@
#include "zend_call_stack.h"
#include "zend_max_execution_timer.h"
#include "zend_hrtime.h"
+#include "zend_enum.h"
+#include "zend_closures.h"
#include "Optimizer/zend_optimizer.h"
#include "php.h"
#include "php_globals.h"
@@ -1054,6 +1056,7 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
ZVAL_UNDEF(&EG(last_fatal_error_backtrace));
zend_interned_strings_init();
+ zend_object_handlers_startup();
zend_startup_builtin_functions();
zend_register_standard_constants();
zend_register_auto_global(zend_string_init_interned("GLOBALS", sizeof("GLOBALS") - 1, 1), 1, php_auto_globals_create_globals);
@@ -1077,6 +1080,9 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
tsrm_set_new_thread_end_handler(zend_new_thread_end_handler);
tsrm_set_shutdown_handler(zend_interned_strings_dtor);
#endif
+
+ zend_enum_startup();
+ zend_closure_startup();
}
/* }}} */
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index ca001f0e0c4..0881a169dfa 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -20,6 +20,7 @@
*/
#include "zend.h"
+#include "zend_compile.h"
#include "zend_execute.h"
#include "zend_API.h"
#include "zend_hash.h"
@@ -2927,6 +2928,80 @@ static zend_always_inline void zend_normalize_internal_type(zend_type *type) {
} ZEND_TYPE_FOREACH_END();
}
+static void zend_convert_internal_arg_info_type(zend_type *type, bool persistent)
+{
+ if (ZEND_TYPE_HAS_LITERAL_NAME(*type)) {
+ // gen_stubs.php does not support codegen for compound types. As a
+ // temporary workaround, we support union types by splitting
+ // the type name on `|` characters if necessary.
+ const char *class_name = ZEND_TYPE_LITERAL_NAME(*type);
+ type->type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT;
+
+ size_t num_types = 1;
+ const char *p = class_name;
+ while ((p = strchr(p, '|'))) {
+ num_types++;
+ p++;
+ }
+
+ if (num_types == 1) {
+ /* Simple class type */
+ zend_string *str = zend_string_init_interned(class_name, strlen(class_name), persistent);
+ zend_alloc_ce_cache(str);
+ ZEND_TYPE_SET_PTR(*type, str);
+ type->type_mask |= _ZEND_TYPE_NAME_BIT;
+ } else {
+ /* Union type */
+ zend_type_list *list = pemalloc(ZEND_TYPE_LIST_SIZE(num_types), persistent);
+ list->num_types = num_types;
+ ZEND_TYPE_SET_LIST(*type, list);
+ ZEND_TYPE_FULL_MASK(*type) |= _ZEND_TYPE_UNION_BIT;
+
+ const char *start = class_name;
+ uint32_t j = 0;
+ while (true) {
+ const char *end = strchr(start, '|');
+ zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), persistent);
+ zend_alloc_ce_cache(str);
+ list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
+ if (!end) {
+ break;
+ }
+ start = end + 1;
+ j++;
+ }
+ }
+ }
+ if (ZEND_TYPE_IS_ITERABLE_FALLBACK(*type)) {
+ /* Warning generated an extension load warning which is emitted for every test
+ zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable,"
+ " regenerate the argument info via the php-src gen_stub build script");
+ */
+ zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK(
+ ZSTR_KNOWN(ZEND_STR_TRAVERSABLE),
+ (type->type_mask | MAY_BE_ARRAY)
+ );
+ *type = legacy_iterable;
+ }
+}
+
+ZEND_API void zend_convert_internal_arg_info(zend_arg_info *new_arg_info, const zend_internal_arg_info *arg_info, bool is_return_info, bool persistent)
+{
+ if (!is_return_info) {
+ new_arg_info->name = zend_string_init_interned(arg_info->name, strlen(arg_info->name), persistent);
+ if (arg_info->default_value) {
+ new_arg_info->default_value = zend_string_init_interned(arg_info->default_value, strlen(arg_info->default_value), persistent);
+ } else {
+ new_arg_info->default_value = NULL;
+ }
+ } else {
+ new_arg_info->name = NULL;
+ new_arg_info->default_value = NULL;
+ }
+ new_arg_info->type = arg_info->type;
+ zend_convert_internal_arg_info_type(&new_arg_info->type, persistent);
+}
+
/* registers all functions in *library_functions in the function hash */
ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend_function_entry *functions, HashTable *function_table, int type) /* {{{ */
{
@@ -2938,6 +3013,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
int error_type;
zend_string *lowercase_name;
size_t fname_len;
+ const zend_internal_arg_info *internal_arg_info;
if (type==MODULE_PERSISTENT) {
error_type = E_CORE_WARNING;
@@ -2994,7 +3070,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
if (ptr->arg_info) {
zend_internal_function_info *info = (zend_internal_function_info*)ptr->arg_info;
- internal_function->arg_info = (zend_internal_arg_info*)ptr->arg_info+1;
+ internal_arg_info = ptr->arg_info+1;
internal_function->num_args = ptr->num_args;
/* Currently you cannot denote that the function can accept less arguments than num_args */
if (info->required_num_args == (uintptr_t)-1) {
@@ -3024,7 +3100,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
zend_error(E_CORE_WARNING, "Missing arginfo for %s%s%s()",
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
- internal_function->arg_info = NULL;
+ internal_arg_info = NULL;
internal_function->num_args = 0;
internal_function->required_num_args = 0;
}
@@ -3035,13 +3111,11 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
!(internal_function->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
zend_error(E_CORE_WARNING, "%s::__toString() implemented without string return type",
ZSTR_VAL(scope->name));
- internal_function->arg_info = (zend_internal_arg_info *) arg_info_toString + 1;
+ internal_arg_info = (zend_internal_arg_info *) arg_info_toString + 1;
internal_function->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE;
internal_function->num_args = internal_function->required_num_args = 0;
}
-
- zend_set_function_arg_flags((zend_function*)internal_function);
if (ptr->flags & ZEND_ACC_ABSTRACT) {
if (scope) {
/* This is a class that must be abstract itself. Here we set the check info. */
@@ -3106,17 +3180,17 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
}
/* If types of arguments have to be checked */
- if (reg_function->arg_info && num_args) {
+ if (internal_arg_info && num_args) {
uint32_t i;
for (i = 0; i < num_args; i++) {
- zend_internal_arg_info *arg_info = ®_function->arg_info[i];
+ const zend_internal_arg_info *arg_info = &internal_arg_info[i];
ZEND_ASSERT(arg_info->name && "Parameter must have a name");
if (ZEND_TYPE_IS_SET(arg_info->type)) {
reg_function->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
}
#if ZEND_DEBUG
for (uint32_t j = 0; j < i; j++) {
- if (!strcmp(arg_info->name, reg_function->arg_info[j].name)) {
+ if (!strcmp(arg_info->name, internal_arg_info[j].name)) {
zend_error_noreturn(E_CORE_ERROR,
"Duplicate parameter name $%s for function %s%s%s()", arg_info->name,
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
@@ -3126,78 +3200,24 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
}
}
- /* Rebuild arginfos if parameter/property types and/or a return type are used */
- if (reg_function->arg_info &&
- (reg_function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
- /* convert "const char*" class type names into "zend_string*" */
+ /* Convert zend_internal_arg_info to zend_arg_info */
+ if (internal_arg_info) {
uint32_t i;
- zend_internal_arg_info *arg_info = reg_function->arg_info - 1;
- zend_internal_arg_info *new_arg_info;
+ const zend_internal_arg_info *arg_info = internal_arg_info - 1;
+ zend_arg_info *new_arg_info;
/* Treat return type as an extra argument */
num_args++;
- new_arg_info = malloc(sizeof(zend_internal_arg_info) * num_args);
- memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args);
+ new_arg_info = malloc(sizeof(zend_arg_info) * num_args);
reg_function->arg_info = new_arg_info + 1;
for (i = 0; i < num_args; i++) {
- if (ZEND_TYPE_HAS_LITERAL_NAME(new_arg_info[i].type)) {
- // gen_stubs.php does not support codegen for DNF types in arg infos.
- // As a temporary workaround, we split the type name on `|` characters,
- // converting it to an union type if necessary.
- const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type);
- new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT;
-
- size_t num_types = 1;
- const char *p = class_name;
- while ((p = strchr(p, '|'))) {
- num_types++;
- p++;
- }
-
- if (num_types == 1) {
- /* Simple class type */
- zend_string *str = zend_string_init_interned(class_name, strlen(class_name), 1);
- zend_alloc_ce_cache(str);
- ZEND_TYPE_SET_PTR(new_arg_info[i].type, str);
- new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT;
- } else {
- /* Union type */
- zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types));
- list->num_types = num_types;
- ZEND_TYPE_SET_LIST(new_arg_info[i].type, list);
- ZEND_TYPE_FULL_MASK(new_arg_info[i].type) |= _ZEND_TYPE_UNION_BIT;
-
- const char *start = class_name;
- uint32_t j = 0;
- while (true) {
- const char *end = strchr(start, '|');
- zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), 1);
- zend_alloc_ce_cache(str);
- list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
- if (!end) {
- break;
- }
- start = end + 1;
- j++;
- }
- }
- }
- if (ZEND_TYPE_IS_ITERABLE_FALLBACK(new_arg_info[i].type)) {
- /* Warning generated an extension load warning which is emitted for every test
- zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable,"
- " regenerate the argument info via the php-src gen_stub build script");
- */
- zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK(
- ZSTR_KNOWN(ZEND_STR_TRAVERSABLE),
- (new_arg_info[i].type.type_mask | MAY_BE_ARRAY)
- );
- new_arg_info[i].type = legacy_iterable;
- }
-
- zend_normalize_internal_type(&new_arg_info[i].type);
+ zend_convert_internal_arg_info(&new_arg_info[i], &arg_info[i],
+ i == 0, true);
}
}
+ zend_set_function_arg_flags((zend_function*)reg_function);
+
if (scope) {
zend_check_magic_method_implementation(
scope, (zend_function *)reg_function, lowercase_name, E_CORE_ERROR);
@@ -5242,48 +5262,43 @@ static zend_string *try_parse_string(const char *str, size_t len, char quote) {
}
ZEND_API zend_result zend_get_default_from_internal_arg_info(
- zval *default_value_zval, const zend_internal_arg_info *arg_info)
+ zval *default_value_zval, const zend_arg_info *arg_info)
{
- const char *default_value = arg_info->default_value;
+ const zend_string *default_value = arg_info->default_value;
if (!default_value) {
return FAILURE;
}
/* Avoid going through the full AST machinery for some simple and common cases. */
- size_t default_value_len = strlen(default_value);
zend_ulong lval;
- if (default_value_len == sizeof("null")-1
- && !memcmp(default_value, "null", sizeof("null")-1)) {
+ if (zend_string_equals_literal(default_value, "null")) {
ZVAL_NULL(default_value_zval);
return SUCCESS;
- } else if (default_value_len == sizeof("true")-1
- && !memcmp(default_value, "true", sizeof("true")-1)) {
+ } else if (zend_string_equals_literal(default_value, "true")) {
ZVAL_TRUE(default_value_zval);
return SUCCESS;
- } else if (default_value_len == sizeof("false")-1
- && !memcmp(default_value, "false", sizeof("false")-1)) {
+ } else if (zend_string_equals_literal(default_value, "false")) {
ZVAL_FALSE(default_value_zval);
return SUCCESS;
- } else if (default_value_len >= 2
- && (default_value[0] == '\'' || default_value[0] == '"')
- && default_value[default_value_len - 1] == default_value[0]) {
+ } else if (ZSTR_LEN(default_value) >= 2
+ && (ZSTR_VAL(default_value)[0] == '\'' || ZSTR_VAL(default_value)[0] == '"')
+ && ZSTR_VAL(default_value)[ZSTR_LEN(default_value) - 1] == ZSTR_VAL(default_value)[0]) {
zend_string *str = try_parse_string(
- default_value + 1, default_value_len - 2, default_value[0]);
+ ZSTR_VAL(default_value) + 1, ZSTR_LEN(default_value) - 2, ZSTR_VAL(default_value)[0]);
if (str) {
ZVAL_STR(default_value_zval, str);
return SUCCESS;
}
- } else if (default_value_len == sizeof("[]")-1
- && !memcmp(default_value, "[]", sizeof("[]")-1)) {
+ } else if (zend_string_equals_literal(default_value, "[]")) {
ZVAL_EMPTY_ARRAY(default_value_zval);
return SUCCESS;
- } else if (ZEND_HANDLE_NUMERIC_STR(default_value, default_value_len, lval)) {
+ } else if (ZEND_HANDLE_NUMERIC(default_value, lval)) {
ZVAL_LONG(default_value_zval, lval);
return SUCCESS;
}
#if 0
- fprintf(stderr, "Evaluating %s via AST\n", default_value);
+ fprintf(stderr, "Evaluating %s via AST\n", ZSTR_VAL(default_value));
#endif
- return get_default_via_ast(default_value_zval, default_value);
+ return get_default_via_ast(default_value_zval, ZSTR_VAL(default_value));
}
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 1bb47a2e870..c1ccbf13666 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -922,8 +922,12 @@ ZEND_API bool zend_is_iterable(const zval *iterable);
ZEND_API bool zend_is_countable(const zval *countable);
+ZEND_API void zend_convert_internal_arg_info(zend_arg_info *new_arg_info,
+ const zend_internal_arg_info *arg_info, bool is_return_info,
+ bool permanent);
+
ZEND_API zend_result zend_get_default_from_internal_arg_info(
- zval *default_value_zval, const zend_internal_arg_info *arg_info);
+ zval *default_value_zval, const zend_arg_info *arg_info);
END_EXTERN_C()
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index 4ecb6b2c493..05b68620448 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -510,7 +510,7 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {
* ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */
invoke->type = ZEND_INTERNAL_FUNCTION;
invoke->internal_function.fn_flags =
- ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags);
+ ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | ZEND_ACC_NEVER_CACHE | (closure->func.common.fn_flags & keep_flags);
if (closure->func.type != ZEND_INTERNAL_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
invoke->internal_function.fn_flags |=
ZEND_ACC_USER_ARG_INFO;
@@ -618,7 +618,6 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
zval val;
struct _zend_arg_info *arg_info = closure->func.common.arg_info;
HashTable *debug_info;
- bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
*is_temp = 1;
@@ -694,15 +693,9 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
zend_string *name;
zval info;
ZEND_ASSERT(arg_info->name && "Argument should have name");
- if (zstr_args) {
- name = zend_strpprintf(0, "%s$%s",
- ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
- ZSTR_VAL(arg_info->name));
- } else {
- name = zend_strpprintf(0, "%s$%s",
- ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
- ((zend_internal_arg_info*)arg_info)->name);
- }
+ name = zend_strpprintf(0, "%s$%s",
+ ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
+ ZSTR_VAL(arg_info->name));
ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "<optional>" : "<required>"));
zend_hash_update(Z_ARRVAL(val), name, &info);
zend_string_release_ex(name, 0);
@@ -885,8 +878,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
}
/* }}} */
-/* __call and __callStatic name the arguments "$arguments" in the docs. */
-static zend_internal_arg_info trampoline_arg_info[] = {ZEND_ARG_VARIADIC_TYPE_INFO(false, arguments, IS_MIXED, false)};
+static zend_arg_info trampoline_arg_info[1];
void zend_closure_from_frame(zval *return_value, const zend_execute_data *call) { /* {{{ */
zval instance;
@@ -951,3 +943,11 @@ void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {
ZVAL_COPY_VALUE(var, val);
}
/* }}} */
+
+void zend_closure_startup(void)
+{
+ /* __call and __callStatic name the arguments "$arguments" in the docs. */
+ trampoline_arg_info[0].name = zend_string_init_interned("arguments", strlen("arguments"), true);
+ trampoline_arg_info[0].type = (zend_type)ZEND_TYPE_INIT_CODE(IS_MIXED, false, _ZEND_ARG_INFO_FLAGS(false, 1, 0));
+ trampoline_arg_info[0].default_value = NULL;
+}
diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h
index 8bea4ffb051..a118044c6e2 100644
--- a/Zend/zend_closures.h
+++ b/Zend/zend_closures.h
@@ -28,6 +28,7 @@ BEGIN_EXTERN_C()
#define ZEND_CLOSURE_OBJECT(op_array) \
((zend_object*)((char*)(op_array) - sizeof(zend_object)))
+void zend_closure_startup(void);
void zend_register_closure_ce(void);
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var);
void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val);
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 50ba8029873..5eba2ec1366 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -3688,21 +3688,10 @@ static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
static uint32_t zend_get_arg_num(const zend_function *fn, const zend_string *arg_name) {
// TODO: Caching?
- if (fn->type == ZEND_USER_FUNCTION) {
- for (uint32_t i = 0; i < fn->common.num_args; i++) {
- const zend_arg_info *arg_info = &fn->op_array.arg_info[i];
- if (zend_string_equals(arg_info->name, arg_name)) {
- return i + 1;
- }
- }
- } else {
- ZEND_ASSERT(fn->common.num_args == 0 || fn->internal_function.arg_info);
- for (uint32_t i = 0; i < fn->common.num_args; i++) {
- const zend_internal_arg_info *arg_info = &fn->internal_function.arg_info[i];
- size_t len = strlen(arg_info->name);
- if (zend_string_equals_cstr(arg_name, arg_info->name, len)) {
- return i + 1;
- }
+ for (uint32_t i = 0; i < fn->common.num_args; i++) {
+ zend_arg_info *arg_info = &fn->op_array.arg_info[i];
+ if (zend_string_equals(arg_info->name, arg_name)) {
+ return i + 1;
}
}
@@ -4702,7 +4691,7 @@ static uint32_t zend_compile_frameless_icall_ex(znode *result, const zend_ast_li
if (i < args->children) {
zend_compile_expr(&arg_zvs[i], args->child[i]);
} else {
- const zend_internal_arg_info *arg_info = (const zend_internal_arg_info *)&fbc->common.arg_info[i];
+ const zend_arg_info *arg_info = &fbc->common.arg_info[i];
arg_zvs[i].op_type = IS_CONST;
if (zend_get_default_from_internal_arg_info(&arg_zvs[i].u.constant, arg_info) == FAILURE) {
ZEND_UNREACHABLE();
@@ -7934,6 +7923,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
arg_info = &arg_infos[i];
arg_info->name = zend_string_copy(name);
arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0);
+ arg_info->default_value = NULL;
if (attributes_ast) {
zend_compile_attributes(
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 86fab4b57de..d2a3b47bf92 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -592,7 +592,7 @@ typedef struct _zend_internal_function {
zend_function *prototype;
uint32_t num_args;
uint32_t required_num_args;
- zend_internal_arg_info *arg_info;
+ zend_arg_info *arg_info;
HashTable *attributes;
ZEND_MAP_PTR_DEF(void **, run_time_cache);
zend_string *doc_comment;
@@ -976,7 +976,8 @@ ZEND_API ZEND_COLD void zend_user_exception_handler(void);
} \
} while (0)
-void zend_free_internal_arg_info(zend_internal_function *function);
+ZEND_API void zend_free_internal_arg_info(zend_internal_function *function,
+ bool permanent);
ZEND_API void destroy_zend_function(zend_function *function);
ZEND_API void zend_function_dtor(zval *zv);
ZEND_API void destroy_zend_class(zval *zv);
diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c
index ae9e7b70121..00fbab22a05 100644
--- a/Zend/zend_enum.c
+++ b/Zend/zend_enum.c
@@ -36,6 +36,10 @@ ZEND_API zend_class_entry *zend_ce_unit_enum;
ZEND_API zend_class_entry *zend_ce_backed_enum;
ZEND_API zend_object_handlers zend_enum_object_handlers;
+static zend_arg_info zarginfo_class_UnitEnum_cases[sizeof(arginfo_class_UnitEnum_cases)/sizeof(zend_internal_arg_info)];
+static zend_arg_info zarginfo_class_BackedEnum_from[sizeof(arginfo_class_BackedEnum_from)/sizeof(zend_internal_arg_info)];
+static zend_arg_info zarginfo_class_BackedEnum_tryFrom[sizeof(arginfo_class_BackedEnum_tryFrom)/sizeof(zend_internal_arg_info)];
+
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *backing_value_zv)
{
zend_object *zobj = zend_objects_new(ce);
@@ -446,7 +450,7 @@ void zend_enum_register_funcs(zend_class_entry *ce)
cases_function->function_name = ZSTR_KNOWN(ZEND_STR_CASES);
cases_function->fn_flags = fn_flags;
cases_function->doc_comment = NULL;
- cases_function->arg_info = (zend_internal_arg_info *) (arginfo_class_UnitEnum_cases + 1);
+ cases_function->arg_info = zarginfo_class_UnitEnum_cases + 1;
zend_enum_register_func(ce, ZEND_STR_CASES, cases_function);
if (ce->enum_backing_type != IS_UNDEF) {
@@ -457,7 +461,7 @@ void zend_enum_register_funcs(zend_class_entry *ce)
from_function->doc_comment = NULL;
from_function->num_args = 1;
from_function->required_num_args = 1;
- from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_from + 1);
+ from_function->arg_info = zarginfo_class_BackedEnum_from + 1;
zend_enum_register_func(ce, ZEND_STR_FROM, from_function);
zend_internal_function *try_from_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
@@ -467,7 +471,7 @@ void zend_enum_register_funcs(zend_class_entry *ce)
try_from_function->doc_comment = NULL;
try_from_function->num_args = 1;
try_from_function->required_num_args = 1;
- try_from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_tryFrom + 1);
+ try_from_function->arg_info = zarginfo_class_BackedEnum_tryFrom + 1;
zend_enum_register_func(ce, ZEND_STR_TRYFROM_LOWERCASE, try_from_function);
}
}
@@ -633,3 +637,16 @@ ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *
zend_class_constant *c = zend_hash_str_find_ptr(CE_CONSTANTS_TABLE(ce), name, strlen(name));
return zend_enum_case_from_class_constant(c);
}
+
+void zend_enum_startup(void)
+{
+ for (size_t i = 0; i < sizeof(zarginfo_class_UnitEnum_cases)/sizeof(zend_arg_info); i++) {
+ zend_convert_internal_arg_info(&zarginfo_class_UnitEnum_cases[i], &arginfo_class_UnitEnum_cases[i], i == 0, true);
+ }
+ for (size_t i = 0; i < sizeof(zarginfo_class_BackedEnum_from)/sizeof(zend_arg_info); i++) {
+ zend_convert_internal_arg_info(&zarginfo_class_BackedEnum_from[i], &arginfo_class_BackedEnum_from[i], i == 0, true);
+ }
+ for (size_t i = 0; i < sizeof(zarginfo_class_BackedEnum_tryFrom)/sizeof(zend_arg_info); i++) {
+ zend_convert_internal_arg_info(&zarginfo_class_BackedEnum_tryFrom[i], &arginfo_class_BackedEnum_tryFrom[i], i == 0, true);
+ }
+}
diff --git a/Zend/zend_enum.h b/Zend/zend_enum.h
index 7b3b0184b4e..d6c82018947 100644
--- a/Zend/zend_enum.h
+++ b/Zend/zend_enum.h
@@ -30,6 +30,7 @@ extern ZEND_API zend_class_entry *zend_ce_unit_enum;
extern ZEND_API zend_class_entry *zend_ce_backed_enum;
extern ZEND_API zend_object_handlers zend_enum_object_handlers;
+void zend_enum_startup(void);
void zend_register_enum_ce(void);
void zend_enum_add_interfaces(zend_class_entry *ce);
zend_result zend_enum_build_backed_enum_table(zend_class_entry *ce);
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index d411dcbc3b9..518cbb98fc0 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -136,8 +136,7 @@ static ZEND_FUNCTION(pass)
{
}
-ZEND_BEGIN_ARG_INFO_EX(zend_pass_function_arg_info, 0, 0, 0)
-ZEND_END_ARG_INFO()
+static zend_arg_info zend_pass_function_arg_info[1] = {0};
ZEND_API const zend_internal_function zend_pass_function = {
ZEND_INTERNAL_FUNCTION, /* type */
@@ -148,7 +147,7 @@ ZEND_API const zend_internal_function zend_pass_function = {
NULL, /* prototype */
0, /* num_args */
0, /* required_num_args */
- (zend_internal_arg_info *) zend_pass_function_arg_info + 1, /* arg_info */
+ zend_pass_function_arg_info + 1, /* arg_info */
NULL, /* attributes */
NULL, /* run_time_cache */
NULL, /* doc_comment */
@@ -1480,7 +1479,7 @@ static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, con
ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *ret)
{
- const zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1;
+ const zend_arg_info *ret_info = zf->internal_function.arg_info - 1;
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_VOID) {
if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) {
@@ -5474,28 +5473,17 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
// TODO: Use a hash table?
uint32_t num_args = fbc->common.num_args;
- if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)
- || EXPECTED(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
- for (uint32_t i = 0; i < num_args; i++) {
- const zend_arg_info *arg_info = &fbc->common.arg_info[i];
- if (zend_string_equals(arg_name, arg_info->name)) {
- if (fbc->type == ZEND_USER_FUNCTION && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) {
- *cache_slot = unique_id;
- *(uintptr_t *)(cache_slot + 1) = i;
- }
- return i;
- }
- }
- } else {
- ZEND_ASSERT(num_args == 0 || fbc->internal_function.arg_info);
- for (uint32_t i = 0; i < num_args; i++) {
- const zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i];
- size_t len = strlen(arg_info->name);
- if (zend_string_equals_cstr(arg_name, arg_info->name, len)) {
+ for (uint32_t i = 0; i < num_args; i++) {
+ const zend_arg_info *arg_info = &fbc->common.arg_info[i];
+ if (zend_string_equals(arg_name, arg_info->name)) {
+ if ((fbc->type == ZEND_USER_FUNCTION
+ && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)))
+ || (fbc->type == ZEND_INTERNAL_FUNCTION
+ && !(fbc->common.fn_flags & ZEND_ACC_NEVER_CACHE))) {
*cache_slot = unique_id;
*(uintptr_t *)(cache_slot + 1) = i;
- return i;
}
+ return i;
}
}
@@ -5503,7 +5491,7 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
if ((fbc->type == ZEND_USER_FUNCTION
&& (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)))
|| (fbc->type == ZEND_INTERNAL_FUNCTION
- && !(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO))) {
+ && !(fbc->common.fn_flags & ZEND_ACC_NEVER_CACHE))) {
*cache_slot = unique_id;
*(uintptr_t *)(cache_slot + 1) = fbc->common.num_args;
}
@@ -5661,7 +5649,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *cal
continue;
}
- zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i];
+ zend_arg_info *arg_info = &fbc->internal_function.arg_info[i];
if (i < fbc->common.required_num_args) {
zend_execute_data *old = start_fake_frame(call, NULL);
zend_argument_error(zend_ce_argument_count_error, i + 1, "not passed");
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 660975f9bc1..e134d3d496b 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -652,11 +652,7 @@ ZEND_API const char *get_function_arg_name(const zend_function *func, uint32_t a
return NULL;
}
- if (func->type == ZEND_USER_FUNCTION || (func->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
- return ZSTR_VAL(func->common.arg_info[arg_num - 1].name);
- } else {
- return ((zend_internal_arg_info*) func->common.arg_info)[arg_num - 1].name;
- }
+ return ZSTR_VAL(func->common.arg_info[arg_num - 1].name);
}
/* }}} */
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 64448bc5355..3c3931cdca1 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -953,18 +953,14 @@ static ZEND_COLD zend_string *zend_get_function_declaration(
}
smart_str_appendc(&str, '$');
- if (fptr->type == ZEND_INTERNAL_FUNCTION) {
- smart_str_appends(&str, ((zend_internal_arg_info*)arg_info)->name);
- } else {
- smart_str_appendl(&str, ZSTR_VAL(arg_info->name), ZSTR_LEN(arg_info->name));
- }
+ smart_str_append(&str, arg_info->name);
if (i >= required && !ZEND_ARG_IS_VARIADIC(arg_info)) {
smart_str_appends(&str, " = ");
if (fptr->type == ZEND_INTERNAL_FUNCTION) {
- if (((zend_internal_arg_info*)arg_info)->default_value) {
- smart_str_appends(&str, ((zend_internal_arg_info*)arg_info)->default_value);
+ if (arg_info->default_value) {
+ smart_str_append(&str, arg_info->default_value);
} else {
smart_str_appends(&str, "<default>");
}
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index 470fb76ec14..88b7b1112d7 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -46,6 +46,9 @@
#define IN_ISSET ZEND_GUARD_PROPERTY_ISSET
#define IN_HOOK ZEND_GUARD_PROPERTY_HOOK
+static zend_arg_info zend_call_trampoline_arginfo[1] = {{0}};
+static zend_arg_info zend_property_hook_arginfo[1] = {{0}};
+
static zend_always_inline bool zend_objects_check_stack_limit(void)
{
#ifdef ZEND_CHECK_STACK_LIMIT
@@ -1782,9 +1785,6 @@ ZEND_API zend_function *zend_get_property_hook_trampoline(
const zend_property_info *prop_info,
zend_property_hook_kind kind, zend_string *prop_name)
{
- static const zend_internal_arg_info arg_info[2] = {
- { .name = "value" }
- };
zend_function *func;
if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
func = &EG(trampoline);
@@ -1810,7 +1810,7 @@ ZEND_API zend_function *zend_get_property_hook_trampoline(
func->common.scope = prop_info->ce;
func->common.prototype = NULL;
func->common.prop_info = prop_info;
- func->common.arg_info = (zend_arg_info *) arg_info;
+ func->common.arg_info = zend_property_hook_arginfo;
func->internal_function.handler = kind == ZEND_PROPERTY_HOOK_GET
? ZEND_FN(zend_parent_hook_get_trampoline)
: ZEND_FN(zend_parent_hook_set_trampoline);
@@ -2574,3 +2574,8 @@ ZEND_API const zend_object_handlers std_object_handlers = {
zend_std_compare_objects, /* compare */
NULL, /* get_properties_for */
};
+
+void zend_object_handlers_startup(void) {
+ zend_call_trampoline_arginfo[0].name = ZSTR_KNOWN(ZEND_STR_ARGS);
+ zend_property_hook_arginfo[0].name = ZSTR_KNOWN(ZEND_STR_VALUE);
+}
diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h
index 59277c09d80..3e922343eb1 100644
--- a/Zend/zend_object_handlers.h
+++ b/Zend/zend_object_handlers.h
@@ -334,6 +334,8 @@ ZEND_API zend_function *zend_get_property_hook_trampoline(
ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_property_info *prop_info);
+void zend_object_handlers_startup(void);
+
#define zend_release_properties(ht) do { \
if (ht) { \
zend_array_release(ht); \
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index 1962c7b5a56..5e9e7b20d86 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -124,21 +124,32 @@ ZEND_API void zend_type_release(zend_type type, bool persistent) {
}
}
-void zend_free_internal_arg_info(zend_internal_function *function) {
- if ((function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
- function->arg_info) {
+ZEND_API void zend_free_internal_arg_info(zend_internal_function *function,
+ bool persistent) {
+ if (function->arg_info) {
+ ZEND_ASSERT((persistent || (function->fn_flags & ZEND_ACC_NEVER_CACHE))
+ && "Functions with non-persistent arg_info must be flagged ZEND_ACC_NEVER_CACHE");
uint32_t i;
uint32_t num_args = function->num_args + 1;
- zend_internal_arg_info *arg_info = function->arg_info - 1;
+ zend_arg_info *arg_info = function->arg_info - 1;
if (function->fn_flags & ZEND_ACC_VARIADIC) {
num_args++;
}
for (i = 0 ; i < num_args; i++) {
- zend_type_release(arg_info[i].type, /* persistent */ true);
+ bool is_return_info = i == 0;
+ if (!is_return_info) {
+ zend_string_release_ex(arg_info[i].name, persistent);
+ if (arg_info[i].default_value) {
+ zend_string_release_ex(arg_info[i].default_value,
+ persistent);
+ }
+ }
+ zend_type_release(arg_info[i].type, persistent);
}
- free(arg_info);
+
+ pefree(arg_info, persistent);
}
}
@@ -157,7 +168,7 @@ ZEND_API void zend_function_dtor(zval *zv)
/* For methods this will be called explicitly. */
if (!function->common.scope) {
- zend_free_internal_arg_info(&function->internal_function);
+ zend_free_internal_arg_info(&function->internal_function, true);
if (function->common.attributes) {
zend_hash_release(function->common.attributes);
@@ -474,12 +485,9 @@ ZEND_API void destroy_zend_class(zval *zv)
zend_hash_destroy(&ce->properties_info);
zend_string_release_ex(ce->name, 1);
- /* TODO: eliminate this loop for classes without functions with arg_info / attributes */
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, fn) {
if (fn->common.scope == ce) {
- if (fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) {
- zend_free_internal_arg_info(&fn->internal_function);
- }
+ zend_free_internal_arg_info(&fn->internal_function, true);
if (fn->common.attributes) {
zend_hash_release(fn->common.attributes);
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index d1d49ca391a..7acb14b778f 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -663,8 +663,7 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int
if (Z_FUNC(p->val)->common.function_name) {
Z_FUNC(p->val)->common.function_name = new_interned_string(Z_FUNC(p->val)->common.function_name);
}
- if (Z_FUNC(p->val)->common.arg_info &&
- (Z_FUNC(p->val)->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
+ if (Z_FUNC(p->val)->common.arg_info) {
uint32_t i;
uint32_t num_args = Z_FUNC(p->val)->common.num_args + 1;
zend_arg_info *arg_info = Z_FUNC(p->val)->common.arg_info - 1;
@@ -673,6 +672,12 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int
num_args++;
}
for (i = 0 ; i < num_args; i++) {
+ if (i > 0) {
+ arg_info[i].name = new_interned_string(arg_info[i].name);
+ if (arg_info[i].default_value) {
+ arg_info[i].default_value = new_interned_string(arg_info[i].default_value);
+ }
+ }
accel_copy_permanent_list_types(new_interned_string, arg_info[i].type);
}
}
@@ -714,6 +719,24 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int
if (Z_FUNC(q->val)->common.function_name) {
Z_FUNC(q->val)->common.function_name = new_interned_string(Z_FUNC(q->val)->common.function_name);
}
+ if (Z_FUNC(q->val)->common.scope == ce) {
+ uint32_t i;
+ uint32_t num_args = Z_FUNC(q->val)->common.num_args + 1;
+ zend_arg_info *arg_info = Z_FUNC(q->val)->common.arg_info - 1;
+
+ if (Z_FUNC(q->val)->common.fn_flags & ZEND_ACC_VARIADIC) {
+ num_args++;
+ }
+ for (i = 0 ; i < num_args; i++) {
+ if (i > 0) {
+ arg_info[i].name = new_interned_string(arg_info[i].name);
+ if (arg_info[i].default_value) {
+ arg_info[i].default_value = new_interned_string(arg_info[i].default_value);
+ }
+ }
+ accel_copy_permanent_list_types(new_interned_string, arg_info[i].type);
+ }
+ }
} ZEND_HASH_FOREACH_END();
ZEND_HASH_MAP_FOREACH_BUCKET(&ce->constants_table, q) {
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 9dc6ea1c3b0..10be237d06b 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -493,7 +493,7 @@ static bool zend_jit_needs_arg_dtor(const zend_function *func, uint32_t arg_num,
&& func->type == ZEND_INTERNAL_FUNCTION
&& (func->internal_function.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0
&& arg_num < func->internal_function.num_args) {
- const zend_internal_arg_info *arg_info = &func->internal_function.arg_info[arg_num];
+ const zend_arg_info *arg_info = &func->internal_function.arg_info[arg_num];
if (ZEND_ARG_SEND_MODE(arg_info) == ZEND_SEND_BY_VAL
&& ZEND_TYPE_IS_SET(arg_info->type)
diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c
index beb3665c4d9..34f19e364fa 100644
--- a/ext/pdo/pdo_dbh.c
+++ b/ext/pdo/pdo_dbh.c
@@ -1321,6 +1321,7 @@ static void cls_method_dtor(zval *el) /* {{{ */ {
if (func->common.attributes) {
zend_hash_release(func->common.attributes);
}
+ zend_free_internal_arg_info(&func->internal_function, false);
efree(func);
}
/* }}} */
@@ -1336,6 +1337,7 @@ static void cls_method_pdtor(zval *el) /* {{{ */ {
if (func->common.attributes) {
zend_hash_release(func->common.attributes);
}
+ zend_free_internal_arg_info(&func->internal_function, true);
pefree(func, 1);
}
/* }}} */
@@ -1408,7 +1410,19 @@ bool pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind)
if (funcs->arg_info) {
zend_internal_function_info *info = (zend_internal_function_info*)funcs->arg_info;
- func.arg_info = (zend_internal_arg_info*)funcs->arg_info + 1;
+ uint32_t num_arg_info = 1 + funcs->num_args;
+ if (func.fn_flags & ZEND_ACC_VARIADIC) {
+ num_arg_info++;
+ }
+
+ zend_arg_info *arg_info = safe_pemalloc(num_arg_info,
+ sizeof(zend_arg_info), 0, dbh->is_persistent);
+ for (uint32_t i = 0; i < num_arg_info; i++) {
+ zend_convert_internal_arg_info(&arg_info[i],
+ &funcs->arg_info[i], i == 0, dbh->is_persistent);
+ }
+
+ func.arg_info = arg_info + 1;
func.num_args = funcs->num_args;
if (info->required_num_args == (uint32_t)-1) {
func.required_num_args = funcs->num_args;
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index d6e55c982b4..c6f681ee227 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -763,11 +763,6 @@ static void format_default_value(smart_str *str, zval *value) {
}
}
-static inline bool has_internal_arg_info(const zend_function *fptr) {
- return fptr->type == ZEND_INTERNAL_FUNCTION
- && !(fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO);
-}
-
/* {{{ _parameter_string */
static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_arg_info *arg_info, uint32_t offset, bool required, char* indent)
{
@@ -789,17 +784,15 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
if (ZEND_ARG_IS_VARIADIC(arg_info)) {
smart_str_appends(str, "...");
}
- smart_str_append_printf(str, "$%s", has_internal_arg_info(fptr)
- ? ((zend_internal_arg_info*)arg_info)->name : ZSTR_VAL(arg_info->name));
+ smart_str_append_printf(str, "$%s", ZSTR_VAL(arg_info->name));
if (!required && !ZEND_ARG_IS_VARIADIC(arg_info)) {
if (fptr->type == ZEND_INTERNAL_FUNCTION) {
smart_str_appends(str, " = ");
/* TODO: We don't have a way to fetch the default value for an internal function
* with userland arg info. */
- if (has_internal_arg_info(fptr)
- && ((zend_internal_arg_info*)arg_info)->default_value) {
- smart_str_appends(str, ((zend_internal_arg_info*)arg_info)->default_value);
+ if (arg_info->default_value) {
+ smart_str_append(str, arg_info->default_value);
} else {
smart_str_appends(str, "<default>");
}
@@ -1463,11 +1456,7 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje
}
prop_name = reflection_prop_name(object);
- if (has_internal_arg_info(fptr)) {
- ZVAL_STRING(prop_name, ((zend_internal_arg_info*)arg_info)->name);
- } else {
- ZVAL_STR_COPY(prop_name, arg_info->name);
- }
+ ZVAL_STR_COPY(prop_name, arg_info->name);
}
/* }}} */
@@ -1653,8 +1642,7 @@ static zend_result get_parameter_default(zval *result, parameter_reference *para
/* We don't have a way to determine the default value for this case right now. */
return FAILURE;
}
- return zend_get_default_from_internal_arg_info(
- result, (zend_internal_arg_info *) param->arg_info);
+ return zend_get_default_from_internal_arg_info(result, param->arg_info);
} else {
zval *default_value = get_default_from_recv((zend_op_array *) param->fptr, param->offset);
if (!default_value) {
@@ -2590,22 +2578,11 @@ ZEND_METHOD(ReflectionParameter, __construct)
uint32_t i;
position = -1;
- if (has_internal_arg_info(fptr)) {
- for (i = 0; i < num_args; i++) {
- if (arg_info[i].name) {
- if (strcmp(((zend_internal_arg_info*)arg_info)[i].name, ZSTR_VAL(arg_name)) == 0) {
- position = i;
- break;
- }
- }
- }
- } else {
- for (i = 0; i < num_args; i++) {
- if (arg_info[i].name) {
- if (zend_string_equals(arg_name, arg_info[i].name)) {
- position = i;
- break;
- }
+ for (i = 0; i < num_args; i++) {
+ if (arg_info[i].name) {
+ if (zend_string_equals(arg_name, arg_info[i].name)) {
+ position = i;
+ break;
}
}
}
@@ -2646,11 +2623,7 @@ ZEND_METHOD(ReflectionParameter, __construct)
prop_name = reflection_prop_name(object);
zval_ptr_dtor(prop_name);
- if (has_internal_arg_info(fptr)) {
- ZVAL_STRING(prop_name, ((zend_internal_arg_info*)arg_info)[position].name);
- } else {
- ZVAL_STR_COPY(prop_name, arg_info[position].name);
- }
+ ZVAL_STR_COPY(prop_name, arg_info[position].name);
return;
failure:
@@ -2689,11 +2662,7 @@ ZEND_METHOD(ReflectionParameter, getName)
ZEND_PARSE_PARAMETERS_NONE();
GET_REFLECTION_OBJECT_PTR(param);
- if (has_internal_arg_info(param->fptr)) {
- RETURN_STRING(((zend_internal_arg_info *) param->arg_info)->name);
- } else {
- RETURN_STR_COPY(param->arg_info->name);
- }
+ RETURN_STR_COPY(param->arg_info->name);
}
/* }}} */
@@ -2947,8 +2916,7 @@ ZEND_METHOD(ReflectionParameter, isDefaultValueAvailable)
GET_REFLECTION_OBJECT_PTR(param);
if (param->fptr->type == ZEND_INTERNAL_FUNCTION) {
- RETURN_BOOL(!(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)
- && ((zend_internal_arg_info*) (param->arg_info))->default_value);
+ RETURN_BOOL(param->arg_info->default_value);
} else {
zval *default_value = get_default_from_recv((zend_op_array *)param->fptr, param->offset);
RETURN_BOOL(default_value != NULL);
diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c
index 4e06b2106ce..fd1a51c5776 100644
--- a/ext/zend_test/test.c
+++ b/ext/zend_test/test.c
@@ -37,10 +37,12 @@
#include "Zend/Optimizer/zend_optimizer.h"
#include "Zend/zend_alloc.h"
#include "test_arginfo.h"
+#include "tmp_methods_arginfo.h"
#include "zend_call_stack.h"
#include "zend_exceptions.h"
#include "zend_mm_custom_handlers.h"
#include "ext/uri/php_uri.h"
+#include "zend_observer.h"
#if defined(HAVE_LIBXML) && !defined(PHP_WIN32)
# include <libxml/globals.h>
@@ -1030,16 +1032,38 @@ static ZEND_FUNCTION(zend_test_log_err_debug)
php_log_err_with_severity(ZSTR_VAL(str), LOG_DEBUG);
}
+typedef struct _zend_test_object {
+ zend_internal_function *tmp_method;
+ zend_object std;
+} zend_test_object;
+
static zend_object *zend_test_class_new(zend_class_entry *class_type)
{
- zend_object *obj = zend_objects_new(class_type);
- object_properties_init(obj, class_type);
- obj->handlers = &zend_test_class_handlers;
- return obj;
+ zend_test_object *intern = zend_object_alloc(sizeof(zend_test_object), class_type);
+ zend_object_std_init(&intern->std, class_type);
+ object_properties_init(&intern->std, class_type);
+ return &intern->std;
+}
+
+static void zend_test_class_free_obj(zend_object *object)
+{
+ zend_test_object *intern = (zend_test_object*)((char*)object - XtOffsetOf(zend_test_object, std));
+
+ if (intern->tmp_method) {
+ zend_internal_function *func = intern->tmp_method;
+ intern->tmp_method = NULL;
+ zend_string_release_ex(func->function_name, 0);
+ zend_free_internal_arg_info(func, false);
+ efree(func);
+ }
+
+ zend_object_std_dtor(object);
}
static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key)
{
+ zend_test_object *intern = (zend_test_object*)((char*)(*object) - XtOffsetOf(zend_test_object, std));
+
if (zend_string_equals_literal_ci(name, "test")) {
zend_internal_function *fptr;
@@ -1058,6 +1082,41 @@ static zend_function *zend_test_class_method_get(zend_object **object, zend_stri
fptr->handler = ZEND_FN(zend_test_func);
fptr->doc_comment = NULL;
+ return (zend_function*)fptr;
+ } else if (zend_string_equals_literal_ci(name, "testTmpMethodWithArgInfo")) {
+ if (intern->tmp_method) {
+ return (zend_function*)intern->tmp_method;
+ }
+
+ const zend_function_entry *entry = &class_ZendTestTmpMethods_methods[0];
+ zend_internal_function *fptr = emalloc(sizeof(zend_internal_function));
+ memset(fptr, 0, sizeof(zend_internal_function));
+ fptr->type = ZEND_INTERNAL_FUNCTION;
+ fptr->handler = entry->handler;
+ fptr->function_name = zend_string_init(entry->fname, strlen(entry->fname), false);
+ fptr->scope = intern->std.ce;
+ fptr->prototype = NULL;
+ fptr->T = ZEND_OBSERVER_ENABLED;
+ fptr->fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_NEVER_CACHE;
+
+ zend_internal_function_info *info = (zend_internal_function_info*)entry->arg_info;
+
+ uint32_t num_arg_info = 1 + entry->num_args;
+ zend_arg_info *arg_info = safe_emalloc(num_arg_info, sizeof(zend_arg_info), 0);
+ for (uint32_t i = 0; i < num_arg_info; i++) {
+ zend_convert_internal_arg_info(&arg_info[i], &entry->arg_info[i], i == 0, false);
+ }
+
+ fptr->arg_info = arg_info + 1;
+ fptr->num_args = entry->num_args;
+ if (info->required_num_args == (uint32_t)-1) {
+ fptr->required_num_args = entry->num_args;
+ } else {
+ fptr->required_num_args = info->required_num_args;
+ }
+
+ intern->tmp_method = fptr;
+
return (zend_function*)fptr;
}
return zend_std_get_method(object, name, key);
@@ -1145,6 +1204,18 @@ static ZEND_METHOD(_ZendTestClass, variadicTest) {
object_init_ex(return_value, zend_get_called_scope(execute_data));
}
+ZEND_METHOD(ZendTestTmpMethods, testTmpMethodWithArgInfo)
+{
+ zend_object *obj;
+ zend_string *str;
+
+ ZEND_PARSE_PARAMETERS_START(0, 2);
+ Z_PARAM_OPTIONAL;
+ Z_PARAM_OBJ_OR_NULL(obj);
+ Z_PARAM_STR(str);
+ ZEND_PARSE_PARAMETERS_END();
+}
+
static ZEND_METHOD(_ZendTestChildClass, returnsThrowable)
{
ZEND_PARSE_PARAMETERS_NONE();
@@ -1450,11 +1521,14 @@ PHP_MINIT_FUNCTION(zend_test)
register_ZendTestClass_dnf_property(zend_test_class);
zend_test_class->create_object = zend_test_class_new;
zend_test_class->get_static_method = zend_test_class_static_method_get;
+ zend_test_class->default_object_handlers = &zend_test_class_handlers;
zend_test_child_class = register_class__ZendTestChildClass(zend_test_class);
memcpy(&zend_test_class_handlers, &std_object_handlers, sizeof(zend_object_handlers));
zend_test_class_handlers.get_method = zend_test_class_method_get;
+ zend_test_class_handlers.free_obj = zend_test_class_free_obj;
+ zend_test_class_handlers.offset = XtOffsetOf(zend_test_object, std);
zend_test_gen_stub_flag_compatibility_test = register_class_ZendTestGenStubFlagCompatibilityTest();
diff --git a/ext/zend_test/tmp_methods.stub.php b/ext/zend_test/tmp_methods.stub.php
new file mode 100644
index 00000000000..af479c7d542
--- /dev/null
+++ b/ext/zend_test/tmp_methods.stub.php
@@ -0,0 +1,11 @@
+<?php
+
+/** @generate-function-entries */
+
+/**
+ * These methods will be added to a class at runtime
+ * @undocumentable
+ */
+class ZendTestTmpMethods {
+ public function testTmpMethodWithArgInfo(Foo|Bar|null $tmpMethodParamName = null, string $tmpMethodParamWithStringDefaultValue = "tmpMethodParamWithStringDefaultValue"): void {}
+}
diff --git a/ext/zend_test/tmp_methods_arginfo.h b/ext/zend_test/tmp_methods_arginfo.h
new file mode 100644
index 00000000000..92598a34ad8
Binary files /dev/null and b/ext/zend_test/tmp_methods_arginfo.h differ
diff --git a/sapi/phpdbg/phpdbg_frame.c b/sapi/phpdbg/phpdbg_frame.c
index a8f1bf01433..01b19de80a9 100644
--- a/sapi/phpdbg/phpdbg_frame.c
+++ b/sapi/phpdbg/phpdbg_frame.c
@@ -34,11 +34,7 @@ static inline void phpdbg_append_individual_arg(smart_str *s, uint32_t i, zend_f
}
if (i < func->common.num_args) {
if (arginfo) {
- if (func->type == ZEND_INTERNAL_FUNCTION) {
- arg_name = (char *) ((zend_internal_arg_info *) &arginfo[i])->name;
- } else {
- arg_name = ZSTR_VAL(arginfo[i].name);
- }
+ arg_name = ZSTR_VAL(arginfo[i].name);
}
smart_str_appends(s, arg_name ? arg_name : "?");
smart_str_appendc(s, '=');
@@ -213,11 +209,7 @@ static void phpdbg_dump_prototype(zval *tmp) /* {{{ */
char *arg_name = NULL;
if (arginfo) {
- if (func->type == ZEND_INTERNAL_FUNCTION) {
- arg_name = (char *)((zend_internal_arg_info *)&arginfo[j])->name;
- } else {
- arg_name = ZSTR_VAL(arginfo[j].name);
- }
+ arg_name = ZSTR_VAL(arginfo[j].name);
}
if (!is_variadic) {