diff --git a/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php b/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php index 986f299cd8..bd88f6bbc5 100644 --- a/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php +++ b/src/Type/Php/ArrayCountValuesDynamicReturnTypeExtension.php @@ -54,6 +54,12 @@ public function getTypeFromFunctionCall( continue; } + if (!$itemType->isString()->no() && !$itemType->isConstantScalarValue()->yes()) { + $itemType = $allowedValues; + } else { + $itemType = $itemType->toArrayKey(); + } + $outputTypes[] = new IntersectionType([ 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 0be5d86a1c..64af79934f 100644 --- a/tests/PHPStan/Analyser/nsrt/array-count-values.php +++ b/tests/PHPStan/Analyser/nsrt/array-count-values.php @@ -25,7 +25,7 @@ function returnsStringOrObjectArray(): array } // Objects are ignored by array_count_values, with a warning emitted. -assertType('non-empty-array>', array_count_values(returnsStringOrObjectArray())); +assertType('non-empty-array>', array_count_values(returnsStringOrObjectArray())); class StringableObject { @@ -50,3 +50,37 @@ public function __toString(): string $intAsString = array_count_values(['1', '2', '2', '3']); assertType("non-empty-array<1|2|3, int<1, max>>", $intAsString); + +/** + * @param array $strings + * @param array $ints + * @param array $positives + * @param array> $ranges + * @param array $stringOrInts + * @param array $stringsOrBool + * @param array $intOrConstNumericString + * @param array $intOrConstNonNumericString + */ +function strings( + array $strings, + array $ints, + array $positives, + array $ranges, + array $stringOrInts, + array $stringsOrBool, + array $intOrConstNumericString, + array $intOrConstNonNumericString, +): void +{ + // numeric-strings in array-keys are auto-casted to int, see https://3v4l.org/VQoSJQ#vnull + assertType("non-empty-array>", array_count_values($strings)); + + assertType("non-empty-array>", array_count_values($ints)); + assertType("non-empty-array, int<1, max>>", array_count_values($positives)); + assertType("non-empty-array, int<1, max>>", array_count_values($ranges)); + + assertType("non-empty-array>", array_count_values($stringOrInts)); + assertType("non-empty-array>", array_count_values($stringsOrBool)); + assertType("non-empty-array>", array_count_values($intOrConstNumericString)); // could have only "int" key + assertType("non-empty-array>", array_count_values($intOrConstNonNumericString)); +}