Skip to content

Commit 2e89a22

Browse files
committed
Fix array_filter that does not return all items
1 parent ec92402 commit 2e89a22

File tree

3 files changed

+17
-3
lines changed

3 files changed

+17
-3
lines changed

src/Type/Php/ArrayFilterFunctionReturnTypeReturnTypeExtension.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use PHPStan\Type\BenevolentUnionType;
2222
use PHPStan\Type\Constant\ConstantArrayType;
2323
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
24+
use PHPStan\Type\Constant\ConstantBooleanType;
2425
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
2526
use PHPStan\Type\MixedType;
2627
use PHPStan\Type\NeverType;
@@ -170,12 +171,12 @@ private function filterByTruthyValue(Scope $scope, Error|Variable|null $itemVar,
170171
$builder = ConstantArrayTypeBuilder::createEmpty();
171172
foreach ($constantArray->getKeyTypes() as $i => $keyType) {
172173
$itemType = $constantArray->getValueTypes()[$i];
173-
[$newKeyType, $newItemType] = $this->processKeyAndItemType($scope, $keyType, $itemType, $itemVar, $keyVar, $expr);
174+
[$newKeyType, $newItemType, $optional] = $this->processKeyAndItemType($scope, $keyType, $itemType, $itemVar, $keyVar, $expr);
174175
if ($newKeyType instanceof NeverType || $newItemType instanceof NeverType) {
175176
continue;
176177
}
177178
if ($itemType->equals($newItemType) && $keyType->equals($newKeyType)) {
178-
$builder->setOffsetValueType($keyType, $itemType);
179+
$builder->setOffsetValueType($keyType, $itemType, $optional);
179180
continue;
180181
}
181182

@@ -198,7 +199,7 @@ private function filterByTruthyValue(Scope $scope, Error|Variable|null $itemVar,
198199
}
199200

200201
/**
201-
* @return array{Type, Type}
202+
* @return array{Type, Type, bool}
202203
*/
203204
private function processKeyAndItemType(MutatingScope $scope, Type $keyType, Type $itemType, Error|Variable|null $itemVar, Error|Variable|null $keyVar, Expr $expr): array
204205
{
@@ -220,11 +221,13 @@ private function processKeyAndItemType(MutatingScope $scope, Type $keyType, Type
220221
$scope = $scope->assignVariable($keyVarName, $keyType);
221222
}
222223

224+
$booleanResult = $scope->getType($expr)->toBoolean();
223225
$scope = $scope->filterByTruthyValue($expr);
224226

225227
return [
226228
$keyVarName !== null ? $scope->getVariableType($keyVarName) : $keyType,
227229
$itemVarName !== null ? $scope->getVariableType($itemVarName) : $itemType,
230+
!$booleanResult instanceof ConstantBooleanType,
228231
];
229232
}
230233

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,7 @@ public function dataFileAsserts(): iterable
10071007
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6170.php');
10081008
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/slevomat-foreach-array-key-exists-bug.php');
10091009
yield from $this->gatherAssertTypes(__DIR__ . '/data/array-key-exists.php');
1010+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7909.php');
10101011
}
10111012

10121013
/**
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Bug7909;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function (): void {
8+
$filenames = array_filter(['file1', 'file2'], 'file_exists');
9+
assertType("array{0?: 'file1', 1?: 'file2'}", $filenames);
10+
};

0 commit comments

Comments
 (0)