From 0f82bdb4a3ebe3117ccf5eb5a4fc375591f2ebb0 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sun, 20 Jul 2025 14:40:58 +0200 Subject: [PATCH] Improve trim on numeric string --- .../Php/LtrimFunctionReturnTypeExtension.php | 4 +++ ...TrimFunctionDynamicReturnTypeExtension.php | 24 ++++++++------ .../Analyser/nsrt/numeric-string-trim.php | 33 +++++++++++++++++++ 3 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/numeric-string-trim.php diff --git a/src/Type/Php/LtrimFunctionReturnTypeExtension.php b/src/Type/Php/LtrimFunctionReturnTypeExtension.php index 77cd8cbb7b..2db0c7be4e 100644 --- a/src/Type/Php/LtrimFunctionReturnTypeExtension.php +++ b/src/Type/Php/LtrimFunctionReturnTypeExtension.php @@ -7,6 +7,7 @@ use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Reflection\FunctionReflection; use PHPStan\Type\Accessory\AccessoryLowercaseStringType; +use PHPStan\Type\Accessory\AccessoryNumericStringType; use PHPStan\Type\Accessory\AccessoryUppercaseStringType; use PHPStan\Type\ClassStringType; use PHPStan\Type\Constant\ConstantStringType; @@ -17,6 +18,7 @@ use PHPStan\Type\TypeCombinator; use function count; use function ltrim; +use function preg_match; #[AutowiredService] final class LtrimFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension @@ -69,6 +71,8 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, } } elseif ($trimConstantString->getValue() === '\\' && $string->isClassString()->yes()) { $result[] = new ClassStringType(); + } elseif (preg_match('/\d/', $trimConstantString->getValue()) === 0 && $string->isNumericString()->yes()) { + $result[] = new AccessoryNumericStringType(); } else { return $defaultType; } diff --git a/src/Type/Php/TrimFunctionDynamicReturnTypeExtension.php b/src/Type/Php/TrimFunctionDynamicReturnTypeExtension.php index 3cf845f523..fd016b7650 100644 --- a/src/Type/Php/TrimFunctionDynamicReturnTypeExtension.php +++ b/src/Type/Php/TrimFunctionDynamicReturnTypeExtension.php @@ -7,6 +7,7 @@ use PHPStan\DependencyInjection\AutowiredService; use PHPStan\Reflection\FunctionReflection; use PHPStan\Type\Accessory\AccessoryLowercaseStringType; +use PHPStan\Type\Accessory\AccessoryNumericStringType; use PHPStan\Type\Accessory\AccessoryUppercaseStringType; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; @@ -16,6 +17,7 @@ use PHPStan\Type\TypeCombinator; use function count; use function in_array; +use function preg_match; use function rtrim; use function trim; @@ -66,18 +68,20 @@ public function getTypeFromFunctionCall( $functionName = $functionReflection->getName(); foreach ($trimConstantStrings as $trimConstantString) { - if (count($stringConstantStrings) === 0) { + if (count($stringConstantStrings) > 0) { + foreach ($stringConstantStrings as $stringConstantString) { + $result[] = new ConstantStringType( + $functionName === 'rtrim' + ? rtrim($stringConstantString->getValue(), $trimConstantString->getValue()) + : trim($stringConstantString->getValue(), $trimConstantString->getValue()), + true, + ); + } + } elseif (preg_match('/\d/', $trimConstantString->getValue()) === 0 && $stringType->isNumericString()->yes()) { + $result[] = new AccessoryNumericStringType(); + } else { return $defaultType; } - - foreach ($stringConstantStrings as $stringConstantString) { - $result[] = new ConstantStringType( - $functionName === 'rtrim' - ? rtrim($stringConstantString->getValue(), $trimConstantString->getValue()) - : trim($stringConstantString->getValue(), $trimConstantString->getValue()), - true, - ); - } } return TypeCombinator::union(...$result); diff --git a/tests/PHPStan/Analyser/nsrt/numeric-string-trim.php b/tests/PHPStan/Analyser/nsrt/numeric-string-trim.php new file mode 100644 index 0000000000..9a35224ab0 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/numeric-string-trim.php @@ -0,0 +1,33 @@ +