From 8aa9b789607e04658acc4a8f7a57314a207d3612 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sun, 7 Dec 2025 15:07:12 +0100 Subject: [PATCH 1/2] Fix ArrayCountValues --- ...rrayCountValuesDynamicReturnTypeExtension.php | 16 ++++++---------- .../PHPStan/Analyser/nsrt/array-count-values.php | 5 +++++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php b/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php index 2cc1df4870..b1ff88db8d 100644 --- a/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php +++ b/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php @@ -10,8 +10,10 @@ use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; -use PHPStan\Type\ErrorType; use PHPStan\Type\IntegerRangeType; +use PHPStan\Type\IntegerType; +use PHPStan\Type\NeverType; +use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; @@ -43,17 +45,11 @@ public function getTypeFromFunctionCall( $arrayTypes = $inputType->getArrays(); $outputTypes = []; + $allowedValues = new UnionType([new IntegerType(), new StringType()]); foreach ($arrayTypes as $arrayType) { - $itemType = $arrayType->getItemType(); - - if ($itemType instanceof UnionType) { - $itemType = $itemType->filterTypes( - static fn ($type) => !$type->toArrayKey() instanceof ErrorType, - ); - } - - if ($itemType->toArrayKey() instanceof ErrorType) { + $itemType = TypeCombinator::intersect($arrayType->getItemType(), $allowedValues); + if ($itemType instanceof NeverType) { continue; } diff --git a/tests/PHPStan/Analyser/nsrt/array-count-values.php b/tests/PHPStan/Analyser/nsrt/array-count-values.php index f752bac1ad..5e6b61abbd 100644 --- a/tests/PHPStan/Analyser/nsrt/array-count-values.php +++ b/tests/PHPStan/Analyser/nsrt/array-count-values.php @@ -41,3 +41,8 @@ public function __toString(): string $stringable = array_count_values([new StringableObject(), 'string', 1]); assertType('non-empty-array<1|\'string\', int<1, max>>', $stringable); + +// Booleans, floats and null are ignored by array_count_values even if they can be cast to array key. +$scalar = array_count_values([true, 1.0, false, 0.0, null]); + +assertType('array{}', $scalar); From 793506dbb8acd5ce37a018b643d2532a8dbeb3ab Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sun, 7 Dec 2025 15:16:56 +0100 Subject: [PATCH 2/2] Fix --- src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php | 2 +- tests/PHPStan/Analyser/nsrt/array-count-values.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php b/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php index b1ff88db8d..b12ddaad80 100644 --- a/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php +++ b/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php @@ -54,7 +54,7 @@ public function getTypeFromFunctionCall( } $outputTypes[] = TypeCombinator::intersect( - new ArrayType($itemType, IntegerRangeType::fromInterval(1, null)), + new ArrayType($itemType->toArrayKey(), IntegerRangeType::fromInterval(1, null)), new NonEmptyArrayType(), ); } diff --git a/tests/PHPStan/Analyser/nsrt/array-count-values.php b/tests/PHPStan/Analyser/nsrt/array-count-values.php index 5e6b61abbd..0be5d86a1c 100644 --- a/tests/PHPStan/Analyser/nsrt/array-count-values.php +++ b/tests/PHPStan/Analyser/nsrt/array-count-values.php @@ -46,3 +46,7 @@ public function __toString(): string $scalar = array_count_values([true, 1.0, false, 0.0, null]); assertType('array{}', $scalar); + +$intAsString = array_count_values(['1', '2', '2', '3']); + +assertType("non-empty-array<1|2|3, int<1, max>>", $intAsString);