From c71a412245c04310fb7ec6795f015cc7635f0ccf Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 4 Aug 2026 22:52:50 +0100 Subject: [PATCH 1/5] 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 | 151 ++++++++++++++++++++++++------------------------ 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index de3570c5e848..f8426001580b 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,40 @@ 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; - } - 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_STRING_OR_NULL : Z_EXPECTED_STRING; } + + *pce = zend_lookup_class(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; + } + if (ce_base && !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; } break; @@ -1060,19 +1040,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 +1060,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); } From b712cdaed9347cac3be75be7b81bdeaf6267466c Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 4 Aug 2026 23:56:14 +0100 Subject: [PATCH 2/5] Fix tests and update zend_parse_arg_class() to align with new behaviour --- Zend/zend_API.c | 12 +++++------- ext/mysqli/tests/mysqli_fetch_object_oo.phpt | 2 +- .../ArrayObject/arrayObject___construct_error1.phpt | 2 +- .../arrayObject_setIteratorClass_error1.phpt | 4 ++-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index f8426001580b..24ae853e530a 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -500,17 +500,15 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **p } *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; } + if (ce_base && !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; + } return 1; } /* }}} */ 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 diff --git a/ext/spl/tests/ArrayObject/arrayObject___construct_error1.phpt b/ext/spl/tests/ArrayObject/arrayObject___construct_error1.phpt index a9fbc9d3b030..82e9c75eca81 100644 --- a/ext/spl/tests/ArrayObject/arrayObject___construct_error1.phpt +++ b/ext/spl/tests/ArrayObject/arrayObject___construct_error1.phpt @@ -22,4 +22,4 @@ try { Bad iterator type: ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a class name derived from ArrayIterator, Exception given(6) Non-existent class: -ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a class name derived from ArrayIterator, nonExistentClassName given(13) +ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a valid class name, nonExistentClassName given(13) diff --git a/ext/spl/tests/ArrayObject/arrayObject_setIteratorClass_error1.phpt b/ext/spl/tests/ArrayObject/arrayObject_setIteratorClass_error1.phpt index 9a0e67b6052f..cb22af8f900d 100644 --- a/ext/spl/tests/ArrayObject/arrayObject_setIteratorClass_error1.phpt +++ b/ext/spl/tests/ArrayObject/arrayObject_setIteratorClass_error1.phpt @@ -43,7 +43,7 @@ try { ?> --EXPECT-- -string(133) "ArrayObject::setIteratorClass(): Argument #1 ($iteratorClass) must be a class name derived from ArrayIterator, nonExistentClass given" +string(112) "ArrayObject::setIteratorClass(): Argument #1 ($iteratorClass) must be a valid class name, nonExistentClass given" string(125) "ArrayObject::setIteratorClass(): Argument #1 ($iteratorClass) must be a class name derived from ArrayIterator, stdClass given" -string(128) "ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a class name derived from ArrayIterator, nonExistentClass given" +string(107) "ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a valid class name, nonExistentClass given" string(120) "ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a class name derived from ArrayIterator, stdClass given" From a985c844610d0ca5bb44d24ae55d53fb0308c98d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 5 Aug 2026 00:40:46 +0100 Subject: [PATCH 3/5] refactor zend_parse_arg_class() so that it can be reused And stop throwing the exceptions directly Means we restored back the original error message behaviour that's possibly not ideal --- Zend/zend_API.c | 183 ++++++++++-------- Zend/zend_API.h | 10 +- .../arrayObject___construct_error1.phpt | 2 +- .../arrayObject_setIteratorClass_error1.phpt | 4 +- 4 files changed, 113 insertions(+), 86 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 24ae853e530a..9658f55d734b 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,34 +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 (!*pce) { - zend_argument_type_error(num, "must be a valid class name, %s given", ZSTR_VAL(class_name)); - return 0; + return false; } if (ce_base && !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; + return false; } - return 1; + 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(); @@ -1005,30 +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; - } - - 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 (!*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; - } - if (ce_base && !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; + *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; } - break; } break; @@ -1107,10 +1125,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(); @@ -1118,8 +1139,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 a3e4e1690d6c..be9216bd0efd 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; \ } diff --git a/ext/spl/tests/ArrayObject/arrayObject___construct_error1.phpt b/ext/spl/tests/ArrayObject/arrayObject___construct_error1.phpt index 82e9c75eca81..a9fbc9d3b030 100644 --- a/ext/spl/tests/ArrayObject/arrayObject___construct_error1.phpt +++ b/ext/spl/tests/ArrayObject/arrayObject___construct_error1.phpt @@ -22,4 +22,4 @@ try { Bad iterator type: ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a class name derived from ArrayIterator, Exception given(6) Non-existent class: -ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a valid class name, nonExistentClassName given(13) +ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a class name derived from ArrayIterator, nonExistentClassName given(13) diff --git a/ext/spl/tests/ArrayObject/arrayObject_setIteratorClass_error1.phpt b/ext/spl/tests/ArrayObject/arrayObject_setIteratorClass_error1.phpt index cb22af8f900d..9a0e67b6052f 100644 --- a/ext/spl/tests/ArrayObject/arrayObject_setIteratorClass_error1.phpt +++ b/ext/spl/tests/ArrayObject/arrayObject_setIteratorClass_error1.phpt @@ -43,7 +43,7 @@ try { ?> --EXPECT-- -string(112) "ArrayObject::setIteratorClass(): Argument #1 ($iteratorClass) must be a valid class name, nonExistentClass given" +string(133) "ArrayObject::setIteratorClass(): Argument #1 ($iteratorClass) must be a class name derived from ArrayIterator, nonExistentClass given" string(125) "ArrayObject::setIteratorClass(): Argument #1 ($iteratorClass) must be a class name derived from ArrayIterator, stdClass given" -string(107) "ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a valid class name, nonExistentClass given" +string(128) "ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a class name derived from ArrayIterator, nonExistentClass given" string(120) "ArrayObject::__construct(): Argument #3 ($iteratorClass) must be a class name derived from ArrayIterator, stdClass given" From 7bc1cb2b8cc012ded0cb6ab87e54ceb26d82d8be Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 5 Aug 2026 00:46:35 +0100 Subject: [PATCH 4/5] Refactor zend_expected_type partially, as some ZPP errors need special handling anyway --- Zend/zend_API.c | 5 ++--- Zend/zend_API.h | 13 +++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 9658f55d734b..42f175d87518 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -229,7 +229,6 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(uint32_t n { static const char * const expected_error[] = { Z_EXPECTED_TYPES(Z_EXPECTED_TYPE_STR) - NULL }; if (EG(exception)) { @@ -1081,7 +1080,7 @@ static zend_expected_type zend_parse_arg_impl(zval *arg, va_list *va, const char *spec = spec_walk; - return Z_EXPECTED_LAST; + return Z_EXPECTED_OK; } /* }}} */ @@ -1090,7 +1089,7 @@ static zend_result zend_parse_arg(uint32_t arg_num, zval *arg, va_list *va, cons char *error = NULL; zend_expected_type expected_type = zend_parse_arg_impl(arg, va, spec, &error, arg_num); - if (expected_type != Z_EXPECTED_LAST) { + if (expected_type != Z_EXPECTED_OK) { if (EG(exception)) { return FAILURE; } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index be9216bd0efd..d7074ed064f1 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1555,8 +1555,6 @@ static zend_always_inline zval *zend_try_array_init(zval *zv) _(Z_EXPECTED_ARRAY_OR_LONG_OR_NULL, "of type array|int|null") \ _(Z_EXPECTED_ITERABLE, "of type Traversable|array") \ _(Z_EXPECTED_ITERABLE_OR_NULL, "of type Traversable|array|null") \ - _(Z_EXPECTED_FUNC, "a valid callback") \ - _(Z_EXPECTED_FUNC_OR_NULL, "a valid callback or null") \ _(Z_EXPECTED_RESOURCE, "of type resource") \ _(Z_EXPECTED_RESOURCE_OR_NULL, "of type resource or null") \ _(Z_EXPECTED_PATH, "of type string") \ @@ -1577,8 +1575,12 @@ 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") \ + /* Have special error message behaviour */ \ + _(Z_EXPECTED_FUNC, NULL) \ + _(Z_EXPECTED_FUNC_OR_NULL, NULL) \ + _(Z_EXPECTED_CLASS_NAME, NULL) \ + _(Z_EXPECTED_CLASS_NAME_OR_NULL, NULL) \ + _(Z_EXPECTED_OK, NULL) \ #define Z_EXPECTED_TYPE @@ -1587,7 +1589,6 @@ static zend_always_inline zval *zend_try_array_init(zval *zv) typedef enum _zend_expected_type { Z_EXPECTED_TYPES(Z_EXPECTED_TYPE_ENUM) - Z_EXPECTED_LAST } zend_expected_type; C23_ENUM(zpp_error, uint8_t) { @@ -1638,7 +1639,7 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string uint32_t _num_args = EX_NUM_ARGS(); \ uint32_t _i = 0; \ zval *_real_arg, *_arg = NULL; \ - zend_expected_type _expected_type = Z_EXPECTED_LONG; \ + zend_expected_type _expected_type = Z_EXPECTED_OK; \ char *_error = NULL; \ bool _dummy = 0; \ bool _optional = 0; \ From c471e54e1d9e20a988a85cf8bf1d05656fa5f5ac Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 5 Aug 2026 01:36:08 +0100 Subject: [PATCH 5/5] Zend: refactor fast ZPP to only use zend_expected_type Removing the usage of zpp_error and allowing 'slow' ZPP to reuse the error logic --- Zend/tests/bug70914.phpt | 2 +- Zend/zend_API.c | 77 +++++------------- Zend/zend_API.h | 78 +++++-------------- ext/bcmath/bcmath.c | 2 +- ext/gmp/gmp.c | 2 +- .../tests/pdo_mysql_stmt_fetchobject.phpt | 2 +- main/php_streams.h | 3 +- 7 files changed, 42 insertions(+), 124 deletions(-) diff --git a/Zend/tests/bug70914.phpt b/Zend/tests/bug70914.phpt index 73a43a5600dc..dc7eabb744c4 100644 --- a/Zend/tests/bug70914.phpt +++ b/Zend/tests/bug70914.phpt @@ -14,4 +14,4 @@ try { } ?> --EXPECT-- -PDOStatement::fetchObject(): Argument #1 ($class) must be a valid class name, %Z given +PDOStatement::fetchObject(): Argument #1 ($class) must be a valid class name or null, %Z given diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 42f175d87518..f6b4a8586ee3 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -359,49 +359,49 @@ 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) /* {{{ */ +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(uint32_t num, char *name, zend_expected_type expected_type, const zval *arg) /* {{{ */ { - switch (error_code) { - case ZPP_ERROR_WRONG_CALLBACK: + switch (expected_type) { + case Z_EXPECTED_FUNC: zend_wrong_callback_error(num, name); break; - case ZPP_ERROR_WRONG_CALLBACK_OR_NULL: + case Z_EXPECTED_FUNC_OR_NULL: zend_wrong_callback_or_null_error(num, name); break; - case ZPP_ERROR_WRONG_CLASS_NAME: + case Z_EXPECTED_CLASS_NAME: zend_wrong_class_name_error(num, name, arg); break; - case ZPP_ERROR_WRONG_CLASS_NAME_OR_NULL: + case Z_EXPECTED_CLASS_NAME_OR_NULL: zend_wrong_class_name_or_null_error(num, name, arg); break; - case ZPP_ERROR_WRONG_CLASS: + case Z_EXPECTED_CLASS: zend_wrong_parameter_class_error(num, name, arg); break; - case ZPP_ERROR_WRONG_CLASS_OR_NULL: + case Z_EXPECTED_CLASS_OR_NULL: zend_wrong_parameter_class_or_null_error(num, name, arg); break; - case ZPP_ERROR_WRONG_CLASS_OR_STRING: + case Z_EXPECTED_CLASS_OR_STRING: zend_wrong_parameter_class_or_string_error(num, name, arg); break; - case ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL: + case Z_EXPECTED_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: + case Z_EXPECTED_CLASS_OR_LONG: zend_wrong_parameter_class_or_long_error(num, name, arg); break; - case ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL: + case Z_EXPECTED_CLASS_OR_LONG_OR_NULL: zend_wrong_parameter_class_or_long_or_null_error(num, name, arg); break; - case ZPP_ERROR_WRONG_ARG: + default: zend_wrong_parameter_type_error(num, expected_type, arg); break; - case ZPP_ERROR_UNEXPECTED_EXTRA_NAMED: + case Z_EXPECTED_NO_EXTRA_NAMED: zend_unexpected_extra_named_error(); break; - case ZPP_ERROR_FAILURE: + case Z_EXPECTED_FAILURE: ZEND_ASSERT(EG(exception) && "Should have produced an error already"); break; - case ZPP_ERROR_OK: + case Z_EXPECTED_OK: ZEND_UNREACHABLE(); } } @@ -1032,7 +1032,7 @@ static zend_expected_type zend_parse_arg_impl(zval *arg, va_list *va, const char if (ce) { *error = ZSTR_VAL(ce->name); } - return check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; + return check_null ? Z_EXPECTED_CLASS_OR_NULL : Z_EXPECTED_CLASS; } } break; @@ -1095,48 +1095,7 @@ static zend_result zend_parse_arg(uint32_t arg_num, zval *arg, va_list *va, cons } 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) { - 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_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(); - } - } - zend_wrong_parameter_type_error(arg_num, expected_type, arg); + zend_wrong_parameter_error(arg_num, error, expected_type, arg); } else if (error /* 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)) { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index d7074ed064f1..587a2dee0592 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1580,6 +1580,14 @@ static zend_always_inline zval *zend_try_array_init(zval *zv) _(Z_EXPECTED_FUNC_OR_NULL, NULL) \ _(Z_EXPECTED_CLASS_NAME, NULL) \ _(Z_EXPECTED_CLASS_NAME_OR_NULL, NULL) \ + _(Z_EXPECTED_CLASS, NULL) \ + _(Z_EXPECTED_CLASS_OR_NULL, NULL) \ + _(Z_EXPECTED_CLASS_OR_STRING, NULL) \ + _(Z_EXPECTED_CLASS_OR_STRING_OR_NULL, NULL) \ + _(Z_EXPECTED_CLASS_OR_LONG, NULL) \ + _(Z_EXPECTED_CLASS_OR_LONG_OR_NULL, NULL) \ + _(Z_EXPECTED_NO_EXTRA_NAMED, NULL) \ + _(Z_EXPECTED_FAILURE, NULL) /* For custom ZPP specifier which already throw an exception */ \ _(Z_EXPECTED_OK, NULL) \ #define Z_EXPECTED_TYPE @@ -1591,26 +1599,9 @@ typedef enum _zend_expected_type { Z_EXPECTED_TYPES(Z_EXPECTED_TYPE_ENUM) } zend_expected_type; -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, - ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL, - ZPP_ERROR_WRONG_CLASS_OR_LONG, - ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL, - ZPP_ERROR_WRONG_ARG, - ZPP_ERROR_UNEXPECTED_EXTRA_NAMED, -}; - ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_none_error(void); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(uint32_t min_num_args, uint32_t max_num_args); -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); +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(uint32_t num, char *name, zend_expected_type expected_type, const zval *arg); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(uint32_t num, zend_expected_type expected_type, const zval *arg); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(uint32_t num, const char *name, const zval *arg); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(uint32_t num, const char *name, const zval *arg); @@ -1643,7 +1634,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string char *_error = NULL; \ bool _dummy = 0; \ bool _optional = 0; \ - zpp_error _error_code = ZPP_ERROR_OK; \ ((void)_i); \ ((void)_real_arg); \ ((void)_arg); \ @@ -1658,7 +1648,7 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \ zend_wrong_parameters_count_error(_min_num_args, _max_num_args); \ } \ - _error_code = ZPP_ERROR_FAILURE; \ + _expected_type = Z_EXPECTED_FAILURE; \ break; \ } \ _real_arg = ZEND_CALL_ARG(execute_data, 0); @@ -1676,9 +1666,9 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string #define ZEND_PARSE_PARAMETERS_END_EX(failure) \ ZEND_ASSERT(_i == _max_num_args || _max_num_args == (uint32_t) -1); \ } while (0); \ - if (UNEXPECTED(_error_code != ZPP_ERROR_OK)) { \ + if (UNEXPECTED(_expected_type != Z_EXPECTED_OK)) { \ if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \ - zend_wrong_parameter_error(_error_code, _i, _error, _expected_type, _arg); \ + zend_wrong_parameter_error(_i, _error, _expected_type, _arg); \ } \ failure; \ } \ @@ -1718,7 +1708,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, separate); \ if (UNEXPECTED(!zend_parse_arg_array(_arg, &dest, check_null, 0))) { \ _expected_type = check_null ? Z_EXPECTED_ARRAY_OR_NULL : Z_EXPECTED_ARRAY; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1736,7 +1725,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, separate); \ if (UNEXPECTED(!zend_parse_arg_array(_arg, &dest, check_null, 1))) { \ _expected_type = check_null ? Z_EXPECTED_ARRAY_OR_NULL : Z_EXPECTED_ARRAY; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1750,7 +1738,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!zend_parse_arg_iterable(_arg, &dest, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_ITERABLE_OR_NULL : Z_EXPECTED_ITERABLE; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1765,7 +1752,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_bool(_arg, &dest, &is_null, check_null, _i))) { \ _expected_type = check_null ? Z_EXPECTED_BOOL_OR_NULL : Z_EXPECTED_BOOL; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1781,7 +1767,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string _error = dest ? ZSTR_VAL((dest)->name) : NULL; \ if (UNEXPECTED(!zend_parse_arg_class(_arg, &dest, _i, check_null))) { \ _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; \ } @@ -1795,7 +1780,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!zend_parse_arg_obj_or_class_name(_arg, &dest, allow_null))) { \ _expected_type = allow_null ? Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL : Z_EXPECTED_OBJECT_OR_CLASS_NAME; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1809,7 +1793,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!zend_parse_arg_obj_or_str(_arg, &destination_object, NULL, &destination_string, allow_null, _i))) { \ _expected_type = allow_null ? Z_EXPECTED_OBJECT_OR_STRING_OR_NULL : Z_EXPECTED_OBJECT_OR_STRING; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1824,11 +1807,10 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string if (UNEXPECTED(!zend_parse_arg_obj_or_str(_arg, &destination_object, base_ce, &destination_string, allow_null, _i))) { \ if (base_ce) { \ _error = ZSTR_VAL((base_ce)->name); \ - _error_code = allow_null ? ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL : ZPP_ERROR_WRONG_CLASS_OR_STRING; \ + _expected_type = allow_null ? Z_EXPECTED_CLASS_OR_STRING_OR_NULL : Z_EXPECTED_CLASS_OR_STRING; \ break; \ } else { \ _expected_type = allow_null ? Z_EXPECTED_OBJECT_OR_STRING_OR_NULL : Z_EXPECTED_OBJECT_OR_STRING; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } \ } @@ -1844,7 +1826,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_double(_arg, &dest, &is_null, check_null, _i))) { \ _expected_type = check_null ? Z_EXPECTED_DOUBLE_OR_NULL : Z_EXPECTED_DOUBLE; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1858,12 +1839,8 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string #define Z_PARAM_FUNC_EX2(dest_fci, dest_fcc, check_null, deref, free_trampoline) \ Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_func(_arg, &dest_fci, &dest_fcc, check_null, &_error, free_trampoline))) { \ - if (!_error) { \ - _expected_type = check_null ? Z_EXPECTED_FUNC_OR_NULL : Z_EXPECTED_FUNC; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ - } else { \ - _error_code = check_null ? ZPP_ERROR_WRONG_CALLBACK_OR_NULL : ZPP_ERROR_WRONG_CALLBACK; \ - } \ + ZEND_ASSERT(_error); \ + _expected_type = check_null ? Z_EXPECTED_FUNC_OR_NULL : Z_EXPECTED_FUNC; \ break; \ } \ @@ -1890,7 +1867,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, separate); \ if (UNEXPECTED(!zend_parse_arg_array_ht(_arg, &dest, check_null, 0, separate))) { \ _expected_type = check_null ? Z_EXPECTED_ARRAY_OR_NULL : Z_EXPECTED_ARRAY; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1907,7 +1883,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!zend_parse_arg_array_ht_or_long(_arg, &dest_ht, &dest_long, &is_null, allow_null, _i))) { \ _expected_type = allow_null ? Z_EXPECTED_ARRAY_OR_LONG_OR_NULL : Z_EXPECTED_ARRAY_OR_LONG; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1922,7 +1897,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, separate); \ if (UNEXPECTED(!zend_parse_arg_array_ht(_arg, &dest, check_null, 1, separate))) { \ _expected_type = check_null ? Z_EXPECTED_ARRAY_OR_NULL : Z_EXPECTED_ARRAY; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1937,7 +1911,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_long(_arg, &dest, &is_null, check_null, _i))) { \ _expected_type = check_null ? Z_EXPECTED_LONG_OR_NULL : Z_EXPECTED_LONG; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1952,7 +1925,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!zend_parse_arg_number(_arg, &dest, check_null, _i))) { \ _expected_type = check_null ? Z_EXPECTED_NUMBER_OR_NULL : Z_EXPECTED_NUMBER; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1966,7 +1938,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!zend_parse_arg_number_or_str(_arg, &dest, check_null, _i))) { \ _expected_type = check_null ? Z_EXPECTED_NUMBER_OR_STRING_OR_NULL : Z_EXPECTED_NUMBER_OR_STRING; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1981,7 +1952,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_object(_arg, &dest, NULL, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -1996,7 +1966,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_obj(_arg, &dest, NULL, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -2012,11 +1981,10 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string if (UNEXPECTED(!zend_parse_arg_object(_arg, &dest, _ce, check_null))) { \ if (_ce) { \ _error = ZSTR_VAL((_ce)->name); \ - _error_code = check_null ? ZPP_ERROR_WRONG_CLASS_OR_NULL : ZPP_ERROR_WRONG_CLASS; \ + _expected_type = check_null ? Z_EXPECTED_CLASS_OR_NULL : Z_EXPECTED_CLASS; \ break; \ } else { \ _expected_type = check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } \ } @@ -2033,11 +2001,10 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string if (UNEXPECTED(!zend_parse_arg_obj(_arg, &dest, _ce, check_null))) { \ if (_ce) { \ _error = ZSTR_VAL((_ce)->name); \ - _error_code = check_null ? ZPP_ERROR_WRONG_CLASS_OR_NULL : ZPP_ERROR_WRONG_CLASS; \ + _expected_type = check_null ? Z_EXPECTED_CLASS_OR_NULL : Z_EXPECTED_CLASS; \ break; \ } else { \ _expected_type = check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } \ } @@ -2052,7 +2019,7 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!zend_parse_arg_obj_or_long(_arg, &dest_obj, _ce, &dest_long, &is_null, allow_null, _i))) { \ _error = ZSTR_VAL((_ce)->name); \ - _error_code = allow_null ? ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL : ZPP_ERROR_WRONG_CLASS_OR_LONG; \ + _expected_type = allow_null ? Z_EXPECTED_CLASS_OR_LONG_OR_NULL : Z_EXPECTED_CLASS_OR_LONG; \ break; \ } @@ -2074,7 +2041,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_path(_arg, &dest, &dest_len, check_null, _i))) { \ _expected_type = check_null ? Z_EXPECTED_PATH_OR_NULL : Z_EXPECTED_PATH; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -2089,7 +2055,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_path_str(_arg, &dest, check_null, _i))) { \ _expected_type = check_null ? Z_EXPECTED_PATH_OR_NULL : Z_EXPECTED_PATH; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -2104,7 +2069,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_resource(_arg, &dest, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_RESOURCE_OR_NULL : Z_EXPECTED_RESOURCE; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -2119,7 +2083,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_string(_arg, &dest, &dest_len, check_null, _i))) { \ _expected_type = check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -2134,7 +2097,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_str(_arg, &dest, check_null, _i))) { \ _expected_type = check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -2171,7 +2133,7 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string dest_num = 0; \ } \ if (UNEXPECTED(ZEND_CALL_INFO(execute_data) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { \ - _error_code = ZPP_ERROR_UNEXPECTED_EXTRA_NAMED; \ + _expected_type = Z_EXPECTED_NO_EXTRA_NAMED; \ break; \ } \ } while (0); @@ -2199,7 +2161,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!zend_parse_arg_array_ht_or_str(_arg, &dest_ht, &dest_str, allow_null, _i))) { \ _expected_type = allow_null ? Z_EXPECTED_ARRAY_OR_STRING_OR_NULL : Z_EXPECTED_ARRAY_OR_STRING; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } @@ -2213,7 +2174,6 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!zend_parse_arg_str_or_long(_arg, &dest_str, &dest_long, &is_null, allow_null, _i))) { \ _expected_type = allow_null ? Z_EXPECTED_STRING_OR_LONG_OR_NULL : Z_EXPECTED_STRING_OR_LONG; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c index 0e0a9a23b94a..06f1fb891269 100644 --- a/ext/bcmath/bcmath.c +++ b/ext/bcmath/bcmath.c @@ -1399,7 +1399,7 @@ static int bcmath_number_compare(zval *op1, zval *op2) zend_parse_arg_str_or_long(_arg, &(dest_str), &(dest_long), &_dummy, 0, _i)))) { \ zend_argument_type_error(_i, "must be of type int, string, or %s, %s given", \ ZSTR_VAL(bcmath_number_ce->name), zend_zval_value_name(_arg)); \ - _error_code = ZPP_ERROR_FAILURE; \ + _expected_type = Z_EXPECTED_FAILURE; \ break; \ } diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 4a81026d451b..0ee8ff699820 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -159,7 +159,7 @@ static bool gmp_zend_parse_arg_into_mpz(zval *arg, mpz_ptr *destination_mpz_ptr, #define GMP_Z_PARAM_INTO_MPZ_PTR(destination_mpz_ptr) \ Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!gmp_zend_parse_arg_into_mpz(_arg, &destination_mpz_ptr, _i))) { \ - _error_code = ZPP_ERROR_FAILURE; \ + _expected_type = Z_EXPECTED_FAILURE; \ if (!EG(exception)) { \ zend_argument_type_error(_i, "must be of type GMP|string|int, %s given", zend_zval_value_name(_arg)); \ } \ diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject.phpt index 0690878bac09..96f0b75fcebd 100644 --- a/ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject.phpt @@ -117,5 +117,5 @@ object(myclass)#%d (4) { ["null"]=> NULL } -PDOStatement::fetchObject(): Argument #1 ($class) must be a valid class name, class_does_not_exist given +PDOStatement::fetchObject(): Argument #1 ($class) must be a valid class name or null, class_does_not_exist given done! diff --git a/main/php_streams.h b/main/php_streams.h index 8a8fa4b3a567..9e13cd6126ad 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -322,10 +322,9 @@ static zend_always_inline bool php_stream_zend_parse_arg_into_stream( #define PHP_Z_PARAM_STREAM_EX(destination_stream_ptr, check_null) \ Z_PARAM_PROLOGUE(0, 0); \ if (UNEXPECTED(!php_stream_zend_parse_arg_into_stream(_arg, &destination_stream_ptr, check_null, _i))) { \ - _error_code = ZPP_ERROR_FAILURE; \ + _expected_type = Z_EXPECTED_FAILURE; \ if (!EG(exception)) { \ _expected_type = check_null ? Z_EXPECTED_RESOURCE_OR_NULL : Z_EXPECTED_RESOURCE; \ - _error_code = ZPP_ERROR_WRONG_ARG; \ } \ break; \ }