Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8b4783e
Resolve PHP version checks in return-type, throw-type and type-specif…
staabm Sep 22, 2026
c006f1f
Resolve PHP version checks in round() and str_split() throw type exte…
phpstan-bot Sep 22, 2026
56816b0
Discover scope-aware extensions in the build rule instead of listing …
phpstan-bot Sep 22, 2026
037ddad
List the scope-aware extension interfaces in the build rule explicitly
phpstan-bot Sep 22, 2026
add8984
Branch on the version check with a plain if() instead of PhpVersions:…
phpstan-bot Sep 22, 2026
5ee6d34
Move the printf format parser change to its own pull request
phpstan-bot Sep 22, 2026
6dd96dd
Keep PrintfFormatParser reading the PHP version from the call site
phpstan-bot Sep 22, 2026
f6b2836
Resolve PHP version checks in the get_class(), array_chunk() and unse…
phpstan-bot Sep 22, 2026
b74fdd4
Fix the expected lines in NoInjectedPhpVersionInScopeAwareExtensionRu…
phpstan-bot Sep 22, 2026
892a037
fix build
staabm Sep 22, 2026
365835c
Keep the PHP version check inside RegexArrayShapeMatcher::containsUnm…
phpstan-bot Sep 22, 2026
e9d2c95
Do not infer null from version_compare() with a valid constant operator
phpstan-bot Sep 22, 2026
80619fa
Detect DateTime::modify() failure from its return value, not from the…
phpstan-bot Sep 22, 2026
6d24a23
Strip modifiers the host PCRE may not know before compiling the pattern
phpstan-bot Sep 22, 2026
b2db476
Test return-type extensions against a configured PHP version range
phpstan-bot Sep 22, 2026
a69e753
fix baseline
staabm Sep 22, 2026
3565169
Update scope-php-version-range-return-type-extensions.php
staabm Sep 22, 2026
ddc2b6f
Move the src/Type/Php extension changes to their own pull request
phpstan-bot Sep 23, 2026
0d8d599
Leave the Regex* classes out of this pull request
phpstan-bot Sep 23, 2026
c778a5d
Test that FilterVarRule follows PHP_VERSION_ID narrowing
phpstan-bot Sep 23, 2026
8cd6733
restore phpversions
staabm Sep 23, 2026
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
75 changes: 75 additions & 0 deletions src/Php/PhpVersions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,10 +26,6 @@
final class AdapterReflectionEnumDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
{

public function __construct(private PhpVersion $phpVersion)
{
}

public function getClass(): string
{
return ReflectionEnum::class;
Expand All @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
}

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

Expand Down
4 changes: 1 addition & 3 deletions src/Rules/Functions/FilterVarRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,7 +24,6 @@ public function __construct(
private ReflectionProvider $reflectionProvider,
private FilterFunctionReturnTypeHelper $filterFunctionReturnTypeHelper,
private FilterFunctionFlagsHelper $filterFunctionFlagsHelper,
private PhpVersion $phpVersion,
)
{
}
Expand All @@ -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 [];
Expand Down
10 changes: 8 additions & 2 deletions tests/PHPStan/Rules/Functions/FilterVarRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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),
);
}

Expand All @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php // lint >= 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);
}
};
Loading