Skip to content

Commit f56eb92

Browse files
Introduce reportCastedArrayKey parameter
1 parent 02066c7 commit f56eb92

9 files changed

+140
-7
lines changed

conf/config.level3.neon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ services:
3535
class: PHPStan\Rules\Arrays\InvalidKeyInArrayDimFetchRule
3636
arguments:
3737
reportMaybes: %reportMaybes%
38+
reportCastedArrayKey: %reportCastedArrayKey%
3839
tags:
3940
- phpstan.rules.rule
4041

4142
-
4243
class: PHPStan\Rules\Arrays\InvalidKeyInArrayItemRule
4344
arguments:
4445
reportMaybes: %reportMaybes%
46+
reportCastedArrayKey: %reportCastedArrayKey%
4547
tags:
4648
- phpstan.rules.rule
4749

conf/config.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ parameters:
6363
reportStaticMethodSignatures: false
6464
reportWrongPhpDocTypeInVarTag: false
6565
reportAnyTypeWideningInVarTag: false
66+
reportCastedArrayKey: false
6667
reportPossiblyNonexistentGeneralArrayOffset: false
6768
reportPossiblyNonexistentConstantArrayOffset: false
6869
checkMissingOverrideMethodAttribute: false

conf/parametersSchema.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ parametersSchema:
7575
reportStaticMethodSignatures: bool()
7676
reportWrongPhpDocTypeInVarTag: bool()
7777
reportAnyTypeWideningInVarTag: bool()
78+
reportCastedArrayKey: bool()
7879
reportPossiblyNonexistentGeneralArrayOffset: bool()
7980
reportPossiblyNonexistentConstantArrayOffset: bool()
8081
checkMissingOverrideMethodAttribute: bool()

src/Rules/Arrays/AllowedArrayKeysTypes.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,15 @@
2121
final class AllowedArrayKeysTypes
2222
{
2323

24-
public static function getType(): Type
24+
public static function getType(bool $strict = false): Type
2525
{
26+
if ($strict) {
27+
return new UnionType([
28+
new IntegerType(),
29+
new StringType(),
30+
]);
31+
}
32+
2633
return new UnionType([
2734
new IntegerType(),
2835
new StringType(),

src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ final class InvalidKeyInArrayDimFetchRule implements Rule
2222
public function __construct(
2323
private RuleLevelHelper $ruleLevelHelper,
2424
private bool $reportMaybes,
25+
private bool $reportCastedArrayKey,
2526
)
2627
{
2728
}
@@ -46,14 +47,14 @@ public function processNode(Node $node, Scope $scope): array
4647
$scope,
4748
$node->var,
4849
'',
49-
static fn (Type $varType): bool => $varType->isArray()->no() || AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType)->yes(),
50+
static fn (Type $varType): bool => $varType->isArray()->no() || AllowedArrayKeysTypes::getType($this->reportCastedArrayKey)->isSuperTypeOf($dimensionType)->yes(),
5051
)->getType();
5152

5253
if ($varType instanceof ErrorType || $varType->isArray()->no()) {
5354
return [];
5455
}
5556

56-
$isSuperType = AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType);
57+
$isSuperType = AllowedArrayKeysTypes::getType($this->reportCastedArrayKey)->isSuperTypeOf($dimensionType);
5758
if ($isSuperType->yes() || ($isSuperType->maybe() && !$this->reportMaybes)) {
5859
return [];
5960
}

src/Rules/Arrays/InvalidKeyInArrayItemRule.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
final class InvalidKeyInArrayItemRule implements Rule
1717
{
1818

19-
public function __construct(private bool $reportMaybes)
19+
public function __construct(private bool $reportMaybes, private bool $reportCastedArrayKey)
2020
{
2121
}
2222

@@ -32,7 +32,7 @@ public function processNode(Node $node, Scope $scope): array
3232
}
3333

3434
$dimensionType = $scope->getType($node->key);
35-
$isSuperType = AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType);
35+
$isSuperType = AllowedArrayKeysTypes::getType($this->reportCastedArrayKey)->isSuperTypeOf($dimensionType);
3636
if ($isSuperType->no()) {
3737
return [
3838
RuleErrorBuilder::message(

tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
*/
1313
class InvalidKeyInArrayDimFetchRuleTest extends RuleTestCase
1414
{
15+
private bool $reportCastedArrayKey = false;
1516

1617
protected function getRule(): Rule
1718
{
1819
$ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, false, true);
19-
return new InvalidKeyInArrayDimFetchRule($ruleLevelHelper, true);
20+
return new InvalidKeyInArrayDimFetchRule($ruleLevelHelper, true, $this->reportCastedArrayKey);
2021
}
2122

2223
public function testInvalidKey(): void
@@ -61,6 +62,69 @@ public function testInvalidKey(): void
6162
]);
6263
}
6364

