Commit cfb1d5ee12a for php
commit cfb1d5ee12aef4b0a0c6e205cea108570a416b5b
Author: Gina Peter Banyard <girgias@php.net>
Date: Mon Sep 28 14:22:02 2026 +0100
Zend: refactor zend_parse_arg_class() so that it can be reused (#23104)
And stop throwing the exceptions directly, fixing quiet mode handling at the same time.
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index acbf10e163e..9b865b7b5d2 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -223,48 +223,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[] = {
@@ -356,6 +314,34 @@ 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, const char *name, const zval *arg)
+{
+ if (EG(exception)) {
+ return;
+ }
+
+ if (name && Z_TYPE_P(arg) == IS_STRING) {
+ zend_argument_type_error(num, "must be a class name derived from %s, \"%s\" given", name, Z_STRVAL_P(arg));
+ return;
+ }
+
+ zend_wrong_parameter_type_error(num, Z_EXPECTED_CLASS_NAME, arg);
+}
+
+static ZEND_COLD void ZEND_FASTCALL zend_wrong_class_name_or_null_error(uint32_t num, const char *name, const zval *arg)
+{
+ if (EG(exception)) {
+ return;
+ }
+
+ if (name && Z_TYPE_P(arg) == IS_STRING) {
+ zend_argument_type_error(num, "must be a class name derived from %s, \"%s\" given", name, Z_STRVAL_P(arg));
+ return;
+ }
+
+ zend_wrong_parameter_type_error(num, Z_EXPECTED_CLASS_NAME_OR_NULL, arg);
+}
+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(uint32_t num, char *error) /* {{{ */
{
if (!EG(exception)) {
@@ -374,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;
@@ -492,20 +526,18 @@ 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 bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pce, const zend_class_entry *ce_base, 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;
}
/* Only accept string and Stringable(?) as int/foat/bool are not valid class names */
if (UNEXPECTED(Z_TYPE_P(arg) != IS_STRING)) {
if (Z_TYPE_P(arg) != IS_OBJECT || !zend_parse_arg_str_slow(arg, num)) {
*pce = NULL;
zend_wrong_parameter_type_error(num, check_null ? Z_EXPECTED_CLASS_NAME_OR_NULL : Z_EXPECTED_CLASS_NAME, arg);
- return 0;
+ return false;
}
/* Object was converted to string */
ZEND_ASSERT(Z_TYPE_P(arg) == IS_STRING);
@@ -513,20 +545,15 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **p
zend_string *class_name = Z_STR_P(arg);
*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_wrong_parameter_type_error(num, check_null ? Z_EXPECTED_CLASS_NAME_OR_NULL : Z_EXPECTED_CLASS_NAME, arg);
- 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();
@@ -1020,41 +1047,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;
- }
-
- /* Only accept string and Stringable(?) as int/foat/bool are not valid class names */
- if (UNEXPECTED(Z_TYPE_P(arg) != IS_STRING)) {
- if (Z_TYPE_P(arg) != IS_OBJECT || !zend_parse_arg_str_slow(arg, arg_num)) {
- /* __toString may throw */
- if (!EG(exception)) {
- zend_spprintf(error, 0, "must be a valid class name%s, %s given",
- check_null ? " or null" : "", zend_zval_value_name(arg));
- }
- *pce = NULL;
- return check_null ? Z_EXPECTED_CLASS_NAME_OR_NULL : Z_EXPECTED_CLASS_NAME;
- }
- /* Object was converted to string */
- ZEND_ASSERT(Z_TYPE_P(arg) == IS_STRING);
- }
- zend_string *class_name = Z_STR_P(arg);
-
- *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_CLASS_NAME_OR_NULL : Z_EXPECTED_CLASS_NAME;
- }
- }
- if (!*pce) {
+ if (!zend_parse_arg_class(arg, pce, ce_base, arg_num, check_null)) {
+ *error = ce_base ? ZSTR_VAL(ce_base->name) : NULL;
return check_null ? Z_EXPECTED_CLASS_NAME_OR_NULL : Z_EXPECTED_CLASS_NAME;
}
- break;
-
}
break;
@@ -1133,9 +1129,12 @@ static zend_result zend_parse_arg(uint32_t arg_num, zval *arg, va_list *va, cons
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:
- zend_argument_type_error(arg_num, "%s", error);
- efree(error);
+ /* 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();
@@ -1143,8 +1142,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 da871e1eb39..786ccafbb9d 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -1590,6 +1590,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,
@@ -1598,7 +1601,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);
@@ -1767,8 +1769,11 @@ 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); \
- if (UNEXPECTED(!zend_parse_arg_class(_arg, &dest, _i, check_null))) { \
- _error_code = ZPP_ERROR_FAILURE; \
+ const zend_class_entry *_base_ce = dest; \
+ if (UNEXPECTED(!zend_parse_arg_class(_arg, &dest, _base_ce, _i, check_null))) { \
+ _error = _base_ce ? ZSTR_VAL((_base_ce)->name) : 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; \
}
@@ -2216,7 +2221,7 @@ typedef enum zpp_parse_bool_status {
ZPP_PARSE_BOOL_STATUS_ERROR = 2,
} zpp_parse_bool_status;
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pce, uint32_t num, bool check_null);
+ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pce, const zend_class_entry *ce_base, uint32_t num, bool check_null);
ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_slow(const zval *arg, uint32_t arg_num);
ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_weak(const zval *arg, uint32_t arg_num);
ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(const zval *arg, zend_long *dest, uint32_t arg_num);