Skip to content

Commit 6b9f23d

Browse files
Report float and null offset based on PHP version
1 parent a5aa2ce commit 6b9f23d

9 files changed

+121
-27
lines changed

src/Php/PhpVersion.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,4 +414,14 @@ public function hasPDOSubclasses(): bool
414414
return $this->versionId >= 80400;
415415
}
416416

417+
public function deprecatesImplicitlyFloatConversionToInt(): bool
418+
{
419+
return $this->versionId >= 80100;
420+
}
421+
422+
public function deprecatesNullArrayOffset(): bool
423+
{
424+
return $this->versionId >= 80500;
425+
}
426+
417427
}

src/Rules/Arrays/AllowedArrayKeysTypes.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Rules\Arrays;
44

5+
use PHPStan\Php\PhpVersion;
56
use PHPStan\Type\ArrayType;
67
use PHPStan\Type\BooleanType;
78
use PHPStan\Type\Constant\ConstantBooleanType;
@@ -21,15 +22,22 @@
2122
final class AllowedArrayKeysTypes
2223
{
2324

24-
public static function getType(): Type
25+
public static function getType(?PhpVersion $phpVersion = null): Type
2526
{
26-
return new UnionType([
27+
$types = [
2728
new IntegerType(),
2829
new StringType(),
29-
new FloatType(),
3030
new BooleanType(),
31-
new NullType(),
32-
]);
31+
];
32+
33+
if ($phpVersion === null || !$phpVersion->deprecatesImplicitlyFloatConversionToInt()) {
34+
$types[] = new FloatType();
35+
}
36+
if ($phpVersion === null || !$phpVersion->deprecatesNullArrayOffset()) {
37+
$types[] = new NullType();
38+
}
39+
40+
return new UnionType($types);
3341
}
3442

3543
public static function narrowOffsetKeyType(Type $varType, Type $keyType): ?Type

src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,13 @@ public function processNode(Node $node, Scope $scope): array
128128
}
129129

130130
foreach ($keyValues as $value) {
131+
// Prevent php warning by manually casting array keys
132+
if (\is_bool($value) || \is_float($value)) {
133+
$value = (int) $value;
134+
} elseif (null === $value) {
135+
$value = (string) $value;
136+
}
137+
131138
$printedValue = $key !== null
132139
? $this->exprPrinter->printExpr($key)
133140
: $value;

src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\AutowiredParameter;
88
use PHPStan\DependencyInjection\RegisteredRule;
9+
use PHPStan\Php\PhpVersion;
910
use PHPStan\Rules\Rule;
1011
use PHPStan\Rules\RuleErrorBuilder;
1112
use PHPStan\Rules\RuleLevelHelper;
@@ -23,6 +24,7 @@ final class InvalidKeyInArrayDimFetchRule implements Rule
2324

2425
public function __construct(
2526
private RuleLevelHelper $ruleLevelHelper,
27+
private PhpVersion $phpVersion,
2628
#[AutowiredParameter]
2729
private bool $reportMaybes,
2830
)
@@ -56,17 +58,18 @@ public function processNode(Node $node, Scope $scope): array
5658
return [];
5759
}
5860

61+
$phpVersion = $this->phpVersion;
5962
$dimensionType = $this->ruleLevelHelper->findTypeToCheck(
6063
$scope,
6164
$node->dim,
6265
'',
63-
static fn (Type $dimType): bool => AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimType)->yes(),
66+
static fn (Type $dimType): bool => AllowedArrayKeysTypes::getType($phpVersion)->isSuperTypeOf($dimType)->yes(),
6467
)->getType();
6568
if ($dimensionType instanceof ErrorType) {
6669
return [];
6770
}
6871

69-
$isSuperType = AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType);
72+
$isSuperType = AllowedArrayKeysTypes::getType($phpVersion)->isSuperTypeOf($dimensionType);
7073
if ($isSuperType->yes() || ($isSuperType->maybe() && !$this->reportMaybes)) {
7174
return [];
7275
}

