From 3ccd8f2a30d7e0c7f88b93bcfbed5b14f32f5c3d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 4 Aug 2026 22:52:50 +0100 Subject: [PATCH 1/2] Zend: refactor zend_parse_arg_impl() to return zend_expected_type This effectively mimics part of what Fast ZPP does and allows us to re-use the fast ZPP error APIs --- Zend/zend_API.c | 139 ++++++++++--------- ext/mysqli/tests/mysqli_fetch_object_oo.phpt | 2 +- 2 files changed, 73 insertions(+), 68 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index de3570c5e848..94be6fe16e4e 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -829,7 +829,7 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_stri } /* }}} */ -static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec, char **error, uint32_t arg_num) /* {{{ */ +static zend_expected_type zend_parse_arg_impl(zval *arg, va_list *va, const char **spec, char **error, uint32_t arg_num) /* {{{ */ { const char *spec_walk = *spec; char c = *spec_walk++; @@ -863,7 +863,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec } if (!zend_parse_arg_long(arg, p, is_null, check_null, arg_num)) { - return check_null ? "?int" : "int"; + return check_null ? Z_EXPECTED_LONG_OR_NULL : Z_EXPECTED_LONG; } } break; @@ -878,7 +878,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec } if (!zend_parse_arg_double(arg, p, is_null, check_null, arg_num)) { - return check_null ? "?float" : "float"; + return check_null ? Z_EXPECTED_DOUBLE_OR_NULL : Z_EXPECTED_DOUBLE; } } break; @@ -888,7 +888,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec zval **p = va_arg(*va, zval **); if (!zend_parse_arg_number(arg, p, check_null, arg_num)) { - return check_null ? "int|float|null" : "int|float"; + return check_null ? Z_EXPECTED_NUMBER : Z_EXPECTED_NUMBER; } } break; @@ -898,7 +898,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec char **p = va_arg(*va, char **); size_t *pl = va_arg(*va, size_t *); if (!zend_parse_arg_string(arg, p, pl, check_null, arg_num)) { - return check_null ? "?string" : "string"; + return check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING; } } break; @@ -908,12 +908,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec char **p = va_arg(*va, char **); size_t *pl = va_arg(*va, size_t *); if (!zend_parse_arg_path(arg, p, pl, check_null, arg_num)) { - if (Z_TYPE_P(arg) == IS_STRING) { - zend_spprintf(error, 0, "must not contain any null bytes"); - return ""; - } else { - return check_null ? "?string" : "string"; - } + return check_null ? Z_EXPECTED_PATH_OR_NULL : Z_EXPECTED_PATH; } } break; @@ -922,12 +917,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec { zend_string **str = va_arg(*va, zend_string **); if (!zend_parse_arg_path_str(arg, str, check_null, arg_num)) { - if (Z_TYPE_P(arg) == IS_STRING) { - zend_spprintf(error, 0, "must not contain any null bytes"); - return ""; - } else { - return check_null ? "?string" : "string"; - } + return check_null ? Z_EXPECTED_PATH_OR_NULL : Z_EXPECTED_PATH; } } break; @@ -936,7 +926,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec { zend_string **str = va_arg(*va, zend_string **); if (!zend_parse_arg_str(arg, str, check_null, arg_num)) { - return check_null ? "?string" : "string"; + return check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING; } } break; @@ -951,7 +941,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec } if (!zend_parse_arg_bool(arg, p, is_null, check_null, arg_num)) { - return check_null ? "?bool" : "bool"; + return check_null ? Z_EXPECTED_BOOL_OR_NULL : Z_EXPECTED_BOOL; } } break; @@ -961,7 +951,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec zval **p = va_arg(*va, zval **); if (!zend_parse_arg_resource(arg, p, check_null)) { - return check_null ? "resource or null" : "resource"; + return check_null ? Z_EXPECTED_RESOURCE_OR_NULL : Z_EXPECTED_RESOURCE; } } break; @@ -972,7 +962,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec zval **p = va_arg(*va, zval **); if (!zend_parse_arg_array(arg, p, check_null, c == 'A')) { - return check_null ? "?array" : "array"; + return check_null ? Z_EXPECTED_ARRAY_OR_NULL : Z_EXPECTED_ARRAY; } } break; @@ -983,7 +973,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec HashTable **p = va_arg(*va, HashTable **); if (!zend_parse_arg_array_ht(arg, p, check_null, c == 'H', separate)) { - return check_null ? "?array" : "array"; + return check_null ? Z_EXPECTED_ARRAY_OR_NULL : Z_EXPECTED_ARRAY; } } break; @@ -993,7 +983,7 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec zval **p = va_arg(*va, zval **); if (!zend_parse_arg_object(arg, p, NULL, check_null)) { - return check_null ? "?object" : "object"; + return check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; } } break; @@ -1005,50 +995,42 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec if (!zend_parse_arg_object(arg, p, ce, check_null)) { if (ce) { - if (check_null) { - zend_spprintf(error, 0, "must be of type ?%s, %s given", ZSTR_VAL(ce->name), zend_zval_value_name(arg)); - return ""; - } else { - return ZSTR_VAL(ce->name); - } - } else { - return check_null ? "?object" : "object"; + *error = ZSTR_VAL(ce->name); } + return check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; } } break; case 'C': { - zend_class_entry *lookup, **pce = va_arg(*va, zend_class_entry **); - zend_class_entry *ce_base = *pce; + zend_class_entry **pce = va_arg(*va, zend_class_entry **); + const zend_class_entry *ce_base = *pce; if (check_null && Z_TYPE_P(arg) == IS_NULL) { *pce = NULL; break; } - if (!try_convert_to_string(arg)) { - *pce = NULL; - return ""; /* try_convert_to_string() throws an exception */ - } - if ((lookup = zend_lookup_class(Z_STR_P(arg))) == NULL) { + zend_string *class_name = NULL; + if (!zend_parse_arg_str(arg, &class_name, check_null, arg_num)) { *pce = NULL; - } else { - *pce = lookup; + return check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING; } + + *pce = zend_lookup_class(class_name); if (ce_base) { if ((!*pce || !instanceof_function(*pce, ce_base))) { zend_spprintf(error, 0, "must be a class name derived from %s%s, %s given", ZSTR_VAL(ce_base->name), check_null ? " or null" : "", Z_STRVAL_P(arg)); *pce = NULL; - return ""; + return check_null ? Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL : Z_EXPECTED_OBJECT_OR_CLASS_NAME; } } if (!*pce) { zend_spprintf(error, 0, "must be a valid class name%s, %s given", check_null ? " or null" : "", Z_STRVAL_P(arg)); - return ""; + return check_null ? Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL : Z_EXPECTED_OBJECT_OR_CLASS_NAME; } break; @@ -1060,19 +1042,11 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec { zend_fcall_info *fci = va_arg(*va, zend_fcall_info *); zend_fcall_info_cache *fcc = va_arg(*va, zend_fcall_info_cache *); - char *is_callable_error = NULL; - if (EXPECTED(zend_parse_arg_func(arg, fci, fcc, check_null, &is_callable_error, c == 'f'))) { - ZEND_ASSERT(!is_callable_error); + if (EXPECTED(zend_parse_arg_func(arg, fci, fcc, check_null, error, c == 'f'))) { + ZEND_ASSERT(!*error); break; } - - if (is_callable_error) { - zend_spprintf(error, 0, "must be a valid callback%s, %s", check_null ? " or null" : "", is_callable_error); - efree(is_callable_error); - return ""; - } else { - return check_null ? "a valid callback or null" : "a valid callback"; - } + return check_null ? Z_EXPECTED_FUNC_OR_NULL : Z_EXPECTED_FUNC; } case 'z': @@ -1088,37 +1062,68 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec ZEND_ASSERT(0 && "ZPP modifier no longer supported"); ZEND_FALLTHROUGH; default: - return "unknown"; + ZEND_ASSERT(false && "Unknown ZPP modifier"); } *spec = spec_walk; - return NULL; + return Z_EXPECTED_LAST; } /* }}} */ static zend_result zend_parse_arg(uint32_t arg_num, zval *arg, va_list *va, const char **spec, int flags) /* {{{ */ { - const char *expected_type = NULL; char *error = NULL; - expected_type = zend_parse_arg_impl(arg, va, spec, &error, arg_num); - if (expected_type) { + zend_expected_type expected_type = zend_parse_arg_impl(arg, va, spec, &error, arg_num); + if (expected_type != Z_EXPECTED_LAST) { if (EG(exception)) { return FAILURE; } - if (!(flags & ZEND_PARSE_PARAMS_QUIET) && (*expected_type || error)) { + + if (!(flags & ZEND_PARSE_PARAMS_QUIET)) { + /* More complex error, can only happen for: + * Objects of a specific class + * Z_EXPECTED_OBJECT + * Z_EXPECTED_OBJECT_OR_NULL + * Class names + * Z_EXPECTED_OBJECT_OR_CLASS_NAME + * Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL + * Functions + * Z_EXPECTED_FUNC + * Z_EXPECTED_FUNC_OR_NULL + */ if (error) { - if (strcmp(error, "must not contain any null bytes") == 0) { - zend_argument_value_error(arg_num, "%s", error); - } else { - zend_argument_type_error(arg_num, "%s", error); + switch (expected_type) { + case Z_EXPECTED_OBJECT: + /* DO NOT FREE error: it's a pointer to ZSTR_VAL(ce->name) */ + zend_wrong_parameter_class_error(arg_num, error, arg); + break; + case Z_EXPECTED_OBJECT_OR_NULL: + /* DO NOT FREE error: it's a pointer to ZSTR_VAL(ce->name) */ + zend_wrong_parameter_class_or_null_error(arg_num, error, arg); + break; + case Z_EXPECTED_FUNC: + /* error is freed by zend_wrong_callback_error() */ + zend_wrong_callback_error(arg_num, error); + break; + case Z_EXPECTED_FUNC_OR_NULL: + /* error is freed by zend_wrong_callback_or_null_error() */ + zend_wrong_callback_or_null_error(arg_num, error); + break; + case Z_EXPECTED_OBJECT_OR_CLASS_NAME: + case Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL: + zend_argument_type_error(arg_num, "%s", error); + efree(error); + break; + default: + ZEND_UNREACHABLE(); } - efree(error); - } else { - zend_argument_type_error(arg_num, "must be of type %s, %s given", expected_type, zend_zval_value_name(arg)); } - } else if (error) { + zend_wrong_parameter_type_error(arg_num, expected_type, arg); + } else if (error + /* DO NOT FREE error when it's a pointer to ZSTR_VAL(ce->name) */ + && expected_type != Z_EXPECTED_OBJECT && expected_type != Z_EXPECTED_OBJECT_OR_NULL) { efree(error); } diff --git a/ext/mysqli/tests/mysqli_fetch_object_oo.phpt b/ext/mysqli/tests/mysqli_fetch_object_oo.phpt index b7b2c7812a96..868533d28e93 100644 --- a/ext/mysqli/tests/mysqli_fetch_object_oo.phpt +++ b/ext/mysqli/tests/mysqli_fetch_object_oo.phpt @@ -116,7 +116,7 @@ require_once 'skipifconnectfailure.inc'; require_once 'clean_table.inc'; ?> --EXPECT-- -Error: Object of class mysqli could not be converted to string +TypeError: mysqli_result::fetch_object(): Argument #1 ($class) must be of type string, mysqli given ArgumentCountError: mysqli_result::fetch_object() expects at most 2 arguments, 3 given TypeError: mysqli_result::fetch_object(): Argument #2 ($constructor_args) must be of type array, null given ArgumentCountError: Too few arguments to function mysqli_fetch_object_construct::__construct(), 1 passed and exactly 2 expected From 66d92842487478e1dda29e0b528deed682519a56 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 5 Aug 2026 00:40:46 +0100 Subject: [PATCH 2/2] Zend: refactor zend_parse_arg_class() so that it can be reused And stop throwing the exceptions directly, fixing quiet mode handling at the same time. --- Zend/zend_API.c | 194 ++++++++++++++++++++++++++---------------------- Zend/zend_API.h | 10 ++- 2 files changed, 113 insertions(+), 91 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 94be6fe16e4e..aa4aa0ffb47f 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -225,48 +225,6 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(uint32_t } /* }}} */ -ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(zpp_error error_code, uint32_t num, char *name, zend_expected_type expected_type, const zval *arg) /* {{{ */ -{ - switch (error_code) { - case ZPP_ERROR_WRONG_CALLBACK: - zend_wrong_callback_error(num, name); - break; - case ZPP_ERROR_WRONG_CALLBACK_OR_NULL: - zend_wrong_callback_or_null_error(num, name); - break; - case ZPP_ERROR_WRONG_CLASS: - zend_wrong_parameter_class_error(num, name, arg); - break; - case ZPP_ERROR_WRONG_CLASS_OR_NULL: - zend_wrong_parameter_class_or_null_error(num, name, arg); - break; - case ZPP_ERROR_WRONG_CLASS_OR_STRING: - zend_wrong_parameter_class_or_string_error(num, name, arg); - break; - case ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL: - zend_wrong_parameter_class_or_string_or_null_error(num, name, arg); - break; - case ZPP_ERROR_WRONG_CLASS_OR_LONG: - zend_wrong_parameter_class_or_long_error(num, name, arg); - break; - case ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL: - zend_wrong_parameter_class_or_long_or_null_error(num, name, arg); - break; - case ZPP_ERROR_WRONG_ARG: - zend_wrong_parameter_type_error(num, expected_type, arg); - break; - case ZPP_ERROR_UNEXPECTED_EXTRA_NAMED: - zend_unexpected_extra_named_error(); - break; - case ZPP_ERROR_FAILURE: - ZEND_ASSERT(EG(exception) && "Should have produced an error already"); - break; - case ZPP_ERROR_OK: - ZEND_UNREACHABLE(); - } -} -/* }}} */ - ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(uint32_t num, zend_expected_type expected_type, const zval *arg) /* {{{ */ { static const char * const expected_error[] = { @@ -348,6 +306,42 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_or_nu } /* }}} */ +static ZEND_COLD void ZEND_FASTCALL zend_wrong_class_name_error(uint32_t num, char *name, const zval *arg) +{ + if (EG(exception)) { + return; + } + + if (Z_TYPE_P(arg) != IS_STRING) { + zend_wrong_parameter_type_error(num, Z_EXPECTED_STRING, arg); + return; + } + + if (name) { + zend_argument_type_error(num, "must be a class name derived from %s, %s given", name, Z_STRVAL_P(arg)); + } else { + zend_argument_type_error(num, "must be a valid class name, %s given", Z_STRVAL_P(arg)); + } +} + +static ZEND_COLD void ZEND_FASTCALL zend_wrong_class_name_or_null_error(uint32_t num, char *name, const zval *arg) +{ + if (EG(exception)) { + return; + } + + if (Z_TYPE_P(arg) != IS_STRING) { + zend_wrong_parameter_type_error(num, Z_EXPECTED_STRING_OR_NULL, arg); + return; + } + + if (name) { + zend_argument_type_error(num, "must be a class name derived from %s or null, %s given", name, Z_STRVAL_P(arg)); + } else { + zend_argument_type_error(num, "must be a valid class name or null, %s given", Z_STRVAL_P(arg)); + } +} + ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(uint32_t num, char *error) /* {{{ */ { if (!EG(exception)) { @@ -366,6 +360,54 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_or_null_error(uint32_t } /* }}} */ +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(zpp_error error_code, uint32_t num, char *name, zend_expected_type expected_type, const zval *arg) /* {{{ */ +{ + switch (error_code) { + case ZPP_ERROR_WRONG_CALLBACK: + zend_wrong_callback_error(num, name); + break; + case ZPP_ERROR_WRONG_CALLBACK_OR_NULL: + zend_wrong_callback_or_null_error(num, name); + break; + case ZPP_ERROR_WRONG_CLASS_NAME: + zend_wrong_class_name_error(num, name, arg); + break; + case ZPP_ERROR_WRONG_CLASS_NAME_OR_NULL: + zend_wrong_class_name_or_null_error(num, name, arg); + break; + case ZPP_ERROR_WRONG_CLASS: + zend_wrong_parameter_class_error(num, name, arg); + break; + case ZPP_ERROR_WRONG_CLASS_OR_NULL: + zend_wrong_parameter_class_or_null_error(num, name, arg); + break; + case ZPP_ERROR_WRONG_CLASS_OR_STRING: + zend_wrong_parameter_class_or_string_error(num, name, arg); + break; + case ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL: + zend_wrong_parameter_class_or_string_or_null_error(num, name, arg); + break; + case ZPP_ERROR_WRONG_CLASS_OR_LONG: + zend_wrong_parameter_class_or_long_error(num, name, arg); + break; + case ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL: + zend_wrong_parameter_class_or_long_or_null_error(num, name, arg); + break; + case ZPP_ERROR_WRONG_ARG: + zend_wrong_parameter_type_error(num, expected_type, arg); + break; + case ZPP_ERROR_UNEXPECTED_EXTRA_NAMED: + zend_unexpected_extra_named_error(); + break; + case ZPP_ERROR_FAILURE: + ZEND_ASSERT(EG(exception) && "Should have produced an error already"); + break; + case ZPP_ERROR_OK: + ZEND_UNREACHABLE(); + } +} +/* }}} */ + ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void) { const char *space; @@ -484,36 +526,30 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error(int type, const zend_clas zend_class_redeclaration_error_ex(type, old_ce->name, old_ce); } -ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pce, uint32_t num, bool check_null) /* {{{ */ +ZEND_API ZEND_FASTCALL bool zend_parse_arg_class(zval *arg, zend_class_entry **pce, uint32_t num, bool check_null) { const zend_class_entry *ce_base = *pce; if (check_null && Z_TYPE_P(arg) == IS_NULL) { *pce = NULL; - return 1; + return true; } zend_string *class_name; if (!zend_parse_arg_str(arg, &class_name, check_null, num)) { *pce = NULL; - zend_wrong_parameter_error(ZPP_ERROR_WRONG_ARG, num, NULL, check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING, arg); - return 0; + return false; } *pce = zend_lookup_class(class_name); - if (ce_base) { - if ((!*pce || !instanceof_function(*pce, ce_base))) { - zend_argument_type_error(num, "must be a class name derived from %s, %s given", ZSTR_VAL(ce_base->name), ZSTR_VAL(class_name)); - *pce = NULL; - return 0; - } - } if (!*pce) { - zend_argument_type_error(num, "must be a valid class name, %s given", ZSTR_VAL(class_name)); - return 0; + return false; } - return 1; + if (ce_base && !instanceof_function(*pce, ce_base)) { + *pce = NULL; + return false; + } + return true; } -/* }}} */ static ZEND_COLD bool zend_null_arg_deprecated(const char *fallback_type, uint32_t arg_num) { const zend_function *func = zend_active_function(); @@ -1007,33 +1043,10 @@ static zend_expected_type zend_parse_arg_impl(zval *arg, va_list *va, const char zend_class_entry **pce = va_arg(*va, zend_class_entry **); const zend_class_entry *ce_base = *pce; - if (check_null && Z_TYPE_P(arg) == IS_NULL) { - *pce = NULL; - break; + *error = *pce ? ZSTR_VAL(ce_base->name) : NULL; + if (!zend_parse_arg_class(arg, pce, arg_num, check_null)) { + return check_null ? Z_EXPECTED_CLASS_NAME_OR_NULL : Z_EXPECTED_CLASS_NAME; } - - zend_string *class_name = NULL; - if (!zend_parse_arg_str(arg, &class_name, check_null, arg_num)) { - *pce = NULL; - return check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING; - } - - *pce = zend_lookup_class(class_name); - if (ce_base) { - if ((!*pce || !instanceof_function(*pce, ce_base))) { - zend_spprintf(error, 0, "must be a class name derived from %s%s, %s given", - ZSTR_VAL(ce_base->name), check_null ? " or null" : "", Z_STRVAL_P(arg)); - *pce = NULL; - return check_null ? Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL : Z_EXPECTED_OBJECT_OR_CLASS_NAME; - } - } - if (!*pce) { - zend_spprintf(error, 0, "must be a valid class name%s, %s given", - check_null ? " or null" : "", Z_STRVAL_P(arg)); - return check_null ? Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL : Z_EXPECTED_OBJECT_OR_CLASS_NAME; - } - break; - } break; @@ -1111,10 +1124,13 @@ static zend_result zend_parse_arg(uint32_t arg_num, zval *arg, va_list *va, cons /* error is freed by zend_wrong_callback_or_null_error() */ zend_wrong_callback_or_null_error(arg_num, error); break; - case Z_EXPECTED_OBJECT_OR_CLASS_NAME: - case Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL: - zend_argument_type_error(arg_num, "%s", error); - efree(error); + case Z_EXPECTED_CLASS_NAME: + /* DO NOT FREE error: it's a pointer to ZSTR_VAL(ce->name) */ + zend_wrong_class_name_error(arg_num, error, arg); + break; + case Z_EXPECTED_CLASS_NAME_OR_NULL: + /* DO NOT FREE error: it's a pointer to ZSTR_VAL(ce->name) */ + zend_wrong_class_name_or_null_error(arg_num, error, arg); break; default: ZEND_UNREACHABLE(); @@ -1122,8 +1138,8 @@ static zend_result zend_parse_arg(uint32_t arg_num, zval *arg, va_list *va, cons } zend_wrong_parameter_type_error(arg_num, expected_type, arg); } else if (error - /* DO NOT FREE error when it's a pointer to ZSTR_VAL(ce->name) */ - && expected_type != Z_EXPECTED_OBJECT && expected_type != Z_EXPECTED_OBJECT_OR_NULL) { + /* Only free error if it's a callable expected type, as otherwise it's a pointer to ZSTR_VAL(ce->name) */ + && (expected_type == Z_EXPECTED_FUNC || expected_type == Z_EXPECTED_FUNC_OR_NULL)) { efree(error); } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index aff9846d21b8..73dd144bba4c 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1577,6 +1577,8 @@ static zend_always_inline zval *zend_try_array_init(zval *zv) _(Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL, "an object, a valid class name, or null") \ _(Z_EXPECTED_OBJECT_OR_STRING, "of type object|string") \ _(Z_EXPECTED_OBJECT_OR_STRING_OR_NULL, "of type object|string|null") \ + _(Z_EXPECTED_CLASS_NAME, "a valid class name") \ + _(Z_EXPECTED_CLASS_NAME_OR_NULL, "a valid class name or null") \ #define Z_EXPECTED_TYPE @@ -1592,6 +1594,9 @@ C23_ENUM(zpp_error, uint8_t) { ZPP_ERROR_OK, ZPP_ERROR_FAILURE, ZPP_ERROR_WRONG_CALLBACK, + ZPP_ERROR_WRONG_CALLBACK_OR_NULL, + ZPP_ERROR_WRONG_CLASS_NAME, + ZPP_ERROR_WRONG_CLASS_NAME_OR_NULL, ZPP_ERROR_WRONG_CLASS, ZPP_ERROR_WRONG_CLASS_OR_NULL, ZPP_ERROR_WRONG_CLASS_OR_STRING, @@ -1600,7 +1605,6 @@ C23_ENUM(zpp_error, uint8_t) { ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL, ZPP_ERROR_WRONG_ARG, ZPP_ERROR_UNEXPECTED_EXTRA_NAMED, - ZPP_ERROR_WRONG_CALLBACK_OR_NULL, }; ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_none_error(void); @@ -1773,8 +1777,10 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string /* old "C" */ #define Z_PARAM_CLASS_EX(dest, check_null, deref) \ Z_PARAM_PROLOGUE(deref, 0); \ + _error = dest ? ZSTR_VAL((dest)->name) : NULL; \ if (UNEXPECTED(!zend_parse_arg_class(_arg, &dest, _i, check_null))) { \ - _error_code = ZPP_ERROR_FAILURE; \ + _expected_type = check_null ? Z_EXPECTED_CLASS_NAME_OR_NULL : Z_EXPECTED_CLASS_NAME; \ + _error_code = check_null ? ZPP_ERROR_WRONG_CLASS_NAME_OR_NULL : ZPP_ERROR_WRONG_CLASS_NAME; \ break; \ }