Skip to content

Commit 6d522a9

Browse files
committed
array_column should not extract non-public properties from objects
Fixes phpstan/phpstan#13573
1 parent 8873cdc commit 6d522a9

3 files changed

Lines changed: 110 additions & 5 deletions

File tree

src/Type/Php/ArrayColumnFunctionReturnTypeExtension.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ private function handleAnyArray(Type $arrayType, Type $columnType, ?Type $indexT
7979

8080
if ($indexType !== null) {
8181
$type = $this->getOffsetOrProperty($iterableValueType, $indexType, $scope, false);
82-
if ($type !== null) {
82+
if ($type !== null && !$type instanceof NeverType) {
8383
$returnKeyType = $type;
8484
} else {
8585
$type = $this->getOffsetOrProperty($iterableValueType, $indexType, $scope, true);
86-
if ($type !== null) {
86+
if ($type !== null && !$type instanceof NeverType) {
8787
$returnKeyType = TypeCombinator::union($type, new IntegerType());
8888
} else {
8989
$returnKeyType = new IntegerType();
@@ -120,11 +120,11 @@ private function handleConstantArray(ConstantArrayType $arrayType, Type $columnT
120120

121121
if ($indexType !== null) {
122122
$type = $this->getOffsetOrProperty($iterableValueType, $indexType, $scope, false);
123-
if ($type !== null) {
123+
if ($type !== null && !$type instanceof NeverType) {
124124
$keyType = $type;
125125
} else {
126126
$type = $this->getOffsetOrProperty($iterableValueType, $indexType, $scope, true);
127-
if ($type !== null) {
127+
if ($type !== null && !$type instanceof NeverType) {
128128
$keyType = TypeCombinator::union($type, new IntegerType());
129129
} else {
130130
$keyType = null;
@@ -171,7 +171,12 @@ private function getOffsetOrProperty(Type $type, Type $offsetOrProperty, Scope $
171171
continue;
172172
}
173173

174-
$returnTypes[] = $type->getProperty($propertyName, $scope)->getReadableType();
174+
$property = $type->getProperty($propertyName, $scope);
175+
if (!$property->isPublic()) {
176+
continue;
177+
}
178+
179+
$returnTypes[] = $property->getReadableType();
175180
}
176181
}
177182

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,53 @@ public function doFoo(array $a): void
218218
}
219219

220220
}
221+
222+
class ObjectWithVisibility
223+
{
224+
public int $pub = 1;
225+
protected int $prot = 2;
226+
private int $priv = 3;
227+
}
228+
229+
class ArrayColumnVisibilityTest
230+
{
231+
232+
/** @param array<int, ObjectWithVisibility> $objects */
233+
public function testNonPublicProperties(array $objects): void
234+
{
235+
assertType('list<int>', array_column($objects, 'pub'));
236+
assertType('array{}', array_column($objects, 'prot'));
237+
assertType('array{}', array_column($objects, 'priv'));
238+
}
239+
240+
/** @param array{ObjectWithVisibility} $objects */
241+
public function testNonPublicPropertiesConstant(array $objects): void
242+
{
243+
assertType('array{int}', array_column($objects, 'pub'));
244+
assertType('array{}', array_column($objects, 'prot'));
245+
assertType('array{}', array_column($objects, 'priv'));
246+
}
247+
248+
/** @param array<int, ObjectWithVisibility> $objects */
249+
public function testNonPublicAsIndex(array $objects): void
250+
{
251+
assertType('array<int, int>', array_column($objects, 'pub', 'pub'));
252+
assertType('array<int, int>', array_column($objects, 'pub', 'priv'));
253+
}
254+
255+
}
256+
257+
class ArrayColumnVisibilityFromInsideTest
258+
{
259+
260+
public int $pub = 1;
261+
private int $priv = 2;
262+
263+
/** @param list<self> $objects */
264+
public function testFromInside(array $objects): void
265+
{
266+
assertType('list<int>', array_column($objects, 'pub'));
267+
assertType('array{}', array_column($objects, 'priv'));
268+
}
269+
270+
}

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,53 @@ public function doFoo(array $a): void
232232
}
233233

234234
}
235+
236+
class ObjectWithVisibility
237+
{
238+
public int $pub = 1;
239+
protected int $prot = 2;
240+
private int $priv = 3;
241+
}
242+
243+
class ArrayColumnVisibilityTest
244+
{
245+
246+
/** @param array<int, ObjectWithVisibility> $objects */
247+
public function testNonPublicProperties(array $objects): void
248+
{
249+
assertType('list<int>', array_column($objects, 'pub'));
250+
assertType('array{}', array_column($objects, 'prot'));
251+
assertType('array{}', array_column($objects, 'priv'));
252+
}
253+
254+
/** @param array{ObjectWithVisibility} $objects */
255+
public function testNonPublicPropertiesConstant(array $objects): void
256+
{
257+
assertType('array{int}', array_column($objects, 'pub'));
258+
assertType('array{}', array_column($objects, 'prot'));
259+
assertType('array{}', array_column($objects, 'priv'));
260+
}
261+
262+
/** @param array<int, ObjectWithVisibility> $objects */
263+
public function testNonPublicAsIndex(array $objects): void
264+
{
265+
assertType('array<int, int>', array_column($objects, 'pub', 'pub'));
266+
assertType('array<int, int>', array_column($objects, 'pub', 'priv'));
267+
}
268+
269+
}
270+
271+
class ArrayColumnVisibilityFromInsideTest
272+
{
273+
274+
public int $pub = 1;
275+
private int $priv = 2;
276+
277+
/** @param list<self> $objects */
278+
public function testFromInside(array $objects): void
279+
{
280+
assertType('list<int>', array_column($objects, 'pub'));
281+
assertType('array{}', array_column($objects, 'priv'));
282+
}
283+
284+
}

0 commit comments

Comments
 (0)