Skip to content

Commit 342f5b4

Browse files
phpstan-botclaude
andcommitted
Add regression tests for related issues fixed by constant array shape preservation
Add non-regression tests for: - phpstan/phpstan#14080: array key type preserved through foreach loop - phpstan/phpstan#13623: nested array with dynamic keys and ??= operator - phpstan/phpstan#8774: constant string keys not overwritten by loop - phpstan/phpstan#9907: array shape preserved with union key parameter - phpstan/phpstan#11006: array shape preserved with nullable union key Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0888c0d commit 342f5b4

5 files changed

Lines changed: 144 additions & 0 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11006;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class StringOrNullAttributeDto
8+
{
9+
public function __construct(
10+
public ?string $data,
11+
) {
12+
}
13+
}
14+
15+
class StringAttributeDto
16+
{
17+
public function __construct(
18+
public string $data,
19+
) {
20+
}
21+
}
22+
23+
class PhpStanProblem
24+
{
25+
/**
26+
* @param null|'size_uk'|'size_us' $sizeAttributeCode
27+
*/
28+
public function example(?string $sizeAttributeCode): void
29+
{
30+
$values = [
31+
'ean' => [
32+
new StringOrNullAttributeDto(''),
33+
],
34+
$sizeAttributeCode => [
35+
new StringOrNullAttributeDto(''),
36+
],
37+
'osa_sizes' => [
38+
new StringAttributeDto(''),
39+
],
40+
];
41+
42+
assertType("array{ean: array{Bug11006\StringOrNullAttributeDto}, ''?: array{Bug11006\StringOrNullAttributeDto}, size_uk?: array{Bug11006\StringOrNullAttributeDto}, size_us?: array{Bug11006\StringOrNullAttributeDto}, osa_sizes: array{Bug11006\StringAttributeDto}}", $values);
43+
}
44+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug13623;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @param list<array{customer_id: int, order_id: int, order_total: float}> $results
9+
*/
10+
function doFoo(array $results): void
11+
{
12+
$customers = [];
13+
14+
foreach ($results as $row) {
15+
$customers[$row['customer_id']] ??= [];
16+
$customers[$row['customer_id']]['orders'] ??= [];
17+
$customers[$row['customer_id']]['orders'][$row['order_id']] ??= [];
18+
19+
$customers[$row['customer_id']]['orders'][$row['order_id']]['balance_forward'] ??= 0;
20+
$customers[$row['customer_id']]['orders'][$row['order_id']]['new_invoice'] ??= 0;
21+
$customers[$row['customer_id']]['orders'][$row['order_id']]['payments'] ??= 0;
22+
$customers[$row['customer_id']]['orders'][$row['order_id']]['balance'] ??= $row['order_total'];
23+
}
24+
25+
assertType('array<int, array{orders: non-empty-array<int, array{balance_forward: 0, new_invoice: 0, payments: 0, balance: float}>}>', $customers);
26+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14080;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
/**
10+
* @param list<array{sql: string, time: int}> $queries
11+
*/
12+
public function doFoo(array $queries): void
13+
{
14+
$queryTotals = ['all' => 0, 'duplicates' => 0];
15+
$queryTypes = ['select', 'update', 'delete', 'insert'];
16+
17+
$queryTotals['time'] = array_sum(array_column($queries, 'time'));
18+
19+
foreach ($queryTypes as $type) {
20+
assertType('int', $queryTotals['time']);
21+
$tq = array_filter($queries, fn ($v) => str_starts_with(strtolower($v['sql']), $type));
22+
$queryTotals['all'] += count($tq);
23+
$queryTotals[$type] = [
24+
'count' => count($tq),
25+
];
26+
}
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug8774;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class ModerateCtrl
8+
{
9+
private const DISABLE_KEYS_AND_LABELS = [
10+
'DisablePosting' => 'Posting on forum and comments',
11+
'DisableAvatar' => 'Avatar and Custom Icon',
12+
];
13+
14+
public static function handleModerate(): void
15+
{
16+
$summaryTemplates = [
17+
'PermissionID' => "Class changed from <b>'%s'</b> to <b>'%s'</b>.",
18+
'Reset' => '%s reset.',
19+
];
20+
21+
foreach (self::DISABLE_KEYS_AND_LABELS as $key => $label) {
22+
$summaryTemplates[$key] = "Disable $label status %s.";
23+
}
24+
25+
assertType("array{PermissionID: 'Class changed from <b>\\'%s\\'</b> to <b>\\'%s\\'</b>.', Reset: '%s reset.', DisablePosting?: 'Disable Avatar and Custom Icon status %s.'|'Disable Posting on forum and comments status %s.', DisableAvatar?: 'Disable Avatar and Custom Icon status %s.'|'Disable Posting on forum and comments status %s.'}", $summaryTemplates);
26+
assertType("'%s reset.'", $summaryTemplates['Reset']);
27+
}
28+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9907;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class HelloWorld
8+
{
9+
/**
10+
* @param 'foo'|'bar' $key
11+
*/
12+
public function sayHello(string $key): void
13+
{
14+
$a = ['id' => null, $key => 'string'];
15+
16+
assertType("array{id: null, foo?: 'string', bar?: 'string'}", $a);
17+
}
18+
}

0 commit comments

Comments
 (0)