From 1bab072a156b766e64652301f06de2063a63fc5a Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sun, 12 Apr 2026 12:18:53 +0100 Subject: [PATCH 1/2] ZPP: Return zend_string directly from zend_parse_arg_str_weak() This idea is based on @ndossche previous PR to reduce codebloat: https://github.com/php/php-src/pull/18436 --- Zend/zend_API.c | 30 +++++++++++++++--------------- Zend/zend_API.h | 19 +++++++++++++------ Zend/zend_execute.c | 3 +-- Zend/zend_frameless_function.h | 2 +- Zend/zend_vm_def.h | 4 ++-- Zend/zend_vm_execute.h | 24 ++++++++++++------------ 6 files changed, 44 insertions(+), 38 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 7c38779a564a..4ce8aaa9a3f5 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -728,58 +728,58 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_or_str_slow(zval *arg, zval ** return true; } -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */ +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, uint32_t arg_num) /* {{{ */ { if (EXPECTED(Z_TYPE_P(arg) < IS_STRING)) { if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("string", arg_num)) { - return 0; + return NULL; } convert_to_string(arg); - *dest = Z_STR_P(arg); + return Z_STR_P(arg); } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(arg); zval obj; if (zobj->handlers->cast_object(zobj, &obj, IS_STRING) == SUCCESS) { OBJ_RELEASE(zobj); ZVAL_COPY_VALUE(arg, &obj); - *dest = Z_STR_P(arg); - return 1; + return Z_STR_P(arg); } - return 0; + return NULL; } else { - return 0; + return NULL; } - return 1; } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */ +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, uint32_t arg_num) /* {{{ */ { if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { - return 0; + return NULL; } - return zend_parse_arg_str_weak(arg, dest, arg_num); + return zend_parse_arg_str_weak(arg, arg_num); } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num) +ZEND_API zend_string* ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, uint32_t arg_num) { if (UNEXPECTED(ZEND_FLF_ARG_USES_STRICT_TYPES())) { - return 0; + return NULL; } - return zend_parse_arg_str_weak(arg, dest, arg_num); + return zend_parse_arg_str_weak(arg, arg_num); } ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num) /* {{{ */ { + zend_string *str; if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { return 0; } if (zend_parse_arg_long_weak(arg, dest_long, arg_num)) { *dest_str = NULL; return 1; - } else if (zend_parse_arg_str_weak(arg, dest_str, arg_num)) { + } else if ((str = zend_parse_arg_str_weak(arg, arg_num)) != NULL) { *dest_long = 0; + *dest_str = str; return 1; } else { return 0; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 17f7ce3263f8..05ad2212b5e2 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -2180,14 +2180,14 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(const zval *arg, zend_long ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(const zval *arg, zend_long *dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, double *dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, double *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num); +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, uint32_t arg_num); +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_or_str_slow(zval *arg, zval **dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_bool_slow(const zval *arg, bool *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num); +ZEND_API zend_string* ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_long_slow(const zval *arg, zend_long *dest, uint32_t arg_num); static zend_always_inline bool zend_parse_arg_bool_ex(const zval *arg, bool *dest, bool *is_null, bool check_null, uint32_t arg_num, bool frameless) @@ -2289,10 +2289,16 @@ static zend_always_inline bool zend_parse_arg_str_ex(zval *arg, zend_string **de } else if (check_null && Z_TYPE_P(arg) == IS_NULL) { *dest = NULL; } else { + zend_string *str; if (frameless) { - return zend_flf_parse_arg_str_slow(arg, dest, arg_num); + str = zend_flf_parse_arg_str_slow(arg, arg_num); } else { - return zend_parse_arg_str_slow(arg, dest, arg_num); + str = zend_parse_arg_str_slow(arg, arg_num); + } + if (str) { + *dest = str; + } else { + return 0; } } return 1; @@ -2526,7 +2532,8 @@ static zend_always_inline bool zend_parse_arg_array_ht_or_str( *dest_str = NULL; } else { *dest_ht = NULL; - return zend_parse_arg_str_slow(arg, dest_str, arg_num); + *dest_str = zend_parse_arg_str_slow(arg, arg_num); + return *dest_str != NULL; } return 1; } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index a4871bc3bebd..ef9660bcae78 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -733,7 +733,6 @@ static bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) { zend_long lval; double dval; - zend_string *str; bool bval; /* Type preference order: int -> float -> string -> bool */ @@ -765,7 +764,7 @@ static bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) ZVAL_DOUBLE(arg, dval); return true; } - if ((type_mask & MAY_BE_STRING) && zend_parse_arg_str_weak(arg, &str, 0)) { + if ((type_mask & MAY_BE_STRING) && zend_parse_arg_str_weak(arg, 0)) { /* on success "arg" is converted to IS_STRING */ return true; } diff --git a/Zend/zend_frameless_function.h b/Zend/zend_frameless_function.h index 241507aa99e7..b6f361f104b0 100644 --- a/Zend/zend_frameless_function.h +++ b/Zend/zend_frameless_function.h @@ -64,7 +64,7 @@ dest_ht = NULL; \ ZVAL_COPY(&str_tmp, arg ## arg_num); \ arg ## arg_num = &str_tmp; \ - if (!zend_flf_parse_arg_str_slow(arg ## arg_num, &dest_str, arg_num)) { \ + if (!(dest_str = zend_flf_parse_arg_str_slow(arg ## arg_num, arg_num))) { \ zend_wrong_parameter_type_error(arg_num, Z_EXPECTED_ARRAY_OR_STRING, arg ## arg_num); \ goto flf_clean; \ } \ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 391b82241e47..dcc11ef589d0 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8823,8 +8823,8 @@ ZEND_VM_COLD_CONST_HANDLER(121, ZEND_STRLEN, CONST|TMP|CV, ANY) strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -8837,7 +8837,7 @@ ZEND_VM_COLD_CONST_HANDLER(121, ZEND_STRLEN, CONST|TMP|CV, ANY) } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index a2e5eac491dc..bb293f490fc7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -6047,8 +6047,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -6061,7 +6061,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -18131,8 +18131,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_STRLEN_SPEC_T strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -18145,7 +18145,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_STRLEN_SPEC_T } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -40722,8 +40722,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_STRLEN_SPEC_C strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -40736,7 +40736,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_STRLEN_SPEC_C } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -58683,8 +58683,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_STRLE strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -58697,7 +58697,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_STRLE } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -70665,8 +70665,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_STRLEN_SPEC_TMP_TA strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -70679,7 +70679,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_STRLEN_SPEC_TMP_TA } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -93156,8 +93156,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_STRLEN_SPEC_CV_TAI strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -93170,7 +93170,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_STRLEN_SPEC_CV_TAI } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; From 493796d26340ac05b848119836c51ec295e82ebd Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sun, 12 Apr 2026 12:19:00 +0100 Subject: [PATCH 2/2] ZPP: Return double directly from zend_parse_arg_double_weak() This idea is based on @ndossche previous PR to reduce codebloat: https://github.com/php/php-src/pull/18436 We can use NAN as a sentinel value as neither int nor string value can actually hold a NAN value. --- Zend/zend_API.c | 30 ++++++++++++++++-------------- Zend/zend_API.h | 7 ++++--- Zend/zend_execute.c | 14 ++++++++------ 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 4ce8aaa9a3f5..86dbc3debd0b 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -624,44 +624,46 @@ ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_long_slow(const zval *arg, zend_l return zend_parse_arg_long_weak(arg, dest, arg_num); } -ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, double *dest, uint32_t arg_num) /* {{{ */ +ZEND_API double ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, uint32_t arg_num) /* {{{ */ { if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) { - *dest = (double)Z_LVAL_P(arg); + return Z_LVAL_P(arg); } else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { zend_long l; + double dval; uint8_t type; - if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), &l, dest)) != IS_DOUBLE)) { + if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), &l, &dval)) != IS_DOUBLE)) { if (EXPECTED(type != 0)) { - *dest = (double)(l); + return (double)(l); } else { - return 0; + return NAN; } + } else { + return dval; } } else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) { if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("float", arg_num)) { - return 0; + return NAN; } - *dest = 0.0; + return 0.0; } else if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) { - *dest = 1.0; + return 1.0; } else { - return 0; + return NAN; } - return 1; } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, double *dest, uint32_t arg_num) /* {{{ */ +ZEND_API double ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, uint32_t arg_num) /* {{{ */ { if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) { /* SSTH Exception: IS_LONG may be accepted instead as IS_DOUBLE */ - *dest = (double)Z_LVAL_P(arg); + return (double)Z_LVAL_P(arg); } else if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { - return 0; + return NAN; } - return zend_parse_arg_double_weak(arg, dest, arg_num); + return zend_parse_arg_double_weak(arg, arg_num); } /* }}} */ diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 05ad2212b5e2..649c6a4b8147 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -2178,8 +2178,8 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(const zval *arg, bool *dest ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(const zval *arg, bool *dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(const zval *arg, zend_long *dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(const zval *arg, zend_long *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, double *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, double *dest, uint32_t arg_num); +ZEND_API double ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, uint32_t arg_num); +ZEND_API double ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, uint32_t arg_num); ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, uint32_t arg_num); ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest, uint32_t arg_num); @@ -2253,7 +2253,8 @@ static zend_always_inline bool zend_parse_arg_double(const zval *arg, double *de *is_null = 1; *dest = 0.0; } else { - return zend_parse_arg_double_slow(arg, dest, arg_num); + *dest = zend_parse_arg_double_slow(arg, arg_num); + return !zend_isnan(*dest); } return 1; } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index ef9660bcae78..e565d0249de5 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -759,10 +759,13 @@ static bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) return false; } } - if ((type_mask & MAY_BE_DOUBLE) && zend_parse_arg_double_weak(arg, &dval, 0)) { - zval_ptr_dtor(arg); - ZVAL_DOUBLE(arg, dval); - return true; + if (type_mask & MAY_BE_DOUBLE) { + dval = zend_parse_arg_double_weak(arg, 0); + if (EXPECTED(!zend_isnan(dval))) { + zval_ptr_dtor(arg); + ZVAL_DOUBLE(arg, dval); + return true; + } } if ((type_mask & MAY_BE_STRING) && zend_parse_arg_str_weak(arg, 0)) { /* on success "arg" is converted to IS_STRING */ @@ -792,7 +795,6 @@ static bool can_convert_to_string(const zval *zv) { static bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_mask, const zval *arg) { zend_long lval; - double dval; bool bval; /* Pass (uint32_t)-1 as arg_num to indicate to ZPP not to emit any deprecation notice, @@ -800,7 +802,7 @@ static bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_mask, if ((type_mask & MAY_BE_LONG) && zend_parse_arg_long_weak(arg, &lval, (uint32_t)-1)) { return true; } - if ((type_mask & MAY_BE_DOUBLE) && zend_parse_arg_double_weak(arg, &dval, (uint32_t)-1)) { + if ((type_mask & MAY_BE_DOUBLE) && !zend_isnan(zend_parse_arg_double_weak(arg, (uint32_t)-1))) { return true; } if ((type_mask & MAY_BE_STRING) && can_convert_to_string(arg)) {