From 658ac80f58fb1f135473d5647b0a3399799acd81 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 21 Aug 2026 18:51:07 +0800 Subject: [PATCH 1/9] Zend: Add `zend_try_get_double` --- UPGRADING.INTERNALS | 3 + Zend/zend_operators.c | 61 ++++++++++ Zend/zend_operators.h | 1 + ext/zend_test/test.c | 19 ++++ ext/zend_test/test.stub.php | 2 + ext/zend_test/test_arginfo.h | 16 ++- ext/zend_test/test_decl.h | 8 +- ext/zend_test/test_legacy_arginfo.h | 10 +- ext/zend_test/tests/zval_try_get_double.phpt | 113 +++++++++++++++++++ 9 files changed, 221 insertions(+), 12 deletions(-) create mode 100644 ext/zend_test/tests/zval_try_get_double.phpt diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index dc2a35cc9c2c..486ca6c32ccc 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -222,6 +222,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES . Added zend_string_ends_with() and related variants. . Added trait support for internal classes. . Added do_php_cli(). + . Added zval_try_get_double(), which converts a zval to a double and reports + conversion failures through a bool pointer, analogous to + zval_try_get_long(). ======================== 2. Build system changes diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index ab8f2c2b54f8..e8a935c0940b 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1059,6 +1059,67 @@ ZEND_API double ZEND_FASTCALL zval_get_double_func(const zval *op) /* {{{ */ } /* }}} */ +ZEND_API double ZEND_FASTCALL zval_try_get_double(const zval *op, bool *failed) /* {{{ */ +{ + *failed = false; +try_again: + switch (Z_TYPE_P(op)) { + case IS_NULL: + case IS_FALSE: + return 0.0; + case IS_TRUE: + return 1.0; + case IS_LONG: + return (double) Z_LVAL_P(op); + case IS_DOUBLE: + return Z_DVAL_P(op); + case IS_STRING: + { + uint8_t type; + zend_long lval; + double dval; + bool trailing_data = false; + + /* For BC reasons we allow errors so that we can warn on leading numeric string */ + type = is_numeric_string_ex(Z_STRVAL_P(op), Z_STRLEN_P(op), &lval, &dval, + /* allow errors */ true, NULL, &trailing_data); + if (type == 0) { + *failed = true; + return 0.0; + } + if (UNEXPECTED(trailing_data)) { + zend_error(E_WARNING, "A non-numeric value encountered"); + if (UNEXPECTED(EG(exception))) { + *failed = true; + return 0.0; + } + } + return type == IS_LONG ? (double) lval : dval; + } + case IS_OBJECT: + { + zval dst; + if (Z_OBJ_HT_P(op)->cast_object(Z_OBJ_P(op), &dst, IS_DOUBLE) == FAILURE + || EG(exception)) { + *failed = true; + return 0.0; + } + ZEND_ASSERT(Z_TYPE(dst) == IS_DOUBLE); + return Z_DVAL(dst); + } + case IS_UNDEF: + case IS_RESOURCE: + case IS_ARRAY: + *failed = true; + return 0.0; + case IS_REFERENCE: + op = Z_REFVAL_P(op); + goto try_again; + default: ZEND_UNREACHABLE(); + } +} +/* }}} */ + static zend_always_inline zend_string* __zval_get_string_func(const zval *op, bool try) /* {{{ */ { try_again: diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index 27aa4fdb0486..c142dbbeca6b 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -323,6 +323,7 @@ ZEND_API void ZEND_FASTCALL convert_to_object(zval *op); ZEND_API zend_long ZEND_FASTCALL zval_get_long_func(const zval *op, bool is_strict); ZEND_API zend_long ZEND_FASTCALL zval_try_get_long(const zval *op, bool *failed); ZEND_API double ZEND_FASTCALL zval_get_double_func(const zval *op); +ZEND_API double ZEND_FASTCALL zval_try_get_double(const zval *op, bool *failed); ZEND_API zend_string* ZEND_FASTCALL zval_get_string_func(const zval *op); ZEND_API zend_string* ZEND_FASTCALL zval_try_get_string_func(const zval *op); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 82bfa8d38e33..d6d5491a7b55 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1138,6 +1138,25 @@ static ZEND_FUNCTION(zend_test_refcount) RETURN_LONG(Z_REFCOUNT_P(value)); } +static ZEND_FUNCTION(zend_test_zval_try_get_double) +{ + zval *value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END(); + + bool failed; + double result = zval_try_get_double(value, &failed); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } + + array_init(return_value); + add_assoc_double(return_value, "value", result); + add_assoc_bool(return_value, "failed", failed); +} + static ZEND_FUNCTION(zend_get_unit_enum) { ZEND_PARSE_PARAMETERS_NONE(); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 3c09668bddb1..286a06ed081f 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -406,6 +406,8 @@ function zend_test_call_with_consumed_args(callable $cb, array $args, int $consu function zend_test_refcount(mixed $value): int {} + function zend_test_zval_try_get_double(mixed $value): array {} + function zend_test_zend_ini_parse_quantity(string $str): int {} function zend_test_zend_ini_parse_uquantity(string $str): int {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 93fdadb7f6b2..ade2f15872a6 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 4d728e740122add9d4c91f5c1abb5f5017690636 + * Stub hash: 0edc039251d06178acf9ec582a9e11e0c4bf7de3 * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, IS_NEVER, 0) @@ -251,6 +251,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_refcount, 0, 1, IS_LON ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_zval_try_get_double, 0, 1, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_zend_ini_parse_quantity, 0, 1, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -490,6 +494,7 @@ static ZEND_FUNCTION(zend_object_init_with_constructor); static ZEND_FUNCTION(zend_call_method_if_exists); static ZEND_FUNCTION(zend_test_call_with_consumed_args); static ZEND_FUNCTION(zend_test_refcount); +static ZEND_FUNCTION(zend_test_zval_try_get_double); static ZEND_FUNCTION(zend_test_zend_ini_parse_quantity); static ZEND_FUNCTION(zend_test_zend_ini_parse_uquantity); static ZEND_FUNCTION(zend_test_zend_ini_str); @@ -554,7 +559,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_trigger_bailout, arginfo_zend_trigger_bailout) ZEND_FE(zend_test_array_return, arginfo_zend_test_array_return) #if (PHP_VERSION_ID >= 80400) - ZEND_RAW_FENTRY("zend_test_nullable_array_return", zif_zend_test_nullable_array_return, arginfo_zend_test_nullable_array_return, ZEND_ACC_COMPILE_TIME_EVAL, NULL, "/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.3\n */") + ZEND_RAW_FENTRY("zend_test_nullable_array_return", zif_zend_test_nullable_array_return, arginfo_zend_test_nullable_array_return, ZEND_ACC_COMPILE_TIME_EVAL, NULL, "/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.3 \n */") #else #if (PHP_VERSION_ID >= 80200) ZEND_RAW_FENTRY("zend_test_nullable_array_return", zif_zend_test_nullable_array_return, arginfo_zend_test_nullable_array_return, ZEND_ACC_COMPILE_TIME_EVAL) @@ -666,6 +671,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_call_method_if_exists, arginfo_zend_call_method_if_exists) ZEND_FE(zend_test_call_with_consumed_args, arginfo_zend_test_call_with_consumed_args) ZEND_FE(zend_test_refcount, arginfo_zend_test_refcount) + ZEND_FE(zend_test_zval_try_get_double, arginfo_zend_test_zval_try_get_double) ZEND_FE(zend_test_zend_ini_parse_quantity, arginfo_zend_test_zend_ini_parse_quantity) ZEND_FE(zend_test_zend_ini_parse_uquantity, arginfo_zend_test_zend_ini_parse_uquantity) ZEND_FE(zend_test_zend_ini_str, arginfo_zend_test_zend_ini_str) @@ -927,7 +933,7 @@ static zend_class_entry *register_class__ZendTestInterface(void) zval const_DUMMY_value; ZVAL_LONG(&const_DUMMY_value, 0); zend_string *const_DUMMY_name = zend_string_init_interned("DUMMY", sizeof("DUMMY") - 1, true); - zend_string *const_DUMMY_comment = zend_string_init_interned("/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.2\n */", 98, 1); + zend_string *const_DUMMY_comment = zend_string_init_interned("/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.2 \n */", 102, 1); zend_declare_class_constant_ex(class_entry, const_DUMMY_name, &const_DUMMY_value, ZEND_ACC_PUBLIC, const_DUMMY_comment); zend_string_release_ex(const_DUMMY_name, true); @@ -1423,13 +1429,13 @@ static zend_class_entry *register_class_ZendTestPropertyAttribute(void) class_entry->ce_flags |= ZEND_ACC_FINAL; #endif #if (PHP_VERSION_ID >= 80400) - class_entry->doc_comment = zend_string_init_interned("/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.1\n */", 82, 1); + class_entry->doc_comment = zend_string_init_interned("/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.1 \n */", 86, 1); #endif zval property_parameter_default_value; ZVAL_UNDEF(&property_parameter_default_value); zend_string *property_parameter_name = zend_string_init("parameter", sizeof("parameter") - 1, true); - zend_string *property_parameter_comment = zend_string_init_interned("/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.4\n */", 98, 1); + zend_string *property_parameter_comment = zend_string_init_interned("/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.4 \n */", 102, 1); zend_declare_typed_property(class_entry, property_parameter_name, &property_parameter_default_value, ZEND_ACC_PUBLIC, property_parameter_comment, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release_ex(property_parameter_name, true); diff --git a/ext/zend_test/test_decl.h b/ext/zend_test/test_decl.h index 7e41dc18eabb..1029c587f998 100644 --- a/ext/zend_test/test_decl.h +++ b/ext/zend_test/test_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 4d728e740122add9d4c91f5c1abb5f5017690636 */ + * Stub hash: 0edc039251d06178acf9ec582a9e11e0c4bf7de3 */ -#ifndef ZEND_TEST_DECL_4d728e740122add9d4c91f5c1abb5f5017690636_H -#define ZEND_TEST_DECL_4d728e740122add9d4c91f5c1abb5f5017690636_H +#ifndef ZEND_TEST_DECL_0edc039251d06178acf9ec582a9e11e0c4bf7de3_H +#define ZEND_TEST_DECL_0edc039251d06178acf9ec582a9e11e0c4bf7de3_H typedef enum zend_enum_ZendTestUnitEnum { ZEND_ENUM_ZendTestUnitEnum_Foo = 1, @@ -27,4 +27,4 @@ typedef enum zend_enum_ZendTestEnumWithInterface { ZEND_ENUM_ZendTestEnumWithInterface_Bar = 2, } zend_enum_ZendTestEnumWithInterface; -#endif /* ZEND_TEST_DECL_4d728e740122add9d4c91f5c1abb5f5017690636_H */ +#endif /* ZEND_TEST_DECL_0edc039251d06178acf9ec582a9e11e0c4bf7de3_H */ diff --git a/ext/zend_test/test_legacy_arginfo.h b/ext/zend_test/test_legacy_arginfo.h index 479a678df778..9ab4d53e6395 100644 --- a/ext/zend_test/test_legacy_arginfo.h +++ b/ext/zend_test/test_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 4d728e740122add9d4c91f5c1abb5f5017690636 + * Stub hash: 0edc039251d06178acf9ec582a9e11e0c4bf7de3 * Has decl header: yes */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, 0) @@ -197,6 +197,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_test_refcount, 0, 0, 1) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() +#define arginfo_zend_test_zval_try_get_double arginfo_zend_test_refcount + #define arginfo_zend_test_zend_ini_parse_quantity arginfo_zend_create_unterminated_string #define arginfo_zend_test_zend_ini_parse_uquantity arginfo_zend_create_unterminated_string @@ -415,6 +417,7 @@ static ZEND_FUNCTION(zend_object_init_with_constructor); static ZEND_FUNCTION(zend_call_method_if_exists); static ZEND_FUNCTION(zend_test_call_with_consumed_args); static ZEND_FUNCTION(zend_test_refcount); +static ZEND_FUNCTION(zend_test_zval_try_get_double); static ZEND_FUNCTION(zend_test_zend_ini_parse_quantity); static ZEND_FUNCTION(zend_test_zend_ini_parse_uquantity); static ZEND_FUNCTION(zend_test_zend_ini_str); @@ -563,6 +566,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_call_method_if_exists, arginfo_zend_call_method_if_exists) ZEND_FE(zend_test_call_with_consumed_args, arginfo_zend_test_call_with_consumed_args) ZEND_FE(zend_test_refcount, arginfo_zend_test_refcount) + ZEND_FE(zend_test_zval_try_get_double, arginfo_zend_test_zval_try_get_double) ZEND_FE(zend_test_zend_ini_parse_quantity, arginfo_zend_test_zend_ini_parse_quantity) ZEND_FE(zend_test_zend_ini_parse_uquantity, arginfo_zend_test_zend_ini_parse_uquantity) ZEND_FE(zend_test_zend_ini_str, arginfo_zend_test_zend_ini_str) @@ -764,7 +768,7 @@ static zend_class_entry *register_class__ZendTestInterface(void) zval const_DUMMY_value; ZVAL_LONG(&const_DUMMY_value, 0); zend_string *const_DUMMY_name = zend_string_init_interned("DUMMY", sizeof("DUMMY") - 1, true); - zend_string *const_DUMMY_comment = zend_string_init_interned("/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.2\n */", 98, 1); + zend_string *const_DUMMY_comment = zend_string_init_interned("/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.2 \n */", 102, 1); zend_declare_class_constant_ex(class_entry, const_DUMMY_name, &const_DUMMY_value, ZEND_ACC_PUBLIC, const_DUMMY_comment); zend_string_release_ex(const_DUMMY_name, true); @@ -1156,7 +1160,7 @@ static zend_class_entry *register_class_ZendTestPropertyAttribute(void) zval property_parameter_default_value; ZVAL_NULL(&property_parameter_default_value); zend_string *property_parameter_name = zend_string_init("parameter", sizeof("parameter") - 1, true); - zend_string *property_parameter_comment = zend_string_init_interned("/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.4\n */", 98, 1); + zend_string *property_parameter_comment = zend_string_init_interned("/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.4 \n */", 102, 1); zend_declare_property_ex(class_entry, property_parameter_name, &property_parameter_default_value, ZEND_ACC_PUBLIC, property_parameter_comment); zend_string_release_ex(property_parameter_name, true); diff --git a/ext/zend_test/tests/zval_try_get_double.phpt b/ext/zend_test/tests/zval_try_get_double.phpt new file mode 100644 index 000000000000..8b17e02ac28a --- /dev/null +++ b/ext/zend_test/tests/zval_try_get_double.phpt @@ -0,0 +1,113 @@ +--TEST-- +zval_try_get_double() conversion semantics +--EXTENSIONS-- +zend_test +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +array(2) { + ["value"]=> + float(0) + ["failed"]=> + bool(false) +} +array(2) { + ["value"]=> + float(0) + ["failed"]=> + bool(false) +} +array(2) { + ["value"]=> + float(1) + ["failed"]=> + bool(false) +} +array(2) { + ["value"]=> + float(42) + ["failed"]=> + bool(false) +} +array(2) { + ["value"]=> + float(42.5) + ["failed"]=> + bool(false) +} +array(2) { + ["value"]=> + float(42) + ["failed"]=> + bool(false) +} +array(2) { + ["value"]=> + float(42.5) + ["failed"]=> + bool(false) +} +array(2) { + ["value"]=> + float(1000) + ["failed"]=> + bool(false) +} +array(2) { + ["value"]=> + float(0) + ["failed"]=> + bool(true) +} +array(2) { + ["value"]=> + float(0) + ["failed"]=> + bool(true) +} +array(2) { + ["value"]=> + float(0) + ["failed"]=> + bool(true) +} + +Warning: A non-numeric value encountered in %s on line %d +array(2) { + ["value"]=> + float(42) + ["failed"]=> + bool(false) +} +A non-numeric value encountered From 55a14f76c21c374ff37ac18fc36203bd5e08db2f Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 21 Aug 2026 18:54:16 +0800 Subject: [PATCH 2/9] more tests --- Zend/zend_operators.c | 11 ++++++++++- ext/zend_test/tests/zval_try_get_double.phpt | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index e8a935c0940b..2ac3154d6963 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1059,7 +1059,7 @@ ZEND_API double ZEND_FASTCALL zval_get_double_func(const zval *op) /* {{{ */ } /* }}} */ -ZEND_API double ZEND_FASTCALL zval_try_get_double(const zval *op, bool *failed) /* {{{ */ +static zend_never_inline double ZEND_FASTCALL zendi_try_get_double(const zval *op, bool *failed) /* {{{ */ { *failed = false; try_again: @@ -1120,6 +1120,15 @@ ZEND_API double ZEND_FASTCALL zval_try_get_double(const zval *op, bool *failed) } /* }}} */ +ZEND_API double ZEND_FASTCALL zval_try_get_double(const zval *op, bool *failed) +{ + if (EXPECTED(Z_TYPE_P(op) == IS_DOUBLE)) { + *failed = false; + return Z_DVAL_P(op); + } + return zendi_try_get_double(op, failed); +} + static zend_always_inline zend_string* __zval_get_string_func(const zval *op, bool try) /* {{{ */ { try_again: diff --git a/ext/zend_test/tests/zval_try_get_double.phpt b/ext/zend_test/tests/zval_try_get_double.phpt index 8b17e02ac28a..248c3a44b749 100644 --- a/ext/zend_test/tests/zval_try_get_double.phpt +++ b/ext/zend_test/tests/zval_try_get_double.phpt @@ -16,6 +16,8 @@ foreach ([ "1e3", "not numeric", [], + new FloatCastableNoOperations(42.5), + new LongCastableNoOperations(42), ] as $value) { var_dump(zend_test_zval_try_get_double($value)); } @@ -96,6 +98,18 @@ array(2) { ["failed"]=> bool(true) } +array(2) { + ["value"]=> + float(42.5) + ["failed"]=> + bool(false) +} +array(2) { + ["value"]=> + float(0) + ["failed"]=> + bool(true) +} array(2) { ["value"]=> float(0) From e43d922a0cf47bd2ef6eeb1d110f464ed309fc84 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 21 Aug 2026 20:07:57 +0800 Subject: [PATCH 3/9] fix CI --- ext/zend_test/test_arginfo.h | 8 ++++---- ext/zend_test/test_legacy_arginfo.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index ade2f15872a6..26bbed424e07 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -559,7 +559,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_trigger_bailout, arginfo_zend_trigger_bailout) ZEND_FE(zend_test_array_return, arginfo_zend_test_array_return) #if (PHP_VERSION_ID >= 80400) - ZEND_RAW_FENTRY("zend_test_nullable_array_return", zif_zend_test_nullable_array_return, arginfo_zend_test_nullable_array_return, ZEND_ACC_COMPILE_TIME_EVAL, NULL, "/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.3 \n */") + ZEND_RAW_FENTRY("zend_test_nullable_array_return", zif_zend_test_nullable_array_return, arginfo_zend_test_nullable_array_return, ZEND_ACC_COMPILE_TIME_EVAL, NULL, "/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.3\n */") #else #if (PHP_VERSION_ID >= 80200) ZEND_RAW_FENTRY("zend_test_nullable_array_return", zif_zend_test_nullable_array_return, arginfo_zend_test_nullable_array_return, ZEND_ACC_COMPILE_TIME_EVAL) @@ -933,7 +933,7 @@ static zend_class_entry *register_class__ZendTestInterface(void) zval const_DUMMY_value; ZVAL_LONG(&const_DUMMY_value, 0); zend_string *const_DUMMY_name = zend_string_init_interned("DUMMY", sizeof("DUMMY") - 1, true); - zend_string *const_DUMMY_comment = zend_string_init_interned("/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.2 \n */", 102, 1); + zend_string *const_DUMMY_comment = zend_string_init_interned("/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.2\n */", 98, 1); zend_declare_class_constant_ex(class_entry, const_DUMMY_name, &const_DUMMY_value, ZEND_ACC_PUBLIC, const_DUMMY_comment); zend_string_release_ex(const_DUMMY_name, true); @@ -1429,13 +1429,13 @@ static zend_class_entry *register_class_ZendTestPropertyAttribute(void) class_entry->ce_flags |= ZEND_ACC_FINAL; #endif #if (PHP_VERSION_ID >= 80400) - class_entry->doc_comment = zend_string_init_interned("/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.1 \n */", 86, 1); + class_entry->doc_comment = zend_string_init_interned("/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.1\n */", 82, 1); #endif zval property_parameter_default_value; ZVAL_UNDEF(&property_parameter_default_value); zend_string *property_parameter_name = zend_string_init("parameter", sizeof("parameter") - 1, true); - zend_string *property_parameter_comment = zend_string_init_interned("/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.4 \n */", 102, 1); + zend_string *property_parameter_comment = zend_string_init_interned("/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.4\n */", 98, 1); zend_declare_typed_property(class_entry, property_parameter_name, &property_parameter_default_value, ZEND_ACC_PUBLIC, property_parameter_comment, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release_ex(property_parameter_name, true); diff --git a/ext/zend_test/test_legacy_arginfo.h b/ext/zend_test/test_legacy_arginfo.h index 9ab4d53e6395..9dfd52a5cda5 100644 --- a/ext/zend_test/test_legacy_arginfo.h +++ b/ext/zend_test/test_legacy_arginfo.h @@ -768,7 +768,7 @@ static zend_class_entry *register_class__ZendTestInterface(void) zval const_DUMMY_value; ZVAL_LONG(&const_DUMMY_value, 0); zend_string *const_DUMMY_name = zend_string_init_interned("DUMMY", sizeof("DUMMY") - 1, true); - zend_string *const_DUMMY_comment = zend_string_init_interned("/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.2 \n */", 102, 1); + zend_string *const_DUMMY_comment = zend_string_init_interned("/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.2\n */", 98, 1); zend_declare_class_constant_ex(class_entry, const_DUMMY_name, &const_DUMMY_value, ZEND_ACC_PUBLIC, const_DUMMY_comment); zend_string_release_ex(const_DUMMY_name, true); @@ -1160,7 +1160,7 @@ static zend_class_entry *register_class_ZendTestPropertyAttribute(void) zval property_parameter_default_value; ZVAL_NULL(&property_parameter_default_value); zend_string *property_parameter_name = zend_string_init("parameter", sizeof("parameter") - 1, true); - zend_string *property_parameter_comment = zend_string_init_interned("/** \n * \"Lorem ipsum\" \n * @see https://www.php.net \n * @since 8.4 \n */", 102, 1); + zend_string *property_parameter_comment = zend_string_init_interned("/**\n * \"Lorem ipsum\"\n * @see https://www.php.net\n * @since 8.4\n */", 98, 1); zend_declare_property_ex(class_entry, property_parameter_name, &property_parameter_default_value, ZEND_ACC_PUBLIC, property_parameter_comment); zend_string_release_ex(property_parameter_name, true); From d6e895a5967730a6fa151f2e3224a009b9e6766e Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 21 Aug 2026 20:48:15 +0800 Subject: [PATCH 4/9] feedback --- Zend/zend_operators.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 2ac3154d6963..9ddb490d26eb 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1059,7 +1059,7 @@ ZEND_API double ZEND_FASTCALL zval_get_double_func(const zval *op) /* {{{ */ } /* }}} */ -static zend_never_inline double ZEND_FASTCALL zendi_try_get_double(const zval *op, bool *failed) /* {{{ */ +static zend_never_inline double ZEND_FASTCALL zval_try_get_double_func(const zval *op, bool *failed) /* {{{ */ { *failed = false; try_again: @@ -1107,7 +1107,6 @@ static zend_never_inline double ZEND_FASTCALL zendi_try_get_double(const zval *o ZEND_ASSERT(Z_TYPE(dst) == IS_DOUBLE); return Z_DVAL(dst); } - case IS_UNDEF: case IS_RESOURCE: case IS_ARRAY: *failed = true; @@ -1126,7 +1125,7 @@ ZEND_API double ZEND_FASTCALL zval_try_get_double(const zval *op, bool *failed) *failed = false; return Z_DVAL_P(op); } - return zendi_try_get_double(op, failed); + return zval_try_get_double_func(op, failed); } static zend_always_inline zend_string* __zval_get_string_func(const zval *op, bool try) /* {{{ */ From b97b4d8e3351009300160108ae81e36fc3f5d0bd Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 21 Aug 2026 20:58:28 +0800 Subject: [PATCH 5/9] feedback_v2 --- Zend/zend_operators.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 9ddb490d26eb..08d3afb14078 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1107,6 +1107,8 @@ static zend_never_inline double ZEND_FASTCALL zval_try_get_double_func(const zva ZEND_ASSERT(Z_TYPE(dst) == IS_DOUBLE); return Z_DVAL(dst); } + /* Uninitialized typed properties may be represented as IS_UNDEF here. */ + case IS_UNDEF: case IS_RESOURCE: case IS_ARRAY: *failed = true; From b686c8d2158b4a4306c14781456ce022e76d12c0 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 21 Aug 2026 20:59:21 +0800 Subject: [PATCH 6/9] Revert "feedback_v2" This reverts commit b97b4d8e3351009300160108ae81e36fc3f5d0bd. --- Zend/zend_operators.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 08d3afb14078..9ddb490d26eb 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1107,8 +1107,6 @@ static zend_never_inline double ZEND_FASTCALL zval_try_get_double_func(const zva ZEND_ASSERT(Z_TYPE(dst) == IS_DOUBLE); return Z_DVAL(dst); } - /* Uninitialized typed properties may be represented as IS_UNDEF here. */ - case IS_UNDEF: case IS_RESOURCE: case IS_ARRAY: *failed = true; From 7a7840233271fdde427b4ec165dbb02eccaaa7e3 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 21 Aug 2026 21:04:12 +0800 Subject: [PATCH 7/9] feedback_v2 --- Zend/zend_operators.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 9ddb490d26eb..08d3afb14078 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1107,6 +1107,8 @@ static zend_never_inline double ZEND_FASTCALL zval_try_get_double_func(const zva ZEND_ASSERT(Z_TYPE(dst) == IS_DOUBLE); return Z_DVAL(dst); } + /* Uninitialized typed properties may be represented as IS_UNDEF here. */ + case IS_UNDEF: case IS_RESOURCE: case IS_ARRAY: *failed = true; From c7e944df6cf3b2457a03c9f5688cea1cf4a33014 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sat, 22 Aug 2026 01:16:31 +0800 Subject: [PATCH 8/9] feedback_v3 --- Zend/zend_operators.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 08d3afb14078..9ddb490d26eb 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1107,8 +1107,6 @@ static zend_never_inline double ZEND_FASTCALL zval_try_get_double_func(const zva ZEND_ASSERT(Z_TYPE(dst) == IS_DOUBLE); return Z_DVAL(dst); } - /* Uninitialized typed properties may be represented as IS_UNDEF here. */ - case IS_UNDEF: case IS_RESOURCE: case IS_ARRAY: *failed = true; From 6bab7be606d73da3c3f3fe4d7b5287260dc56ddf Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sat, 22 Aug 2026 01:55:09 +0800 Subject: [PATCH 9/9] Test exception type Co-authored-by: NickSdot <32384907+NickSdot@users.noreply.github.com> --- ext/zend_test/tests/zval_try_get_double.phpt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/zend_test/tests/zval_try_get_double.phpt b/ext/zend_test/tests/zval_try_get_double.phpt index 248c3a44b749..de42f872c593 100644 --- a/ext/zend_test/tests/zval_try_get_double.phpt +++ b/ext/zend_test/tests/zval_try_get_double.phpt @@ -32,8 +32,8 @@ set_error_handler(static function (int $errno, string $errstr): never { }); try { zend_test_zval_try_get_double("42 with trailing data"); -} catch (Exception $e) { - echo $e->getMessage(), "\n"; +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; } ?> @@ -124,4 +124,4 @@ array(2) { ["failed"]=> bool(false) } -A non-numeric value encountered +Exception: A non-numeric value encountered