Skip to content

Commit 28179f2

Browse files
jlherrenondrejmirtes
authored andcommitted
Fix array_column() with null as $column_key
Fixes phpstan/phpstan#6497
1 parent b833073 commit 28179f2

File tree

4 files changed

+44
-0
lines changed

4 files changed

+44
-0
lines changed

src/Type/Php/ArrayColumnFunctionReturnTypeExtension.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PHPStan\Type\IntegerType;
1717
use PHPStan\Type\MixedType;
1818
use PHPStan\Type\NeverType;
19+
use PHPStan\Type\NullType;
1920
use PHPStan\Type\Type;
2021
use PHPStan\Type\TypeCombinator;
2122
use PHPStan\Type\TypeUtils;
@@ -132,8 +133,17 @@ private function handleConstantArray(ConstantArrayType $arrayType, Type $columnT
132133

133134
private function getOffsetOrProperty(Type $type, Type $offsetOrProperty, Scope $scope, bool $allowMaybe): ?Type
134135
{
136+
$offsetIsNull = (new NullType())->isSuperTypeOf($offsetOrProperty);
137+
if ($offsetIsNull->yes()) {
138+
return $type;
139+
}
140+
135141
$returnTypes = [];
136142

143+
if ($offsetIsNull->maybe()) {
144+
$returnTypes[] = $type;
145+
}
146+
137147
if (!$type->canAccessProperties()->no()) {
138148
$propertyTypes = TypeUtils::getConstantStrings($offsetOrProperty);
139149
if ($propertyTypes === []) {

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ public function dataFileAsserts(): iterable
651651
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4357.php');
652652
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5817.php');
653653
yield from $this->gatherAssertTypes(__DIR__ . '/data/array-column.php');
654+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6497.php');
654655

655656
if (PHP_VERSION_ID >= 70400) {
656657
yield from $this->gatherAssertTypes(__DIR__ . '/data/isset-coalesce-empty-type.php');

tests/PHPStan/Analyser/data/array-column.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ function testArrays(array $array): void
1010
/** @var array<int, array<string, string>> $array */
1111
assertType('array<int, string>', array_column($array, 'column'));
1212
assertType('array<int|string, string>', array_column($array, 'column', 'key'));
13+
assertType('array<int|string, array<string, string>>', array_column($array, null, 'key'));
1314

1415
/** @var non-empty-array<int, array<string, string>> $array */
1516
// Note: Array may still be empty!
@@ -18,13 +19,15 @@ function testArrays(array $array): void
1819
/** @var array{} $array */
1920
assertType('array{}', array_column($array, 'column'));
2021
assertType('array{}', array_column($array, 'column', 'key'));
22+
assertType('array{}', array_column($array, null, 'key'));
2123
}
2224

2325
function testConstantArrays(array $array): void
2426
{
2527
/** @var array<int, array{column: string, key: string}> $array */
2628
assertType('array<int, string>', array_column($array, 'column'));
2729
assertType('array<string, string>', array_column($array, 'column', 'key'));
30+
assertType('array<string, array{column: string, key: string}>', array_column($array, null, 'key'));
2831

2932
/** @var array<int, array{column: string, key: string}> $array */
3033
assertType('array{}', array_column($array, 'foo'));
@@ -33,20 +36,24 @@ function testConstantArrays(array $array): void
3336
/** @var array{array{column: string, key: 'bar'}} $array */
3437
assertType("array{string}", array_column($array, 'column'));
3538
assertType("array{bar: string}", array_column($array, 'column', 'key'));
39+
assertType("array{bar: array{column: string, key: 'bar'}}", array_column($array, null, 'key'));
3640

3741
/** @var array{array{column: string, key: string}} $array */
3842
assertType("non-empty-array<string, string>", array_column($array, 'column', 'key'));
43+
assertType("non-empty-array<string, array{column: string, key: string}>", array_column($array, null, 'key'));
3944

4045
/** @var array<int, array{column?: 'foo', key?: 'bar'}> $array */
4146
assertType("array<int, 'foo'>", array_column($array, 'column'));
4247
assertType("array<'bar'|int, 'foo'>", array_column($array, 'column', 'key'));
48+
assertType("array<'bar'|int, array{column?: 'foo', key?: 'bar'}>", array_column($array, null, 'key'));
4349

4450
/** @var array<int, array{column1: string, column2: bool}> $array */
4551
assertType('array<int, bool|string>', array_column($array, mt_rand(0, 1) === 0 ? 'column1' : 'column2'));
4652

4753
/** @var non-empty-array<int, array{column: string, key: string}> $array */
4854
assertType('non-empty-array<int, string>', array_column($array, 'column'));
4955
assertType('non-empty-array<string, string>', array_column($array, 'column', 'key'));
56+
assertType('non-empty-array<string, array{column: string, key: string}>', array_column($array, null, 'key'));
5057
}
5158

5259
function testImprecise(array $array): void {
@@ -55,9 +62,11 @@ function testImprecise(array $array): void {
5562
/** @var array{array{column?: 'foo', key: 'bar'}} $array */
5663
assertType("array<int, 'foo'>", array_column($array, 'column'));
5764
assertType("array<'bar', 'foo'>", array_column($array, 'column', 'key'));
65+
assertType("array{bar: array{column?: 'foo', key: 'bar'}}", array_column($array, null, 'key'));
5866

5967
/** @var array{array{column: 'foo', key?: 'bar'}} $array */
6068
assertType("non-empty-array<'bar'|int, 'foo'>", array_column($array, 'column', 'key'));
69+
assertType("non-empty-array<'bar'|int, array{column: 'foo', key?: 'bar'}>", array_column($array, null, 'key'));
6170

6271
/** @var array{array{column: 'foo', key: 'bar'}}|array<int, array<string, string>> $array */
6372
assertType('array<int, string>', array_column($array, 'column'));
@@ -72,21 +81,27 @@ function testObjects(array $array): void {
7281
/** @var array<int, DOMElement> $array */
7382
assertType('array<int, string>', array_column($array, 'nodeName'));
7483
assertType('array<string, string>', array_column($array, 'nodeName', 'tagName'));
84+
assertType('array<string, DOMElement>', array_column($array, null, 'tagName'));
7585
assertType('array<int, mixed>', array_column($array, 'foo'));
7686
assertType('array<string, mixed>', array_column($array, 'foo', 'tagName'));
7787
assertType('array<string>', array_column($array, 'nodeName', 'foo'));
88+
assertType('array<DOMElement>', array_column($array, null, 'foo'));
7889

7990
/** @var non-empty-array<int, DOMElement> $array */
8091
assertType('non-empty-array<int, string>', array_column($array, 'nodeName'));
8192
assertType('non-empty-array<string, string>', array_column($array, 'nodeName', 'tagName'));
93+
assertType('non-empty-array<string, DOMElement>', array_column($array, null, 'tagName'));
8294
assertType('array<int, mixed>', array_column($array, 'foo'));
8395
assertType('array<string, mixed>', array_column($array, 'foo', 'tagName'));
8496
assertType('non-empty-array<string>', array_column($array, 'nodeName', 'foo'));
97+
assertType('non-empty-array<DOMElement>', array_column($array, null, 'foo'));
8598

8699
/** @var array{DOMElement} $array */
87100
assertType('array{string}', array_column($array, 'nodeName'));
88101
assertType('non-empty-array<string, string>', array_column($array, 'nodeName', 'tagName'));
102+
assertType('non-empty-array<string, DOMElement>', array_column($array, null, 'tagName'));
89103
assertType('array<int, mixed>', array_column($array, 'foo'));
90104
assertType('array<string, mixed>', array_column($array, 'foo', 'tagName'));
91105
assertType('non-empty-array<int|string, string>', array_column($array, 'nodeName', 'foo'));
106+
assertType('non-empty-array<int|string, DOMElement>', array_column($array, null, 'foo'));
92107
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Bug6497;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
function bug6497(): void {
10+
/** @var array<int, array{foo: string, bar: int}> */
11+
$array = [
12+
['foo' => 'baz', 'bar' => 3],
13+
];
14+
15+
$array2 = array_column($array, null, 'foo');
16+
17+
assertType('array<string, array{foo: string, bar: int}>', $array2);
18+
}

0 commit comments

Comments
 (0)