diff --git a/src/Php/PhpVersions.php b/src/Php/PhpVersions.php index 7ed0fe5c65c..f6c8bfc2f18 100644 --- a/src/Php/PhpVersions.php +++ b/src/Php/PhpVersions.php @@ -188,9 +188,84 @@ public function supportsDeprecatedTraits(): TrinaryLogic return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result; } + public function arrayFunctionsReturnNullWithNonArray(): TrinaryLogic + { + return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result; + } + + public function hasDateTimeExceptions(): TrinaryLogic + { + return IntegerRangeType::fromInterval(80300, null)->isSuperTypeOf($this->phpVersions)->result; + } + + public function hasFilterThrowOnFailureConstant(): TrinaryLogic + { + return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result; + } + + public function hasPDOSubclasses(): TrinaryLogic + { + return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result; + } + + public function hasStricterRoundFunctions(): TrinaryLogic + { + return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result; + } + + public function highlightStringDoesNotReturnFalse(): TrinaryLogic + { + return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result; + } + + public function isEmptyStringValidAliasForNoneInMbSubstituteCharacter(): TrinaryLogic + { + return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result; + } + + public function isNullValidArgInMbSubstituteCharacter(): TrinaryLogic + { + return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result; + } + + public function isNumericStringValidArgInMbSubstituteCharacter(): TrinaryLogic + { + return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result; + } + + public function strSplitReturnsEmptyArray(): TrinaryLogic + { + return IntegerRangeType::fromInterval(80200, null)->isSuperTypeOf($this->phpVersions)->result; + } + + public function substrReturnFalseInsteadOfEmptyString(): TrinaryLogic + { + return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result; + } + + public function supportsAllUnicodeScalarCodePointsInMbSubstituteCharacter(): TrinaryLogic + { + return IntegerRangeType::fromInterval(70200, null)->isSuperTypeOf($this->phpVersions)->result; + } + public function supportsHhPrintfSpecifier(): TrinaryLogic { return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result; } + public function supportsPassNoneEncodings(): TrinaryLogic + { + return IntegerRangeType::fromInterval(null, 70299)->isSuperTypeOf($this->phpVersions)->result; + } + + public function throwsOnInvalidMbStringEncoding(): TrinaryLogic + { + return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result; + } + + public function throwsValueErrorForInvalidRoundingMode(): TrinaryLogic + { + return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result; + } + } diff --git a/src/Reflection/BetterReflection/Type/AdapterReflectionEnumCaseDynamicReturnTypeExtension.php b/src/Reflection/BetterReflection/Type/AdapterReflectionEnumCaseDynamicReturnTypeExtension.php index 4e1cfbcea63..0bd0ab18198 100644 --- a/src/Reflection/BetterReflection/Type/AdapterReflectionEnumCaseDynamicReturnTypeExtension.php +++ b/src/Reflection/BetterReflection/Type/AdapterReflectionEnumCaseDynamicReturnTypeExtension.php @@ -5,7 +5,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionType; -use PHPStan\Php\PhpVersion; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; @@ -22,7 +21,7 @@ final class AdapterReflectionEnumCaseDynamicReturnTypeExtension implements Dynam /** * @param class-string $class */ - public function __construct(private PhpVersion $phpVersion, private string $class) + public function __construct(private string $class) { } @@ -41,7 +40,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { - if ($this->phpVersion->getVersionId() >= 80000) { + if (!$scope->getPhpVersion()->supportsNativeReflectionAdapterReturnTypes()->no()) { return null; } diff --git a/src/Reflection/BetterReflection/Type/AdapterReflectionEnumDynamicReturnTypeExtension.php b/src/Reflection/BetterReflection/Type/AdapterReflectionEnumDynamicReturnTypeExtension.php index 2d3920e771c..4dc2c2194db 100644 --- a/src/Reflection/BetterReflection/Type/AdapterReflectionEnumDynamicReturnTypeExtension.php +++ b/src/Reflection/BetterReflection/Type/AdapterReflectionEnumDynamicReturnTypeExtension.php @@ -9,7 +9,6 @@ use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnum; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionNamedType; use PHPStan\DependencyInjection\AutowiredService; -use PHPStan\Php\PhpVersion; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\Accessory\AccessoryNonEmptyStringType; use PHPStan\Type\Constant\ConstantBooleanType; @@ -27,10 +26,6 @@ final class AdapterReflectionEnumDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - public function __construct(private PhpVersion $phpVersion) - { - } - public function getClass(): string { return ReflectionEnum::class; @@ -52,7 +47,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { - if ($this->phpVersion->getVersionId() >= 80000) { + if (!$scope->getPhpVersion()->supportsNativeReflectionAdapterReturnTypes()->no()) { return null; } diff --git a/src/Reflection/PHPStan/NativeReflectionEnumReturnDynamicReturnTypeExtension.php b/src/Reflection/PHPStan/NativeReflectionEnumReturnDynamicReturnTypeExtension.php index fc9b44f79a6..5da1c9c82a8 100644 --- a/src/Reflection/PHPStan/NativeReflectionEnumReturnDynamicReturnTypeExtension.php +++ b/src/Reflection/PHPStan/NativeReflectionEnumReturnDynamicReturnTypeExtension.php @@ -5,7 +5,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClass; -use PHPStan\Php\PhpVersion; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\ObjectType; @@ -17,7 +16,7 @@ final class NativeReflectionEnumReturnDynamicReturnTypeExtension implements Dyna /** * @param class-string $className */ - public function __construct(private PhpVersion $phpVersion, private string $className, private string $methodName) + public function __construct(private string $className, private string $methodName) { } @@ -33,7 +32,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { - if ($this->phpVersion->getVersionId() >= 80000) { + if (!$scope->getPhpVersion()->supportsNativeReflectionAdapterReturnTypes()->no()) { return null; } diff --git a/src/Rules/Functions/FilterVarRule.php b/src/Rules/Functions/FilterVarRule.php index 147dfd0cf86..db6edba91cc 100644 --- a/src/Rules/Functions/FilterVarRule.php +++ b/src/Rules/Functions/FilterVarRule.php @@ -7,7 +7,6 @@ use PhpParser\Node\Name; use PHPStan\Analyser\Scope; use PHPStan\DependencyInjection\RegisteredRule; -use PHPStan\Php\PhpVersion; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; @@ -25,7 +24,6 @@ public function __construct( private ReflectionProvider $reflectionProvider, private FilterFunctionReturnTypeHelper $filterFunctionReturnTypeHelper, private FilterFunctionFlagsHelper $filterFunctionFlagsHelper, - private PhpVersion $phpVersion, ) { } @@ -51,7 +49,7 @@ public function processNode(Node $node, Scope $scope): array } if ( - !$this->phpVersion->hasFilterThrowOnFailureConstant() + $scope->getPhpVersion()->hasFilterThrowOnFailureConstant()->no() || !$this->reflectionProvider->hasConstant(new Name\FullyQualified('FILTER_THROW_ON_FAILURE'), null) ) { return []; diff --git a/tests/PHPStan/Rules/Functions/FilterVarRuleTest.php b/tests/PHPStan/Rules/Functions/FilterVarRuleTest.php index 91edc0ed7f4..3810ab0a33d 100644 --- a/tests/PHPStan/Rules/Functions/FilterVarRuleTest.php +++ b/tests/PHPStan/Rules/Functions/FilterVarRuleTest.php @@ -2,7 +2,6 @@ namespace PHPStan\Rules\Functions; -use PHPStan\Php\PhpVersion; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; use PHPStan\Type\Php\FilterFunctionFlagsHelper; @@ -19,7 +18,6 @@ protected function getRule(): Rule self::createReflectionProvider(), self::getContainer()->getByType(FilterFunctionReturnTypeHelper::class), self::getContainer()->getByType(FilterFunctionFlagsHelper::class), - self::getContainer()->getByType(PhpVersion::class), ); } @@ -45,6 +43,14 @@ public function testFilterFunctions(): void ]); } + #[RequiresPhp('>= 8.5.0')] + public function testRuleNarrowedByPhpVersionId(): void + { + $this->analyse([__DIR__ . '/data/filter_var_null_and_throw_php_version.php'], [ + ['Cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE.', 7], + ]); + } + #[RequiresPhp('>= 8.2.0')] public function testRuleWithGlobalRange(): void { diff --git a/tests/PHPStan/Rules/Functions/data/filter_var_null_and_throw_php_version.php b/tests/PHPStan/Rules/Functions/data/filter_var_null_and_throw_php_version.php new file mode 100644 index 00000000000..d879aea3b5c --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/filter_var_null_and_throw_php_version.php @@ -0,0 +1,11 @@ += 8.5 + +namespace FilterVarNullAndThrowPhpVersion; + +function (string $s): void { + if (PHP_VERSION_ID >= 80500) { + filter_var($s, FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE|FILTER_NULL_ON_FAILURE); + } else { + filter_var($s, FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE|FILTER_NULL_ON_FAILURE); + } +};