Skip to content

Commit c230eb0

Browse files
staabmphpstan-botclaude
authored
Resolve PHP version checks in src/Type/Php extensions from Scope::getPhpVersion() (#6551)
Co-authored-by: phpstan-bot <ondrej+phpstanbot@mirtes.cz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent b940151 commit c230eb0

61 files changed

Lines changed: 672 additions & 360 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎src/Php/PhpVersions.php‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,84 @@ public function supportsDeprecatedTraits(): TrinaryLogic
188188
return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result;
189189
}
190190

191+
public function arrayFunctionsReturnNullWithNonArray(): TrinaryLogic
192+
{
193+
return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result;
194+
}
195+
196+
public function hasDateTimeExceptions(): TrinaryLogic
197+
{
198+
return IntegerRangeType::fromInterval(80300, null)->isSuperTypeOf($this->phpVersions)->result;
199+
}
200+
201+
public function hasFilterThrowOnFailureConstant(): TrinaryLogic
202+
{
203+
return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result;
204+
}
205+
206+
public function hasPDOSubclasses(): TrinaryLogic
207+
{
208+
return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result;
209+
}
210+
211+
public function hasStricterRoundFunctions(): TrinaryLogic
212+
{
213+
return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result;
214+
}
215+
216+
public function highlightStringDoesNotReturnFalse(): TrinaryLogic
217+
{
218+
return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result;
219+
}
220+
221+
public function isEmptyStringValidAliasForNoneInMbSubstituteCharacter(): TrinaryLogic
222+
{
223+
return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result;
224+
}
225+
226+
public function isNullValidArgInMbSubstituteCharacter(): TrinaryLogic
227+
{
228+
return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result;
229+
}
230+
231+
public function isNumericStringValidArgInMbSubstituteCharacter(): TrinaryLogic
232+
{
233+
return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result;
234+
}
235+
236+
public function strSplitReturnsEmptyArray(): TrinaryLogic
237+
{
238+
return IntegerRangeType::fromInterval(80200, null)->isSuperTypeOf($this->phpVersions)->result;
239+
}
240+
241+
public function substrReturnFalseInsteadOfEmptyString(): TrinaryLogic
242+
{
243+
return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result;
244+
}
245+
246+
public function supportsAllUnicodeScalarCodePointsInMbSubstituteCharacter(): TrinaryLogic
247+
{
248+
return IntegerRangeType::fromInterval(70200, null)->isSuperTypeOf($this->phpVersions)->result;
249+
}
250+
191251
public function supportsHhPrintfSpecifier(): TrinaryLogic
192252
{
193253
return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result;
194254
}
195255

256+
public function supportsPassNoneEncodings(): TrinaryLogic
257+
{
258+
return IntegerRangeType::fromInterval(null, 70299)->isSuperTypeOf($this->phpVersions)->result;
259+
}
260+
261+
public function throwsOnInvalidMbStringEncoding(): TrinaryLogic
262+
{
263+
return IntegerRangeType::fromInterval(80000, null)->isSuperTypeOf($this->phpVersions)->result;
264+
}
265+
266+
public function throwsValueErrorForInvalidRoundingMode(): TrinaryLogic
267+
{
268+
return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result;
269+
}
270+
196271
}

