From da3a3b63b6bb06f6fecd6a9ac1f8e8978aef0369 Mon Sep 17 00:00:00 2001 From: "ondrej@mirtes.cz" Date: Sat, 7 Nov 2026 07:56:44 +0000 Subject: [PATCH] Turn a NAN float type into the 'NAN' string type without casting it PHP 8.5 warns "unexpected NAN value was coerced to string" when NAN is cast to string, and zend_double_to_str() in the turbo mirror warns the same way. That made PHPStan report a phpstan.php error while analysing code where a float narrows to NAN, such as $x >= 10 || ... . Amp-Thread-ID: https://ampcode.com/threads/T-01a1ad6c-dec2-72ff-bbe8-5b3b62746c94 --- src/Type/Constant/ConstantFloatType.php | 4 ++++ tests/PHPStan/Analyser/nsrt/nan-to-string.php | 14 ++++++++++++++ turbo-ext/src/ConstantFloatType.cpp | 7 +++++-- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/nan-to-string.php diff --git a/src/Type/Constant/ConstantFloatType.php b/src/Type/Constant/ConstantFloatType.php index 008e5cc7468..cdc3b75cda8 100644 --- a/src/Type/Constant/ConstantFloatType.php +++ b/src/Type/Constant/ConstantFloatType.php @@ -108,6 +108,10 @@ public function toString(): Type ]); } + if (is_nan($this->value)) { + return new ConstantStringType('NAN'); + } + return new ConstantStringType((string) $this->value); } diff --git a/tests/PHPStan/Analyser/nsrt/nan-to-string.php b/tests/PHPStan/Analyser/nsrt/nan-to-string.php new file mode 100644 index 00000000000..a5200bcf489 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/nan-to-string.php @@ -0,0 +1,14 @@ += 8.0 + +namespace NanToString; + +use function PHPStan\Testing\assertType; + +function doFoo(): void +{ + $nan = NAN; + assertType("'NAN'", (string) $nan); + assertType("'NAN'", $nan . ''); + assertType("'INF'", (string) INF); + assertType("'-INF'", (string) -INF); +} diff --git a/turbo-ext/src/ConstantFloatType.cpp b/turbo-ext/src/ConstantFloatType.cpp index af15a851b46..5c99caa6fb9 100644 --- a/turbo-ext/src/ConstantFloatType.cpp +++ b/turbo-ext/src/ConstantFloatType.cpp @@ -177,8 +177,10 @@ class ConstantFloatType } /* new UnionType([new ConstantStringType('0'), new ConstantStringType('-0')]) - * for 0.0 (and -0.0, which === 0.0), new ConstantStringType((string) $this->value) - * otherwise — the cast under the current `precision` ini */ + * for 0.0 (and -0.0, which === 0.0), new ConstantStringType('NAN') for a + * NAN, which PHP 8.5's zend_double_to_str() would convert with a warning, + * new ConstantStringType((string) $this->value) otherwise — the cast under + * the current `precision` ini */ zv::Val toString() const { double v = 0.0; @@ -193,6 +195,7 @@ class ConstantFloatType types.push(std::move(negativeZero)); return pt_type_new_union(std::move(types)); } + if (std::isnan(v)) return pt_type_new_constant_string("NAN", 3); zend_string *str = zend_double_to_str(v); zv::Val result = pt_type_new_constant_string(ZSTR_VAL(str), ZSTR_LEN(str)); zend_string_release(str);