Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4d58c28

Browse files
herndlmondrejmirtes
authored andcommitted
Fix array_column() with explicit null $index_key
1 parent f61439f commit 4d58c28

File tree

6 files changed

+91
-7
lines changed

6 files changed

+91
-7
lines changed

‎src/Type/Php/ArrayColumnFunctionReturnTypeExtension.php‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Reflection\FunctionReflection;
88
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
9+
use PHPStan\Type\NullType;
910
use PHPStan\Type\Type;
1011
use function count;
1112

@@ -32,7 +33,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
3233

3334
$arrayType = $scope->getType($functionCall->getArgs()[0]->value);
3435
$columnType = $scope->getType($functionCall->getArgs()[1]->value);
35-
$indexType = $numArgs >= 3 ? $scope->getType($functionCall->getArgs()[2]->value) : null;
36+
$indexType = $numArgs >= 3 ? $scope->getType($functionCall->getArgs()[2]->value) : newNullType();
3637

3738
$constantArrayTypes = $arrayType->getConstantArrays();
3839
if (count($constantArrayTypes) === 1) {

‎src/Type/Php/ArrayColumnHelper.php‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ public function getReturnValueType(Type $arrayType, Type $columnType, Scope $sco
5050
return [$returnValueType, $iterableAtLeastOnce];
5151
}
5252

53-
public function getReturnIndexType(Type $arrayType, ?Type $indexType, Scope $scope): Type
53+
public function getReturnIndexType(Type $arrayType, Type $indexType, Scope $scope): Type
5454
{
55-
if ($indexType !== null) {
55+
if (!$indexType->isNull()->yes()) {
5656
$iterableValueType = $arrayType->getIterableValueType();
5757

5858
$type = $this->getOffsetOrProperty($iterableValueType, $indexType, $scope, false);
@@ -69,7 +69,7 @@ public function getReturnIndexType(Type $arrayType, ?Type $indexType, Scope $sco
6969
return new IntegerType();
7070
}
7171

72-
public function handleAnyArray(Type $arrayType, Type $columnType, ?Type $indexType, Scope $scope): Type
72+
public function handleAnyArray(Type $arrayType, Type $columnType, Type $indexType, Scope $scope): Type
7373
{
7474
[$returnValueType, $iterableAtLeastOnce] = $this->getReturnValueType($arrayType, $columnType, $scope);
7575
if ($returnValueType instanceof NeverType) {
@@ -82,14 +82,14 @@ public function handleAnyArray(Type $arrayType, Type $columnType, ?Type $indexTy
8282
if ($iterableAtLeastOnce->yes()) {
8383
$returnType = TypeCombinator::intersect($returnType, new NonEmptyArrayType());
8484
}
85-
if ($indexType === null) {
85+
if ($indexType->isNull()->yes()) {
8686
$returnType = TypeCombinator::intersect($returnType, new AccessoryArrayListType());
8787
}
8888

8989
return $returnType;
9090
}
9191

92-
public function handleConstantArray(ConstantArrayType $arrayType, Type $columnType, ?Type $indexType, Scope $scope): ?Type
92+
public function handleConstantArray(ConstantArrayType $arrayType, Type $columnType, Type $indexType, Scope $scope): ?Type
9393
{
9494
$builder = ConstantArrayTypeBuilder::createEmpty();
9595

@@ -102,7 +102,7 @@ public function handleConstantArray(ConstantArrayType $arrayType, Type $columnTy
102102
continue;
103103
}
104104

105-
if ($indexType !== null) {
105+
if (!$indexType->isNull()->yes()) {
106106
$type = $this->getOffsetOrProperty($iterableValueType, $indexType, $scope, false);
107107
if ($type !== null) {
108108
$keyType = $type;

‎tests/PHPStan/Analyser/nsrt/array-column-php82.php‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class ArrayColumnTest
1313
public function testArray1(array $array): void
1414
{
1515
assertType('list<string>', array_column($array, 'column'));
16+
assertType('list<string>', array_column($array, 'column', null));
1617
assertType('array<int|string, string>', array_column($array, 'column', 'key'));
1718
assertType('array<int|string, array<string, string>>', array_column($array, null, 'key'));
1819
}
@@ -22,12 +23,14 @@ public function testArray2(array $array): void
2223
{
2324
// Note: Array may still be empty!
2425
assertType('list<string>', array_column($array, 'column'));
26+
assertType('list<string>', array_column($array, 'column', null));
2527
}
2628

2729
/** @param array{} $array */
2830
public function testArray3(array $array): void
2931
{
3032
assertType('array{}', array_column($array, 'column'));
33+
assertType('array{}', array_column($array, 'column', null));
3134
assertType('array{}', array_column($array, 'column', 'key'));
3235
assertType('array{}', array_column($array, null, 'key'));
3336
}
@@ -66,6 +69,7 @@ public function testArray8(array $array): void
6669
public function testConstantArray1(array $array): void
6770
{
6871
assertType('list<string>', array_column($array, 'column'));
72+
assertType('list<string>', array_column($array, 'column', null));
6973
assertType('array<string, string>', array_column($array, 'column', 'key'));
7074
assertType('array<string, array{column: string, key: string}>', array_column($array, null, 'key'));
7175
}
@@ -74,13 +78,15 @@ public function testConstantArray1(array $array): void
7478
public function testConstantArray2(array $array): void
7579
{
7680
assertType('array{}', array_column($array, 'foo'));
81+
assertType('array{}', array_column($array, 'foo', null));
7782
assertType('array{}', array_column($array, 'foo', 'key'));
7883
}
7984

8085
/** @param array{array{column: string, key: 'bar'}} $array */
8186
public function testConstantArray3(array $array): void
8287
{
8388
assertType("array{string}", array_column($array, 'column'));
89+
assertType("array{string}", array_column($array, 'column', null));
8490
assertType("array{bar: string}", array_column($array, 'column', 'key'));
8591
assertType("array{bar: array{column: string, key: 'bar'}}", array_column($array, null, 'key'));
8692
}
@@ -96,6 +102,7 @@ public function testConstantArray4(array $array): void
96102
public function testConstantArray5(array $array): void
97103
{
98104
assertType("list<'foo'>", array_column($array, 'column'));
105+
assertType("list<'foo'>", array_column($array, 'column', null));
99106
assertType("array<'bar'|int, 'foo'>", array_column($array, 'column', 'key'));
100107
assertType("array<'bar'|int, array{column?: 'foo', key?: 'bar'}>", array_column($array, null, 'key'));
101108
}
@@ -104,12 +111,14 @@ public function testConstantArray5(array $array): void
104111
public function testConstantArray6(array $array): void
105112
{
106113
assertType('list<bool|string>', array_column($array, mt_rand(0, 1) === 0 ? 'column1' : 'column2'));
114+
assertType('list<bool|string>', array_column($array, mt_rand(0, 1) === 0 ? 'column1' : 'column2', null));
107115
}
108116

109117
/** @param non-empty-array<int, array{column: string, key: string}> $array */
110118
public function testConstantArray7(array $array): void
111119
{
112120
assertType('non-empty-list<string>', array_column($array, 'column'));
121+
assertType('non-empty-list<string>', array_column($array, 'column', null));
113122
assertType('non-empty-array<string, string>', array_column($array, 'column', 'key'));
114123
assertType('non-empty-array<string, array{column: string, key: string}>', array_column($array, null, 'key'));
115124
}
@@ -142,6 +151,7 @@ public function testConstantArray11(array $array): void
142151
public function testConstantArray12(array $array): void
143152
{
144153
assertType("array{0?: 'foo'}", array_column($array, 'column'));
154+
assertType("array{0?: 'foo'}", array_column($array, 'column', null));
145155
assertType("array{bar?: 'foo'}", array_column($array, 'column', 'key'));
146156
}
147157

@@ -151,6 +161,7 @@ public function testConstantArray12(array $array): void
151161
public function testImprecise1(array $array): void
152162
{
153163
assertType("list<'foo'>", array_column($array, 'column'));
164+
assertType("list<'foo'>", array_column($array, 'column', null));
154165
assertType("array<'bar', 'foo'>", array_column($array, 'column', 'key'));
155166
assertType("array{bar: array{column?: 'foo', key: 'bar'}}", array_column($array, null, 'key'));
156167
}
@@ -166,16 +177,19 @@ public function testImprecise2(array $array): void
166177
public function testImprecise3(array $array): void
167178
{
168179
assertType('list<string>', array_column($array, 'column'));
180+
assertType('list<string>', array_column($array, 'column', null));
169181
assertType('array<int|string, string>', array_column($array, 'column', 'key'));
170182
}
171183

172184
/** @param array<int, DOMElement> $array */
173185
public function testImprecise5(array $array): void
174186
{
175187
assertType('list<string>', array_column($array, 'nodeName'));
188+
assertType('list<string>', array_column($array, 'nodeName', null));
176189
assertType('array<string, string>', array_column($array, 'nodeName', 'tagName'));
177190
assertType('array<string, DOMElement>', array_column($array, null, 'tagName'));
178191
assertType('list', array_column($array, 'foo'));
192+
assertType('list', array_column($array, 'foo', null));
179193
assertType('array<string, mixed>', array_column($array, 'foo', 'tagName'));
180194
assertType('array<string>', array_column($array, 'nodeName', 'foo'));
181195
assertType('array<DOMElement>', array_column($array, null, 'foo'));
@@ -185,9 +199,11 @@ public function testImprecise5(array $array): void
185199
public function testObjects1(array $array): void
186200
{
187201
assertType('non-empty-list<string>', array_column($array, 'nodeName'));
202+
assertType('non-empty-list<string>', array_column($array, 'nodeName', null));
188203
assertType('non-empty-array<string, string>', array_column($array, 'nodeName', 'tagName'));
189204
assertType('non-empty-array<string, DOMElement>', array_column($array, null, 'tagName'));
190205
assertType('list', array_column($array, 'foo'));
206+
assertType('list', array_column($array, 'foo', null));
191207
assertType('array<string, mixed>', array_column($array, 'foo', 'tagName'));
192208
assertType('non-empty-array<string>', array_column($array, 'nodeName', 'foo'));
193209
assertType('non-empty-array<DOMElement>', array_column($array, null, 'foo'));
@@ -197,9 +213,11 @@ public function testObjects1(array $array): void
197213
public function testObjects2(array $array): void
198214
{
199215
assertType('array{string}', array_column($array, 'nodeName'));
216+
assertType('array{string}', array_column($array, 'nodeName', null));
200217
assertType('non-empty-array<string, string>', array_column($array, 'nodeName', 'tagName'));
201218
assertType('non-empty-array<string, DOMElement>', array_column($array, null, 'tagName'));
202219
assertType('list', array_column($array, 'foo'));
220+
assertType('list', array_column($array, 'foo', null));
203221
assertType('array<string, mixed>', array_column($array, 'foo', 'tagName'));
204222
assertType('non-empty-array<string>', array_column($array, 'nodeName', 'foo'));
205223
assertType('non-empty-array<DOMElement>', array_column($array, null, 'foo'));
@@ -214,6 +232,7 @@ final class Foo
214232
public function doFoo(array $a): void
215233
{
216234
assertType('array{}', array_column($a, 'nodeName'));
235+
assertType('array{}', array_column($a, 'nodeName', null));
217236
assertType('array{}', array_column($a, 'nodeName', 'tagName'));
218237
}
219238

‎tests/PHPStan/Analyser/nsrt/array-column.php‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class ArrayColumnTest
1313
public function testArray1(array $array): void
1414
{
1515
assertType('list<string>', array_column($array, 'column'));
16+
assertType('list<string>', array_column($array, 'column', null));
1617
assertType('array<int|string, string>', array_column($array, 'column', 'key'));
1718
assertType('array<int|string, array<string, string>>', array_column($array, null, 'key'));
1819
}
@@ -22,12 +23,14 @@ public function testArray2(array $array): void
2223
{
2324
// Note: Array may still be empty!
2425
assertType('list<string>', array_column($array, 'column'));
26+
assertType('list<string>', array_column($array, 'column', null));
2527
}
2628

2729
/** @param array{} $array */
2830
public function testArray3(array $array): void
2931
{
3032
assertType('array{}', array_column($array, 'column'));
33+
assertType('array{}', array_column($array, 'column', null));
3134
assertType('array{}', array_column($array, 'column', 'key'));
3235
assertType('array{}', array_column($array, null, 'key'));
3336
}
@@ -66,6 +69,7 @@ public function testArray8(array $array): void
6669
public function testConstantArray1(array $array): void
6770
{
6871
assertType('list<string>', array_column($array, 'column'));
72+
assertType('list<string>', array_column($array, 'column', null));
6973
assertType('array<string, string>', array_column($array, 'column', 'key'));
7074
assertType('array<string, array{column: string, key: string}>', array_column($array, null, 'key'));
7175
}
@@ -74,13 +78,15 @@ public function testConstantArray1(array $array): void
7478
public function testConstantArray2(array $array): void
7579
{
7680
assertType('array{}', array_column($array, 'foo'));
81+
assertType('array{}', array_column($array, 'foo', null));
7782
assertType('array{}', array_column($array, 'foo', 'key'));
7883
}
7984

8085
/** @param array{array{column: string, key: 'bar'}} $array */
8186
public function testConstantArray3(array $array): void
8287
{
8388
assertType("array{string}", array_column($array, 'column'));
89+
assertType("array{string}", array_column($array, 'column', null));
8490
assertType("array{bar: string}", array_column($array, 'column', 'key'));
8591
assertType("array{bar: array{column: string, key: 'bar'}}", array_column($array, null, 'key'));
8692
}
@@ -96,6 +102,7 @@ public function testConstantArray4(array $array): void
96102
public function testConstantArray5(array $array): void
97103
{
98104
assertType("list<'foo'>", array_column($array, 'column'));
105+
assertType("list<'foo'>", array_column($array, 'column', null));
99106
assertType("array<'bar'|int, 'foo'>", array_column($array, 'column', 'key'));
100107
assertType("array<'bar'|int, array{column?: 'foo', key?: 'bar'}>", array_column($array, null, 'key'));
101108
}
@@ -104,12 +111,14 @@ public function testConstantArray5(array $array): void
104111
public function testConstantArray6(array $array): void
105112
{
106113
assertType('list<bool|string>', array_column($array, mt_rand(0, 1) === 0 ? 'column1' : 'column2'));
114+
assertType('list<bool|string>', array_column($array, mt_rand(0, 1) === 0 ? 'column1' : 'column2', null));
107115
}
108116

109117
/** @param non-empty-array<int, array{column: string, key: string}> $array */
110118
public function testConstantArray7(array $array): void
111119
{
112120
assertType('non-empty-list<string>', array_column($array, 'column'));
121+
assertType('non-empty-list<string>', array_column($array, 'column', null));
113122
assertType('non-empty-array<string, string>', array_column($array, 'column', 'key'));
114123
assertType('non-empty-array<string, array{column: string, key: string}>', array_column($array, null, 'key'));
115124
}
@@ -142,20 +151,23 @@ public function testConstantArray11(array $array): void
142151
public function testConstantArray12(array $array): void
143152
{
144153
assertType("array{0?: 'foo'}", array_column($array, 'column'));
154+
assertType("array{0?: 'foo'}", array_column($array, 'column', null));
145155
assertType("array{bar?: 'foo'}", array_column($array, 'column', 'key'));
146156
}
147157

148158
/** @param array{0?: array{column: 'foo1', key: 'bar1'}, 1?: array{column: 'foo2', key: 'bar2'}} $array */
149159
public function testConstantArray13(array $array): void
150160
{
151161
assertType("array{0?: 'foo1'|'foo2', 1?: 'foo2'}", array_column($array, 'column'));
162+
assertType("array{0?: 'foo1'|'foo2', 1?: 'foo2'}", array_column($array, 'column', null));
152163
assertType("array{bar1?: 'foo1', bar2?: 'foo2'}", array_column($array, 'column', 'key'));
153164
}
154165

155166
/** @param array{0?: array{column: 'foo1', key: 'bar1'}, 1: array{column: 'foo2', key: 'bar2'}} $array */
156167
public function testConstantArray14(array $array): void
157168
{
158169
assertType("array{0: 'foo1'|'foo2', 1?: 'foo2'}", array_column($array, 'column'));
170+
assertType("array{0: 'foo1'|'foo2', 1?: 'foo2'}", array_column($array, 'column', null));
159171
assertType("array{bar1?: 'foo1', bar2: 'foo2'}", array_column($array, 'column', 'key'));
160172
}
161173

@@ -165,6 +177,7 @@ public function testConstantArray14(array $array): void
165177
public function testImprecise1(array $array): void
166178
{
167179
assertType("list<'foo'>", array_column($array, 'column'));
180+
assertType("list<'foo'>", array_column($array, 'column', null));
168181
assertType("array<'bar', 'foo'>", array_column($array, 'column', 'key'));
169182
assertType("array{bar: array{column?: 'foo', key: 'bar'}}", array_column($array, null, 'key'));
170183
}
@@ -180,16 +193,19 @@ public function testImprecise2(array $array): void
180193
public function testImprecise3(array $array): void
181194
{
182195
assertType('list<string>', array_column($array, 'column'));
196+
assertType('list<string>', array_column($array, 'column', null));
183197
assertType('array<int|string, string>', array_column($array, 'column', 'key'));
184198
}
185199

186200
/** @param array<int, DOMElement> $array */
187201
public function testImprecise5(array $array): void
188202
{
189203
assertType('list<string>', array_column($array, 'nodeName'));
204+
assertType('list<string>', array_column($array, 'nodeName', null));
190205
assertType('array<string, string>', array_column($array, 'nodeName', 'tagName'));
191206
assertType('array<string, DOMElement>', array_column($array, null, 'tagName'));
192207
assertType('list', array_column($array, 'foo'));
208+
assertType('list', array_column($array, 'foo', null));
193209
assertType('array<string, mixed>', array_column($array, 'foo', 'tagName'));
194210
assertType('array<string>', array_column($array, 'nodeName', 'foo'));
195211
assertType('array<DOMElement>', array_column($array, null, 'foo'));
@@ -199,9 +215,11 @@ public function testImprecise5(array $array): void
199215
public function testObjects1(array $array): void
200216
{
201217
assertType('non-empty-list<string>', array_column($array, 'nodeName'));
218+
assertType('non-empty-list<string>', array_column($array, 'nodeName', null));
202219
assertType('non-empty-array<string, string>', array_column($array, 'nodeName', 'tagName'));
203220
assertType('non-empty-array<string, DOMElement>', array_column($array, null, 'tagName'));
204221
assertType('list', array_column($array, 'foo'));
222+
assertType('list', array_column($array, 'foo', null));
205223
assertType('array<string, mixed>', array_column($array, 'foo', 'tagName'));
206224
assertType('non-empty-array<string>', array_column($array, 'nodeName', 'foo'));
207225
assertType('non-empty-array<DOMElement>', array_column($array, null, 'foo'));
@@ -211,9 +229,11 @@ public function testObjects1(array $array): void
211229
public function testObjects2(array $array): void
212230
{
213231
assertType('array{string}', array_column($array, 'nodeName'));
232+
assertType('array{string}', array_column($array, 'nodeName', null));
214233
assertType('non-empty-array<string, string>', array_column($array, 'nodeName', 'tagName'));
215234
assertType('non-empty-array<string, DOMElement>', array_column($array, null, 'tagName'));
216235
assertType('list', array_column($array, 'foo'));
236+
assertType('list', array_column($array, 'foo', null));
217237
assertType('array<string, mixed>', array_column($array, 'foo', 'tagName'));
218238
assertType('non-empty-array<string>', array_column($array, 'nodeName', 'foo'));
219239
assertType('non-empty-array<DOMElement>', array_column($array, null, 'foo'));
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12954;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
$plop = [
8+
12 => [
9+
'name' => 'ROLE_USER',
10+
'description' => 'User role'
11+
],
12+
28 => [
13+
'name' => 'ROLE_ADMIN',
14+
'description' => 'Admin role'
15+
],
16+
43 => [
17+
'name' => 'ROLE_SUPER_ADMIN',
18+
'description' => 'SUPER Admin role'
19+
],
20+
];
21+
22+
$list = ['ROLE_USER', 'ROLE_ADMIN', 'ROLE_SUPER_ADMIN'];
23+
24+
$result = array_column($plop, 'name', null);
25+
26+
/**
27+
* @param list<string> $array
28+
*/
29+
function doSomething(array $array): void
30+
{
31+
assertType('list<string>', $array);
32+
}
33+
34+
doSomething($result);
35+
doSomething($list);
36+
37+
assertType('array{\'ROLE_USER\', \'ROLE_ADMIN\', \'ROLE_SUPER_ADMIN\'}', $result);
38+
assertType('array{\'ROLE_USER\', \'ROLE_ADMIN\', \'ROLE_SUPER_ADMIN\'}', $list);

‎tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,4 +2242,10 @@ public function testBug12847(): void
22422242
]);
22432243
}
22442244

2245+
public function testBug12954(): void
2246+
{
2247+
$this->checkExplicitMixed = true;
2248+
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-12954.php'], []);
2249+
}
2250+
22452251
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /