Skip to content

Commit 414879c

Browse files
committed
Fix phpstan/phpstan#14324: Crash in getAllArrays() when constant array has 32+ closures
- Disable array degradation in ConstantArrayType::getAllArrays() builder - The builder was degrading to a general array due to CLOSURES_COUNT_LIMIT, causing a ShouldNotHappenException since getAllArrays() expects ConstantArrayType - New regression test in tests/PHPStan/Analyser/nsrt/bug-14324.php
1 parent 6bac0de commit 414879c

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

src/Type/Constant/ConstantArrayType.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ public function getAllArrays(): array
241241
}
242242

243243
$builder = ConstantArrayTypeBuilder::createEmpty();
244+
$builder->disableArrayDegradation();
244245
foreach ($keys as $i) {
245246
$builder->setOffsetValueType($this->keyTypes[$i], $this->valueTypes[$i]);
246247
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14324;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
final class Test
8+
{
9+
private const ADDITIONAL_MAPS = [
10+
'foo-',
11+
'bar-',
12+
'baz-',
13+
];
14+
15+
/** @var array<string, callable(): string> */
16+
private static array $map = [];
17+
18+
public function createMap(): void
19+
{
20+
if ([] === self::$map) {
21+
// 29 entries
22+
self::$map = [
23+
'foo' => static fn() => 'foo',
24+
'bar' => static fn() => 'bar',
25+
'baz' => static fn() => 'baz',
26+
'qux' => static fn() => 'qux',
27+
'quux' => static fn() => 'quux',
28+
'corge' => static fn() => 'corge',
29+
'grault' => static fn() => 'grault',
30+
'garply' => static fn() => 'garply',
31+
'waldo' => static fn() => 'waldo',
32+
'fred' => static fn() => 'fred',
33+
'plugh' => static fn() => 'plugh',
34+
'xyzzy' => static fn() => 'xyzzy',
35+
'thud' => static fn() => 'thud',
36+
'foo1' => static fn() => 'foo1',
37+
'bar1' => static fn() => 'bar1',
38+
'baz1' => static fn() => 'baz1',
39+
'qux1' => static fn() => 'qux1',
40+
'quux1' => static fn() => 'quux1',
41+
'corge1' => static fn() => 'corge1',
42+
'grault1' => static fn() => 'grault1',
43+
'garply1' => static fn() => 'garply1',
44+
'waldo1' => static fn() => 'waldo1',
45+
'fred1' => static fn() => 'fred1',
46+
'plugh1' => static fn() => 'plugh1',
47+
'xyzzy1' => static fn() => 'xyzzy1',
48+
'thud1' => static fn() => 'thud1',
49+
'foo2' => static fn() => 'foo2',
50+
'bar2' => static fn() => 'bar2',
51+
'baz2' => static fn() => 'baz2',
52+
];
53+
54+
foreach (self::ADDITIONAL_MAPS as $map) {
55+
// added with 3 entries, breaching the closure limit of 32 entries
56+
self::$map[$map] = fn () => self::$map['foo']();
57+
}
58+
59+
assertType("non-empty-array<'bar'|'bar-'|'bar1'|'bar2'|'baz'|'baz-'|'baz1'|'baz2'|'corge'|'corge1'|'foo'|'foo-'|'foo1'|'foo2'|'fred'|'fred1'|'garply'|'garply1'|'grault'|'grault1'|'plugh'|'plugh1'|'quux'|'quux1'|'qux'|'qux1'|'thud'|'thud1'|'waldo'|'waldo1'|'xyzzy'|'xyzzy1', callable(): mixed>&oversized-array", self::$map);
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)