Skip to content

Commit 36b3925

Browse files
Fix numeric-string to array-key
1 parent 2799c24 commit 36b3925

11 files changed

+112
-11
lines changed

src/Type/Accessory/AccessoryNumericStringType.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,13 @@ public function toArray(): Type
229229

230230
public function toArrayKey(): Type
231231
{
232-
return new IntegerType();
232+
return new UnionType([
233+
new IntegerType(),
234+
new IntersectionType([
235+
new StringType(),
236+
new AccessoryNumericStringType(),
237+
]),
238+
]);
233239
}
234240

235241
public function isNull(): TrinaryLogic

src/Type/Constant/ConstantArrayType.php

+9-3
Original file line numberDiff line numberDiff line change
@@ -609,11 +609,17 @@ public function findTypeAndMethodNames(): array
609609

610610
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
611611
{
612-
$offsetType = $offsetType->toArrayKey();
612+
$offsetArrayKeyType = $offsetType->toArrayKey();
613+
614+
return $this->recursiveHasOffsetValueType($offsetArrayKeyType);
615+
}
616+
617+
private function recursiveHasOffsetValueType(Type $offsetType): TrinaryLogic
618+
{
613619
if ($offsetType instanceof UnionType) {
614620
$results = [];
615621
foreach ($offsetType->getTypes() as $innerType) {
616-
$results[] = $this->hasOffsetValueType($innerType);
622+
$results[] = $this->recursiveHasOffsetValueType($innerType);
617623
}
618624

619625
return TrinaryLogic::extremeIdentity(...$results);
@@ -623,7 +629,7 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic
623629
if ($finiteTypes !== []) {
624630
$results = [];
625631
foreach ($finiteTypes as $innerType) {
626-
$results[] = $this->hasOffsetValueType($innerType);
632+
$results[] = $this->recursiveHasOffsetValueType($innerType);
627633
}
628634

629635
return TrinaryLogic::extremeIdentity(...$results);

src/Type/IntersectionType.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,10 @@ public function toArray(): Type
10291029
public function toArrayKey(): Type
10301030
{
10311031
if ($this->isNumericString()->yes()) {
1032-
return new IntegerType();
1032+
return TypeCombinator::union(
1033+
new IntegerType(),
1034+
$this,
1035+
);
10331036
}
10341037

10351038
if ($this->isString()->yes()) {

tests/PHPStan/Analyser/nsrt/bug-3133.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public function doLorem(
5252
{
5353
$a = [];
5454
$a[$numericString] = 'foo';
55-
assertType('non-empty-array<int, \'foo\'>', $a);
55+
assertType('non-empty-array<int|numeric-string, \'foo\'>', $a);
5656
}
5757

5858
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug8592;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @param array<numeric-string, mixed> $foo
9+
*/
10+
function foo(array $foo): void
11+
{
12+
foreach ($foo as $key => $value) {
13+
assertType('int|numeric-string', $key);
14+
}
15+
}

tests/PHPStan/Analyser/nsrt/isset-coalesce-empty-type.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ class Bug4671
296296
*/
297297
public function doFoo(int $intput, array $strings): void
298298
{
299-
assertType('false', isset($strings[(string) $intput]));
299+
assertType('bool', isset($strings[(string) $intput]));
300300
}
301301

302302
}

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

+5
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,11 @@ public function testBug2634(): void
898898
$this->analyse([__DIR__ . '/data/bug-2634.php'], []);
899899
}
900900

901+
public function testBug11390(): void
902+
{
903+
$this->analyse([__DIR__ . '/data/bug-11390.php'], []);
904+
}
905+
901906
public function testInternalClassesWithOverloadedOffsetAccess(): void
902907
{
903908
$this->analyse([__DIR__ . '/data/internal-classes-overload-offset-access.php'], []);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11390;
4+
5+
/**
6+
* @param array<array{
7+
* id: numeric-string,
8+
* tagName: string
9+
* }> $tags
10+
* @param numeric-string $tagId
11+
*/
12+
function printTagName(array $tags, string $tagId): void
13+
{
14+
// Adding the second `*` to either of the following lines makes the error disappear
15+
16+
$tagsById = array_combine(array_column($tags, 'id'), $tags);
17+
if (false !== $tagsById) {
18+
echo $tagsById[$tagId]['tagName'] . PHP_EOL;
19+
}
20+
}
21+
22+
printTagName(
23+
[
24+
['id' => '123', 'tagName' => 'abc'],
25+
['id' => '4.5', 'tagName' => 'def'],
26+
['id' => '6e78', 'tagName' => 'ghi']
27+
],
28+
'4.5'
29+
);

tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php

+10
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,16 @@ public function testBug10715(): void
10691069
$this->analyse([__DIR__ . '/data/bug-10715.php'], []);
10701070
}
10711071

1072+
public function testBug4163(): void
1073+
{
1074+
$this->analyse([__DIR__ . '/data/bug-4163.php'], [
1075+
[
1076+
'Method Bug4163\HelloWorld::lall() should return array<string, string> but returns array<int|string, string>.',
1077+
28,
1078+
],
1079+
]);
1080+
}
1081+
10721082
public function testBug11663(): void
10731083
{
10741084
$this->analyse([__DIR__ . '/data/bug-11663.php'], []);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug4163;
4+
5+
class HelloWorld
6+
{
7+
/**
8+
* @phpstan-var numeric
9+
*/
10+
public $lall = 0;
11+
12+
/**
13+
* @phpstan-return array<string, string>
14+
*/
15+
function lall() {
16+
$helloCollection = [new HelloWorld(), new HelloWorld()];
17+
$result = [];
18+
19+
foreach ($helloCollection as $hello) {
20+
$key = (string)$hello->lall;
21+
22+
if (!isset($result[$key])) {
23+
$lall = 'do_something_here';
24+
$result[$key] = $lall;
25+
}
26+
}
27+
28+
return $result;
29+
}
30+
}

tests/PHPStan/Rules/Variables/IssetRuleTest.php

+1-4
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,7 @@ public function testBug4671(): void
233233
{
234234
$this->treatPhpDocTypesAsCertain = true;
235235
$this->strictUnnecessaryNullsafePropertyFetch = false;
236-
$this->analyse([__DIR__ . '/data/bug-4671.php'], [[
237-
'Offset numeric-string on array<string, string> in isset() does not exist.',
238-
13,
239-
]]);
236+
$this->analyse([__DIR__ . '/data/bug-4671.php'], []);
240237
}
241238

242239
public function testVariableCertaintyInIsset(): void

0 commit comments

Comments
 (0)