src/Rules/Arrays/InvalidKeyInArrayItemRule.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\RegisteredRule;
8+
use PHPStan\Php\PhpVersion;
89
use PHPStan\Rules\Rule;
910
use PHPStan\Rules\RuleErrorBuilder;
1011
use PHPStan\Rules\RuleLevelHelper;
@@ -22,6 +23,7 @@ final class InvalidKeyInArrayItemRule implements Rule
2223

2324
public function __construct(
2425
private RuleLevelHelper $ruleLevelHelper,
26+
private PhpVersion $phpVersion,
2527
)
2628
{
2729
}
@@ -37,17 +39,18 @@ public function processNode(Node $node, Scope $scope): array
3739
return [];
3840
}
3941

42+
$phpVersion = $this->phpVersion;
4043
$dimensionType = $this->ruleLevelHelper->findTypeToCheck(
4144
$scope,
4245
$node->key,
4346
'',
44-
static fn (Type $dimType): bool => AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimType)->yes(),
47+
static fn (Type $dimType): bool => AllowedArrayKeysTypes::getType($phpVersion)->isSuperTypeOf($dimType)->yes(),
4548
)->getType();
4649
if ($dimensionType instanceof ErrorType) {
4750
return [];
4851
}
4952

50-
$isSuperType = AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType);
53+
$isSuperType = AllowedArrayKeysTypes::getType($phpVersion)->isSuperTypeOf($dimensionType);
5154
if ($isSuperType->yes()) {
5255
return [];
5356
}

tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Rules\Arrays;
44

5+
use PHPStan\Php\PhpVersion;
56
use PHPStan\Rules\Rule;
67
use PHPStan\Rules\RuleLevelHelper;
78
use PHPStan\Testing\RuleTestCase;
@@ -16,12 +17,16 @@ class InvalidKeyInArrayDimFetchRuleTest extends RuleTestCase
1617
protected function getRule(): Rule
1718
{
1819
$ruleLevelHelper = new RuleLevelHelper(self::createReflectionProvider(), true, false, true, true, true, false, true);
19-
return new InvalidKeyInArrayDimFetchRule($ruleLevelHelper, true);
20+
return new InvalidKeyInArrayDimFetchRule(
21+
$ruleLevelHelper,
22+
self::getContainer()->getByType(PhpVersion::class),
23+
true,
24+
);
2025
}
2126

2227
public function testInvalidKey(): void
2328
{
24-
$this->analyse([__DIR__ . '/data/invalid-key-array-dim-fetch.php'], [
29+
$errors = [
2530
[
2631
'Invalid array key type DateTimeImmutable.',
2732
7,
@@ -62,7 +67,22 @@ public function testInvalidKey(): void
6267
'Invalid array key type DateTimeImmutable.',
6368
48,
6469
],
65-
]);
70+
];
71+
72+
if (PHP_VERSION_ID >= 80100) {
73+
$errors[] = [
74+
'Invalid array key type float.',
75+
51,
76+
];
77+
}
78+
if (PHP_VERSION_ID >= 80500) {
79+
$errors[] = [
80+
'Invalid array key type null.',
81+
52,
82+
];
83+
}
84+
85+
$this->analyse([__DIR__ . '/data/invalid-key-array-dim-fetch.php'], $errors);
6686
}
6787

6888
#[RequiresPhp('>= 8.1')]

tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Rules\Arrays;
44

5+
use PHPStan\Php\PhpVersion;
56
use PHPStan\Rules\Rule;
67
use PHPStan\Rules\RuleLevelHelper;
78
use PHPStan\Testing\RuleTestCase;
@@ -21,50 +22,83 @@ protected function getRule(): Rule
2122
{
2223
$ruleLevelHelper = new RuleLevelHelper(self::createReflectionProvider(), true, false, true, $this->checkExplicitMixed, $this->checkImplicitMixed, false, true);
2324

24-
return new InvalidKeyInArrayItemRule($ruleLevelHelper);
25+
return new InvalidKeyInArrayItemRule(
26+
$ruleLevelHelper,
27+
self::getContainer()->getByType(PhpVersion::class),
28+
);
2529
}
2630

