Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
71 changes: 71 additions & 0 deletions Zend/zend_operators.c
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,77 @@ ZEND_API double ZEND_FASTCALL zval_get_double_func(const zval *op) /* {{{ */
}
/* }}} */

/*
* 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:
Expand Down
8 changes: 8 additions & 0 deletions Zend/zend_operators.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
}
Expand Down
Loading