Zend: Add zval_try_get_double() (#23398)

Add a failure-reporting double conversion API analogous to
zval_try_get_long(). Non-convertible values set the failure flag instead
of being silently coerced.

Use zval_try_get_long()-compatible numeric-string semantics, including
warnings for trailing data, while preserving signed zero. Document that
the return value must not be used on failure and that an exception may
already be pending.
This commit is contained in:
Weilin Du authored and GitHub committed 2026-09-07 00:03:08 +08:00
1 parent 1d004f0e71
commit 21034a8d4f
3 files changed
+86

No files matched your search

+7
View File
@@ -227,6 +227,13 @@ 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 defined zval to a double and
reports conversion failures through a bool pointer. String conversion uses
the numeric-string semantics of zval_try_get_long(), rather than the
zend_strtod() semantics of zval_get_double(); non-numeric strings such as
"INF" and "NAN" fail, while leading-numeric strings emit E_WARNING. When
*failed is true, the returned value must not be used and an exception may
already be pending. Passing an IS_UNDEF zval is a caller error.
========================
2. Build system changes
+71
View File
@@ -1058,6 +1058,77 @@ try_again:
}
/* }}} */
/*
* Strings use zval_try_get_long() numeric-string semantics. If *failed is true,
* the return value must not be used and an exception may be pending. The input
* must not be IS_UNDEF.
*/
ZEND_API double ZEND_FASTCALL zval_try_get_double_func(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;
double result;
bool trailing_data = false;
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 (type == IS_DOUBLE) {
result = dval;
} else if (UNEXPECTED(lval == 0)) {
result = zend_strtod(Z_STRVAL_P(op), NULL);
} else {
result = (double) lval;
}
if (UNEXPECTED(trailing_data)) {
zend_error(E_WARNING, "A non-numeric value encountered");
if (UNEXPECTED(EG(exception))) {
*failed = true;
return 0.0;
}
}
return result;
}
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_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:
+8
View File
@@ -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_func(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);
@@ -335,6 +336,13 @@ static zend_always_inline zend_long zval_get_long_ex(const zval *op, bool is_str
static zend_always_inline double zval_get_double(const zval *op) {
return EXPECTED(Z_TYPE_P(op) == IS_DOUBLE) ? Z_DVAL_P(op) : zval_get_double_func(op);
}
static zend_always_inline double 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 zval_try_get_double_func(op, failed);
}
static zend_always_inline zend_string *zval_get_string(const zval *op) {
return EXPECTED(Z_TYPE_P(op) == IS_STRING) ? zend_string_copy(Z_STR_P(op)) : zval_get_string_func(op);
}