2731
public function testInvalidKey(): void
2832
{
29-
$this->analyse([__DIR__ . '/data/invalid-key-array-item.php'], [
33+
$errors = [
3034
[
3135
'Invalid array key type DateTimeImmutable.',
32-
13,
36+
12,
3337
],
3438
[
3539
'Invalid array key type array.',
36-
14,
40+
13,
3741
],
3842
[
3943
'Possibly invalid array key type stdClass|string.',
40-
15,
44+
14,
4145
],
42-
]);
46+
];
47+
48+
if (PHP_VERSION_ID >= 80100) {
49+
$errors[] = [
50+
'Invalid array key type float.',
51+
26,
52+
];
53+
}
54+
if (PHP_VERSION_ID >= 80500) {
55+
$errors[] = [
56+
'Invalid array key type null.',
57+
27,
58+
];
59+
}
60+
61+
$this->analyse([__DIR__ . '/data/invalid-key-array-item.php'], $errors);
4362
}
4463

4564
public function testInvalidMixedKey(): void
4665
{
4766
$this->checkExplicitMixed = true;
4867
$this->checkImplicitMixed = true;
4968

50-
$this->analyse([__DIR__ . '/data/invalid-key-array-item.php'], [
69+
$errors = [
5170
[
5271
'Invalid array key type DateTimeImmutable.',
53-
13,
72+
12,
5473
],
5574
[
5675
'Invalid array key type array.',
57-
14,
76+
13,
5877
],
5978
[
6079
'Possibly invalid array key type stdClass|string.',
61-
15,
80+
14,
6281
],
6382
[
6483
'Possibly invalid array key type mixed.',
65-
22,
84+
21,
6685
],
67-
]);
86+
];
87+
88+
if (PHP_VERSION_ID >= 80100) {
89+
$errors[] = [
90+
'Invalid array key type float.',
91+
26,
92+
];
93+
}
94+
if (PHP_VERSION_ID >= 80500) {
95+
$errors[] = [
96+
'Invalid array key type null.',
97+
27,
98+
];
99+
}
100+
101+
$this->analyse([__DIR__ . '/data/invalid-key-array-item.php'], $errors);
68102
}
69103

70104
public function testInvalidKeyInList(): void

tests/PHPStan/Rules/Arrays/data/invalid-key-array-dim-fetch.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
namespace InvalidKeyArrayDimFetch;
44

55
$a = [];
6-
$foo = $a[null];
6+
77
$foo = $a[new \DateTimeImmutable()];
88
$a[[]] = $foo;
99
$a[1];
10-
$a[1.0];
10+
1111
$a['1'];
1212
$a[true];
1313
$a[false];
@@ -46,3 +46,7 @@
4646
$array[5][new \DateTimeImmutable()];
4747
$array[new \stdClass()][new \DateTimeImmutable()];
4848
$array[new \DateTimeImmutable()][] = 5;
49+
50+
// Php version dependant
51+
$a[1.0];
52+
$foo = $a[null];

tests/PHPStan/Rules/Arrays/data/invalid-key-array-item.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
'foo',
1010
1 => 'aaa',
1111
'1' => 'aaa',
12-
null => 'aaa',
1312
new \DateTimeImmutable() => 'aaa',
1413
[] => 'bbb',
1514
$stringOrObject => 'aaa',
@@ -21,3 +20,9 @@
2120
$b = [
2221
$mixed => 'foo',
2322
];
23+
24+
// PHP version dependent
25+
$c = [
26+
1.0 => 'aaa',
27+
null => 'aaa',
28+
];

0 commit comments

Comments
 (0)