‎src/Type/Php/ArrayChunkFunctionReturnTypeExtension.php‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use PhpParser\Node\Expr\FuncCall;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\AutowiredService;
8-
use PHPStan\Php\PhpVersion;
98
use PHPStan\Reflection\FunctionReflection;
109
use PHPStan\Type\Constant\ConstantBooleanType;
1110
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
@@ -19,10 +18,6 @@
1918
final class ArrayChunkFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
2019
{
2120

22-
public function __construct(private PhpVersion $phpVersion)
23-
{
24-
}
25-
2621
public function isFunctionSupported(FunctionReflection $functionReflection): bool
2722
{
2823
return $functionReflection->getName() === 'array_chunk';
@@ -37,13 +32,21 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
3732

3833
$arrayType = $scope->getType($args[0]->value);
3934
if ($arrayType->isArray()->no()) {
40-
return $this->phpVersion->arrayFunctionsReturnNullWithNonArray() ? new NullType() : new NeverType();
35+
if ($scope->getPhpVersion()->arrayFunctionsReturnNullWithNonArray()->no()) {
36+
return new NeverType();
37+
}
38+
39+
return new NullType();
4140
}
4241

4342
$lengthType = $scope->getType($args[1]->value);
4443
$negativeOrZero = IntegerRangeType::fromInterval(null, 0);
4544
if ($negativeOrZero->isSuperTypeOf($lengthType)->yes()) {
46-
return $this->phpVersion->throwsValueErrorForInternalFunctions() ? new NeverType() : new NullType();
45+
if ($scope->getPhpVersion()->throwsValueErrorForInternalFunctions()->yes()) {
46+
return new NeverType();
47+
}
48+
49+
return new NullType();
4750
}
4851

4952
$preserveKeysType = isset($args[2]) ? $scope->getType($args[2]->value) : new ConstantBooleanType(false);

‎src/Type/Php/ArrayChunkFunctionThrowTypeExtension.php‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use PhpParser\Node\Expr\FuncCall;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\AutowiredService;
8-
use PHPStan\Php\PhpVersion;
98
use PHPStan\Reflection\FunctionReflection;
109
use PHPStan\Type\DynamicFunctionThrowTypeExtension;
1110
use PHPStan\Type\IntegerRangeType;
@@ -20,18 +19,14 @@
2019
final class ArrayChunkFunctionThrowTypeExtension implements DynamicFunctionThrowTypeExtension
2120
{
2221

23-
public function __construct(private PhpVersion $phpVersion)
24-
{
25-
}
26-
2722
public function isFunctionSupported(FunctionReflection $functionReflection): bool
2823
{
2924
return $functionReflection->getName() === 'array_chunk';
3025
}
3126

3227
public function getThrowTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $funcCall, Scope $scope): ?Type
3328
{
34-
if (!$this->phpVersion->throwsValueErrorForInternalFunctions()) {
29+
if ($scope->getPhpVersion()->throwsValueErrorForInternalFunctions()->no()) {
3530
return new VoidType();
3631
}
3732

‎src/Type/Php/ArrayColumnHelper.php‎

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use PHPStan\Analyser\Scope;
66
use PHPStan\DependencyInjection\AutowiredService;
7-
use PHPStan\Php\PhpVersion;
87
use PHPStan\TrinaryLogic;
98
use PHPStan\Type\Accessory\AccessoryArrayListType;
109
use PHPStan\Type\Accessory\NonEmptyArrayType;
@@ -21,12 +20,6 @@
2120
final class ArrayColumnHelper
2221
{
2322

24-
public function __construct(
25-
private PhpVersion $phpVersion,
26-
)
27-
{
28-
}
29-
3023
/**
3124
* @return array{Type, TrinaryLogic}
3225
*/
@@ -74,7 +67,7 @@ public function handleAnyArray(Type $arrayType, Type $columnType, Type $indexTyp
7467
}
7568

7669
$returnKeyType = $this->getReturnIndexType($arrayType, $indexType, $scope);
77-
$returnType = new ArrayType($this->castToArrayKeyType($returnKeyType), $returnValueType);
70+
$returnType = new ArrayType($this->castToArrayKeyType($returnKeyType, $scope), $returnValueType);
7871

7972
if ($iterableAtLeastOnce->yes()) {
8073
$returnType = TypeCombinator::intersect($returnType, new NonEmptyArrayType());
@@ -113,7 +106,7 @@ public function handleConstantArray(ConstantArrayType $arrayType, Type $columnTy
113106
}
114107

115108
if ($keyType !== null) {
116-
$keyType = $this->castToArrayKeyType($keyType);
109+
$keyType = $this->castToArrayKeyType($keyType, $scope);
117110
}
118111
$builder->setOffsetValueType($keyType, $valueType, $arrayType->isOptionalKey($i));
119112
}
@@ -131,9 +124,9 @@ public function handleConstantArray(ConstantArrayType $arrayType, Type $columnTy
131124
if ($unsealedKeyFromIndex instanceof NeverType) {
132125
$unsealedKey = $unsealedTypes[0];
133126
} elseif ($unsealedKeyCertainty->yes()) {
134-
$unsealedKey = $this->castToArrayKeyType($unsealedKeyFromIndex);
127+
$unsealedKey = $this->castToArrayKeyType($unsealedKeyFromIndex, $scope);
135128
} else {
136-
$unsealedKey = $this->castToArrayKeyType(TypeCombinator::union($unsealedKeyFromIndex, new IntegerType()));
129+
$unsealedKey = $this->castToArrayKeyType(TypeCombinator::union($unsealedKeyFromIndex, new IntegerType()), $scope);
137130
}
138131
} else {
139132
// `null` indexType keeps integer-keyed list semantics —
@@ -221,18 +214,23 @@ private function getOffsetOrProperty(Type $type, Type $offsetOrProperty, Scope $
221214
return [TypeCombinator::union(...$returnTypes), $certainty];
222215
}
223216

224-
private function castToArrayKeyType(Type $type): Type
217+
private function castToArrayKeyType(Type $type, Scope $scope): Type
225218
{
219+
$throwsTypeError = $scope->getPhpVersion()->throwsTypeErrorForInternalFunctions();
226220
$isArray = $type->isArray();
227221
if ($isArray->yes()) {
228-
return $this->phpVersion->throwsTypeErrorForInternalFunctions() ? new NeverType() : new IntegerType();
222+
if ($throwsTypeError->yes()) {
223+
return new NeverType();
224+
}
225+
226+
return new IntegerType();
229227
}
230228
if ($isArray->no()) {
231229
return $type->toArrayKey();
232230
}
233231
$withoutArrayType = TypeCombinator::remove($type, new ArrayType(new MixedType(), new MixedType()));
234232
$keyType = $withoutArrayType->toArrayKey();
235-
if ($this->phpVersion->throwsTypeErrorForInternalFunctions()) {
233+
if ($throwsTypeError->yes()) {
236234
return $keyType;
237235
}
238236
return TypeCombinator::union($keyType, new IntegerType());

‎src/Type/Php/ArrayCombineFunctionReturnTypeExtension.php‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use PhpParser\Node\Expr\FuncCall;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\AutowiredService;
8-
use PHPStan\Php\PhpVersion;
98
use PHPStan\Reflection\FunctionReflection;
109
use PHPStan\Type\Constant\ConstantBooleanType;
1110
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
@@ -20,7 +19,6 @@ final class ArrayCombineFunctionReturnTypeExtension implements DynamicFunctionRe
2019

2120
public function __construct(
2221
private ArrayCombineHelper $arrayCombineHelper,
23-
private PhpVersion $phpVersion,
2422
)
2523
{
2624
}
@@ -45,15 +43,16 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
4543
return $returnType;
4644
}
4745

46+
$throwsValueError = $scope->getPhpVersion()->throwsValueErrorForInternalFunctions();
4847
if ($hasValueError->yes()) {
49-
if ($this->phpVersion->throwsValueErrorForInternalFunctions()) {
48+
if ($throwsValueError->yes()) {
5049
return new NeverType();
5150
}
5251

5352
return new ConstantBooleanType(false);
5453
}
5554

56-
if ($this->phpVersion->throwsValueErrorForInternalFunctions()) {
55+
if ($throwsValueError->yes()) {
5756
return $returnType;
5857
}
5958

‎src/Type/Php/ArrayFillFunctionReturnTypeExtension.php‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use PhpParser\Node\Expr\FuncCall;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\AutowiredService;
8-
use PHPStan\Php\PhpVersion;
98
use PHPStan\Reflection\FunctionReflection;
109
use PHPStan\Type\Accessory\AccessoryArrayListType;
1110
use PHPStan\Type\Accessory\NonEmptyArrayType;
@@ -28,10 +27,6 @@ final class ArrayFillFunctionReturnTypeExtension implements DynamicFunctionRetur
2827

2928
private const MAX_SIZE_USE_CONSTANT_ARRAY = 100;
3029

31-
public function __construct(private PhpVersion $phpVersion)
32-
{
33-
}
34-
3530
public function isFunctionSupported(FunctionReflection $functionReflection): bool
3631
{
3732
return $functionReflection->getName() === 'array_fill';
@@ -46,12 +41,14 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
4641

4742
$numberType = $scope->getType($args[1]->value);
4843
$isValidNumberType = IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($numberType);
44+
$throwsValueError = $scope->getPhpVersion()->throwsValueErrorForInternalFunctions();
4945

5046
// check against negative-int, which is not allowed
5147
if ($isValidNumberType->no()) {
52-
if ($this->phpVersion->throwsValueErrorForInternalFunctions()) {
48+
if ($throwsValueError->yes()) {
5349
return new NeverType();
5450
}
51+
5552
return new ConstantBooleanType(false);
5653
}
5754

@@ -96,7 +93,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
9693
$resultType = TypeCombinator::intersect($resultType, new NonEmptyArrayType());
9794
}
9895

99-
if (!$isValidNumberType->yes() && !$this->phpVersion->throwsValueErrorForInternalFunctions()) {
96+
if (!$isValidNumberType->yes() && !$throwsValueError->yes()) {
10097
$resultType = TypeCombinator::union($resultType, new ConstantBooleanType(false));
10198
}
10299

‎src/Type/Php/ArrayFillKeysFunctionReturnTypeExtension.php‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use PhpParser\Node\Expr\FuncCall;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\AutowiredService;
8-
use PHPStan\Php\PhpVersion;
98
use PHPStan\Reflection\FunctionReflection;
109
use PHPStan\Type\Accessory\NonEmptyArrayType;
1110
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
@@ -19,10 +18,6 @@
1918
final class ArrayFillKeysFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
2019
{
2120

22-
public function __construct(private PhpVersion $phpVersion)
23-
{
24-
}
25-
2621
public function isFunctionSupported(FunctionReflection $functionReflection): bool
2722
{
2823
return $functionReflection->getName() === 'array_fill_keys';
@@ -37,7 +32,11 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
3732

3833
$keysType = $scope->getType($args[0]->value);
3934
if ($keysType->isArray()->no()) {
40-
return $this->phpVersion->arrayFunctionsReturnNullWithNonArray() ? new NullType() : new NeverType();
35+
if ($scope->getPhpVersion()->arrayFunctionsReturnNullWithNonArray()->no()) {
36+
return new NeverType();
37+
}
38+
39+
return new NullType();
4140
}
4241

4342
$filled = $keysType->fillKeysArray($scope->getType($args[1]->value));

‎src/Type/Php/ArrayFilterFunctionReturnTypeHelper.php‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use PHPStan\Analyser\MutatingScope;
1717
use PHPStan\Analyser\Scope;
1818
use PHPStan\DependencyInjection\AutowiredService;
19-
use PHPStan\Php\PhpVersion;
2019
use PHPStan\Reflection\ReflectionProvider;
2120
use PHPStan\ShouldNotHappenException;
2221
use PHPStan\TrinaryLogic;
@@ -49,7 +48,6 @@ final class ArrayFilterFunctionReturnTypeHelper
4948

5049
public function __construct(
5150
private ReflectionProvider $reflectionProvider,
52-
private PhpVersion $phpVersion,
5351
)
5452
{
5553
}
@@ -70,7 +68,7 @@ public function getType(Scope $scope, ?Expr $arrayArg, ?Expr $callbackArg, ?Expr
7068
}
7169

7270
if ($arrayArgType instanceof MixedType) {
73-
if ($this->phpVersion->throwsValueErrorForInternalFunctions()) {
71+
if ($scope->getPhpVersion()->throwsValueErrorForInternalFunctions()->yes()) {
7472
return new ArrayType(new MixedType(), new MixedType());
7573
}
7674

0 commit comments

Comments
 (0)