Skip to content

Commit b4dbfa4

Browse files
ArrayType - string offset might exist as integer offset
1 parent d0b1cf6 commit b4dbfa4

File tree

5 files changed

+103
-18
lines changed

5 files changed

+103
-18
lines changed

src/Type/ArrayType.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,10 @@ public function isOffsetAccessible(): TrinaryLogic
397397
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
398398
{
399399
$offsetType = $offsetType->toArrayKey();
400-
if ($this->getKeyType()->isSuperTypeOf($offsetType)->no()) {
400+
401+
if ($this->getKeyType()->isSuperTypeOf($offsetType)->no()
402+
&& ($offsetType->isString()->no() || !$offsetType->isConstantScalarValue()->no())
403+
) {
401404
return TrinaryLogic::createNo();
402405
}
403406

@@ -407,7 +410,9 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic
407410
public function getOffsetValueType(Type $offsetType): Type
408411
{
409412
$offsetType = $offsetType->toArrayKey();
410-
if ($this->getKeyType()->isSuperTypeOf($offsetType)->no()) {
413+
if ($this->getKeyType()->isSuperTypeOf($offsetType)->no()
414+
&& ($offsetType->isString()->no() || !$offsetType->isConstantScalarValue()->no())
415+
) {
411416
return new ErrorType();
412417
}
413418

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ public function dataFileAsserts(): iterable
168168
yield from $this->gatherAssertTypes(__DIR__ . '/data/non-empty-array-key-type.php');
169169
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3133.php');
170170
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Variables/data/bug-10577.php');
171+
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Variables/data/bug-10610.php');
171172
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-2550.php');
172173
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-2899.php');
173174
yield from $this->gatherAssertTypes(__DIR__ . '/data/preg_split.php');

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,10 @@ public function testRule(): void
107107
'Offset \'b\' does not exist on array{a: \'blabla\'}.',
108108
228,
109109
],
110-
[
111-
'Offset string does not exist on array<int, mixed>.',
112-
240,
113-
],
114110
[
115111
'Cannot access offset \'a\' on Closure(): void.',
116112
253,
117113
],
118-
[
119-
'Offset string does not exist on array<int, string>.',
120-
308,
121-
],
122114
[
123115
'Offset null does not exist on array<int, string>.',
124116
310,
@@ -252,18 +244,10 @@ public function testRuleBleedingEdge(): void
252244
'Offset \'b\' does not exist on array{a: \'blabla\'}.',
253245
228,
254246
],
255-
[
256-
'Offset string does not exist on array<int, mixed>.',
257-
240,
258-
],
259247
[
260248
'Cannot access offset \'a\' on Closure(): void.',
261249
253,
262250
],
263-
[
264-
'Offset string does not exist on array<int, string>.',
265-
308,
266-
],
267251
[
268252
'Offset null does not exist on array<int, string>.',
269253
310,

tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,4 +376,12 @@ public function testBug10577(): void
376376
$this->analyse([__DIR__ . '/data/bug-10577.php'], []);
377377
}
378378

379+
public function testBug10610(): void
380+
{
381+
$this->treatPhpDocTypesAsCertain = true;
382+
$this->strictUnnecessaryNullsafePropertyFetch = true;
383+
384+
$this->analyse([__DIR__ . '/data/bug-10610.php'], []);
385+
}
386+
379387
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
namespace Bug10610;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class HelloWorld
8+
{
9+
private const MAP = [
10+
'foo' => [
11+
'19' => '582',
12+
'26' => '689',
13+
'56' => '817',
14+
'52' => '1050',
15+
'67' => '2923',
16+
'78' => '4057',
17+
'75' => '4078',
18+
'54' => '4078',
19+
'76' => '4079',
20+
'77' => '4080',
21+
'9' => '4080',
22+
'46' => '4091',
23+
'22' => '4111',
24+
'48' => '4112',
25+
'70' => '4113',
26+
'42' => '4117',
27+
'43' => '4118',
28+
'6' => '4126',
29+
'36' => '4129',
30+
'13' => '4309',
31+
'14' => '4904',
32+
'5' => '5222',
33+
'71' => '5223',
34+
'73' => '5242',
35+
'74' => '5250',
36+
'24' => '5252',
37+
'58' => '5255',
38+
'35' => '5261',
39+
'1' => '5264',
40+
'20' => '5268',
41+
'21' => '5269',
42+
'31' => '5270',
43+
'51' => '5271',
44+
'55' => '5271',
45+
'39' => '5274',
46+
'50' => '5277',
47+
'49' => '5278',
48+
'11' => '5279',
49+
'41' => '5279',
50+
'44' => '5280',
51+
'59' => '5281',
52+
'60' => '5281',
53+
'23' => '5281',
54+
'72' => '5283',
55+
'32' => '5283',
56+
'8' => '5285',
57+
'40' => '5285',
58+
'12' => '5298',
59+
'37' => '5305',
60+
'65' => '5310',
61+
'64' => '5310',
62+
'57' => '5352',
63+
'33' => '5364',
64+
'25' => '5375',
65+
'34' => '5460',
66+
'45' => '7581',
67+
'3' => '7624',
68+
'53' => '7672',
69+
'999' => '7953',
70+
'69' => '7953',
71+
'2' => '8206',
72+
'7' => '9697',
73+
],
74+
'bar' => [
75+
'30' => 'Test3',
76+
],
77+
];
78+
79+
public function validate(string $k, string $value): void
80+
{
81+
$res = self::MAP[$k][$value] ?? '';
82+
83+
assertType("'1050'|'2923'|'4057'|'4078'|'4079'|'4080'|'4091'|'4111'|'4112'|'4113'|'4117'|'4118'|'4126'|'4129'|'4309'|'4904'|'5222'|'5223'|'5242'|'5250'|'5252'|'5255'|'5261'|'5264'|'5268'|'5269'|'5270'|'5271'|'5274'|'5277'|'5278'|'5279'|'5280'|'5281'|'5283'|'5285'|'5298'|'5305'|'5310'|'5352'|'5364'|'5375'|'5460'|'582'|'689'|'7581'|'7624'|'7672'|'7953'|'817'|'8206'|'9697'|'Test3'", self::MAP[$k][$value]);
84+
85+
// ...
86+
}
87+
}

0 commit comments

Comments
 (0)