From 9b5b0bd9e58df2c712c138212e77895acc98fafd Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 26 Aug 2025 16:52:34 +0200 Subject: [PATCH 1/7] Increment on non-numeric string is deprecated, use str_increment() instead --- src/Analyser/MutatingScope.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 57d047fbf8..db6179e556 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -150,6 +150,7 @@ use function array_values; use function count; use function explode; +use function function_exists; use function get_class; use function implode; use function in_array; @@ -160,6 +161,8 @@ use function ltrim; use function md5; use function sprintf; +use function str_decrement; +use function str_increment; use function str_starts_with; use function strlen; use function strtolower; @@ -1733,10 +1736,18 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu foreach ($varScalars as $varValue) { if ($node instanceof Expr\PreInc) { if (!is_bool($varValue)) { - ++$varValue; + if (function_exists('str_increment')) { + $varValue = str_increment($varValue); + } else { + ++$varValue; + } } } elseif (is_numeric($varValue)) { - --$varValue; + if (function_exists('str_decrement')) { + $varValue = str_decrement($varValue); + } else { + --$varValue; + } } $newTypes[] = $this->getTypeFromValue($varValue); From 7c2e84a7f4fc4c44c2e94d835488ace12f6736ef Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 26 Aug 2025 16:59:47 +0200 Subject: [PATCH 2/7] Update MutatingScope.php --- src/Analyser/MutatingScope.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index db6179e556..f050680348 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -161,7 +161,6 @@ use function ltrim; use function md5; use function sprintf; -use function str_decrement; use function str_increment; use function str_starts_with; use function strlen; @@ -1736,18 +1735,14 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu foreach ($varScalars as $varValue) { if ($node instanceof Expr\PreInc) { if (!is_bool($varValue)) { - if (function_exists('str_increment')) { + if (function_exists('str_increment') && is_string($varValue)) { $varValue = str_increment($varValue); } else { ++$varValue; } } } elseif (is_numeric($varValue)) { - if (function_exists('str_decrement')) { - $varValue = str_decrement($varValue); - } else { - --$varValue; - } + --$varValue; } $newTypes[] = $this->getTypeFromValue($varValue); From 5f0139df10699d3cca8e491177a58de23ab97d7c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 28 Aug 2025 09:38:11 +0200 Subject: [PATCH 3/7] fix --- src/Analyser/MutatingScope.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index f050680348..774ebaebec 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -1734,7 +1734,11 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu foreach ($varScalars as $varValue) { if ($node instanceof Expr\PreInc) { - if (!is_bool($varValue)) { + // until PHP 8.5 it was valid to increment an empty string. + // see https://github.com/php/php-src/issues/19597 + if ($varValue === '') { + $varValue = '1'; + } elseif (!is_bool($varValue)) { if (function_exists('str_increment') && is_string($varValue)) { $varValue = str_increment($varValue); } else { From 1e60552bcde346ea6b7cc388f38a68ca6c4825de Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 28 Aug 2025 09:48:11 +0200 Subject: [PATCH 4/7] try smaller fix --- src/Analyser/MutatingScope.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 774ebaebec..cac0b313d8 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -150,7 +150,6 @@ use function array_values; use function count; use function explode; -use function function_exists; use function get_class; use function implode; use function in_array; @@ -161,7 +160,6 @@ use function ltrim; use function md5; use function sprintf; -use function str_increment; use function str_starts_with; use function strlen; use function strtolower; @@ -1739,11 +1737,7 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu if ($varValue === '') { $varValue = '1'; } elseif (!is_bool($varValue)) { - if (function_exists('str_increment') && is_string($varValue)) { - $varValue = str_increment($varValue); - } else { - ++$varValue; - } + ++$varValue; } } elseif (is_numeric($varValue)) { --$varValue; From bac6284afb10eb8ba218c125db994ae26a878485 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 29 Aug 2025 20:06:32 +0200 Subject: [PATCH 5/7] fix deprecations / add tests --- src/Analyser/MutatingScope.php | 16 +++++++++---- ...ntDecrementFunctionReturnTypeExtension.php | 11 +++++++++ tests/PHPStan/Analyser/nsrt/pre-dec.php | 23 +++++++++++++++++++ tests/PHPStan/Analyser/nsrt/pre-inc.php | 23 +++++++++++++++++++ 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/pre-dec.php create mode 100644 tests/PHPStan/Analyser/nsrt/pre-inc.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index cac0b313d8..611a8b594a 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -150,6 +150,7 @@ use function array_values; use function count; use function explode; +use function function_exists; use function get_class; use function implode; use function in_array; @@ -160,6 +161,7 @@ use function ltrim; use function md5; use function sprintf; +use function str_increment; use function str_starts_with; use function strlen; use function strtolower; @@ -1731,16 +1733,22 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu $newTypes = []; foreach ($varScalars as $varValue) { + // until PHP 8.5 it was valid to increment/decrement an empty string. + // see https://github.com/php/php-src/issues/19597 if ($node instanceof Expr\PreInc) { - // until PHP 8.5 it was valid to increment an empty string. - // see https://github.com/php/php-src/issues/19597 if ($varValue === '') { $varValue = '1'; + } elseif (is_string($varValue) && function_exists('str_increment')) { + $varValue = str_increment($varValue); } elseif (!is_bool($varValue)) { ++$varValue; } - } elseif (is_numeric($varValue)) { - --$varValue; + } else { + if ($varValue === '') { + $varValue = -1; + } elseif (is_numeric($varValue)) { + --$varValue; + } } $newTypes[] = $this->getTypeFromValue($varValue); diff --git a/src/Type/Php/StrIncrementDecrementFunctionReturnTypeExtension.php b/src/Type/Php/StrIncrementDecrementFunctionReturnTypeExtension.php index 5c936e5f72..208d5b0b9e 100644 --- a/src/Type/Php/StrIncrementDecrementFunctionReturnTypeExtension.php +++ b/src/Type/Php/StrIncrementDecrementFunctionReturnTypeExtension.php @@ -6,6 +6,7 @@ use PHPStan\Analyser\Scope; use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Reflection\FunctionReflection; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; use PHPStan\Type\ErrorType; @@ -13,6 +14,7 @@ use PHPStan\Type\TypeCombinator; use function chr; use function count; +use function function_exists; use function implode; use function in_array; use function is_float; @@ -21,6 +23,7 @@ use function is_string; use function ord; use function preg_match; +use function str_increment; use function str_split; use function stripos; @@ -103,6 +106,14 @@ private function increment(string $s): string } } + if ($s === '') { + throw new ShouldNotHappenException(); + } + + if (function_exists('str_increment')) { + return str_increment($s); + } + return (string) ++$s; } diff --git a/tests/PHPStan/Analyser/nsrt/pre-dec.php b/tests/PHPStan/Analyser/nsrt/pre-dec.php new file mode 100644 index 0000000000..7e4bed4d1d --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/pre-dec.php @@ -0,0 +1,23 @@ + Date: Fri, 29 Aug 2025 20:35:36 +0200 Subject: [PATCH 6/7] fix build on php < 8.3 --- build/ignore-by-php-version.neon.php | 5 +++++ build/str-increment.neon | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 build/str-increment.neon diff --git a/build/ignore-by-php-version.neon.php b/build/ignore-by-php-version.neon.php index 8aeff82810..d9bb8452e3 100644 --- a/build/ignore-by-php-version.neon.php +++ b/build/ignore-by-php-version.neon.php @@ -38,6 +38,11 @@ } else { $includes[] = __DIR__ . '/new-phpunit.neon'; } + +if (PHP_VERSION_ID < 80300) { + $includes[] = __DIR__ . '/str-increment.neon'; +} + $config = []; $config['includes'] = $includes; diff --git a/build/str-increment.neon b/build/str-increment.neon new file mode 100644 index 0000000000..327eab4361 --- /dev/null +++ b/build/str-increment.neon @@ -0,0 +1,4 @@ +parameters: + ignoreErrors: + - + message: '#^Used function str_increment not found\.#' From b28e301df940db69172c211ccc3332d3b742b5a2 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 29 Aug 2025 20:43:21 +0200 Subject: [PATCH 7/7] fix type --- src/Analyser/MutatingScope.php | 6 +++++- tests/PHPStan/Analyser/nsrt/pre-inc.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 611a8b594a..534722b2ad 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -1738,7 +1738,11 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu if ($node instanceof Expr\PreInc) { if ($varValue === '') { $varValue = '1'; - } elseif (is_string($varValue) && function_exists('str_increment')) { + } elseif ( + is_string($varValue) + && !is_numeric($varValue) + && function_exists('str_increment') + ) { $varValue = str_increment($varValue); } elseif (!is_bool($varValue)) { ++$varValue; diff --git a/tests/PHPStan/Analyser/nsrt/pre-inc.php b/tests/PHPStan/Analyser/nsrt/pre-inc.php index 64f28c09ab..45b6f25c41 100644 --- a/tests/PHPStan/Analyser/nsrt/pre-inc.php +++ b/tests/PHPStan/Analyser/nsrt/pre-inc.php @@ -13,7 +13,7 @@ function doFoo() { function doFoo2() { $s = '123'; ++$s; - assertType("'124'", $s); + assertType("124", $s); } function doFooBar() {