65+
public function testInvalidKeyReportingCastedArrayKey(): void
66+
{
67+
$this->reportCastedArrayKey = true;
68+
$this->analyse([__DIR__ . '/data/invalid-key-array-dim-fetch.php'], [
69+
[
70+
'Invalid array key type null.',
71+
6,
72+
],
73+
[
74+
'Invalid array key type DateTimeImmutable.',
75+
7,
76+
],
77+
[
78+
'Invalid array key type array.',
79+
8,
80+
],
81+
[
82+
'Invalid array key type float.',
83+
10,
84+
],
85+
[
86+
'Invalid array key type true.',
87+
12,
88+
],
89+
[
90+
'Invalid array key type false.',
91+
13,
92+
],
93+
[
94+
'Possibly invalid array key type string|null.',
95+
17,
96+
],
97+
[
98+
'Possibly invalid array key type stdClass|string.',
99+
24,
100+
],
101+
[
102+
'Invalid array key type DateTimeImmutable.',
103+
31,
104+
],
105+
[
106+
'Invalid array key type DateTimeImmutable.',
107+
45,
108+
],
109+
[
110+
'Invalid array key type DateTimeImmutable.',
111+
46,
112+
],
113+
[
114+
'Invalid array key type DateTimeImmutable.',
115+
47,
116+
],
117+
[
118+
'Invalid array key type stdClass.',
119+
47,
120+
],
121+
[
122+
'Invalid array key type DateTimeImmutable.',
123+
48,
124+
],
125+
]);
126+
}
127+
64128
public function testBug6315(): void
65129
{
66130
if (PHP_VERSION_ID < 80100) {
@@ -95,4 +159,20 @@ public function testBug6315(): void
95159
]);
96160
}
97161

162+
public function testUnsetFalseKey(): void
163+
{
164+
$this->reportCastedArrayKey = true;
165+
166+
$this->analyse([__DIR__ . '/data/unset-false-key.php'], [
167+
[
168+
'Invalid array key type false.',
169+
6,
170+
],
171+
[
172+
'Invalid array key type false.',
173+
13,
174+
],
175+
]);
176+
}
177+
98178
}

tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
class InvalidKeyInArrayItemRuleTest extends RuleTestCase
1313
{
1414

15+
private bool $reportCastedArrayKey = false;
16+
1517
protected function getRule(): Rule
1618
{
17-
return new InvalidKeyInArrayItemRule(true);
19+
return new InvalidKeyInArrayItemRule(true, $this->reportCastedArrayKey);
1820
}
1921

2022
public function testInvalidKey(): void
@@ -35,6 +37,29 @@ public function testInvalidKey(): void
3537
]);
3638
}
3739

40+
public function testInvalidKeyReportingCastedArrayKey(): void
41+
{
42+
$this->reportCastedArrayKey = true;
43+
$this->analyse([__DIR__ . '/data/invalid-key-array-item.php'], [
44+
[
45+
'Invalid array key type null.',
46+
12,
47+
],
48+
[
49+
'Invalid array key type DateTimeImmutable.',
50+
13,
51+
],
52+
[
53+
'Invalid array key type array.',
54+
14,
55+
],
56+
[
57+
'Possibly invalid array key type stdClass|string.',
58+
15,
59+
],
60+
]);
61+
}
62+
3863
public function testInvalidKeyInList(): void
3964
{
4065
$this->analyse([__DIR__ . '/data/invalid-key-list.php'], [
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace UnsetFalseKey;
4+
5+
/** @var array<int, int> $data */
6+
unset($data[false]);
7+
8+
function test_remove_element(): void {
9+
$modified = [1, 4, 6, 8];
10+
11+
// this would happen in the SUT
12+
unset($modified[array_search(4, $modified, true)]);
13+
unset($modified[array_search(5, $modified, true)]); // bug is here - will unset key `0` by accident
14+
15+
assert([1, 6, 8] === $modified); // actually is [6, 8]
16+
}

0 commit comments

Comments
 (0)