Skip to content

Commit fc11ffa

Browse files
committed
Add ArrayCountValuesDynamicReturnTypeExtension.
1 parent 19dfc5e commit fc11ffa

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\DependencyInjection\AutowiredService;
8+
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\Type\ArrayType;
10+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
11+
use PHPStan\Type\ErrorType;
12+
use PHPStan\Type\IntegerRangeType;
13+
use PHPStan\Type\Type;
14+
use PHPStan\Type\TypeCombinator;
15+
16+
#[AutowiredService]
17+
final class ArrayCountValuesDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
18+
{
19+
20+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
21+
{
22+
return $functionReflection->getName() === 'array_count_values';
23+
}
24+
25+
public function getTypeFromFunctionCall(
26+
FunctionReflection $functionReflection,
27+
FuncCall $functionCall,
28+
Scope $scope,
29+
): ?Type
30+
{
31+
$args = $functionCall->getArgs();
32+
33+
if (!isset($args[0])) {
34+
return null;
35+
}
36+
37+
$inputType = $scope->getType($args[0]->value);
38+
39+
$arrayTypes = $inputType->getArrays();
40+
41+
$outputTypes = [];
42+
43+
foreach ($arrayTypes as $arrayType) {
44+
$itemType = $arrayType->getItemType();
45+
46+
if ($itemType->toArrayKey() instanceof ErrorType) {
47+
return new ErrorType();
48+
}
49+
50+
$outputTypes[] = new ArrayType(
51+
$itemType,
52+
IntegerRangeType::fromInterval(0, null),
53+
);
54+
}
55+
56+
return TypeCombinator::union(...$outputTypes);
57+
}
58+
59+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace ArrayCountValues;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
$ints = array_count_values([1, 2, 2, 3]);
8+
9+
assertType('array<1|2|3, int<0, max>>', $ints);
10+
11+
$strings = array_count_values(['one', 'two', 'two', 'three']);
12+
13+
assertType('array<\'one\'|\'three\'|\'two\', int<0, max>>', $strings);
14+
15+
$strings = array_count_values([new \stdClass()]);
16+
17+
assertType('*ERROR*', $strings);
18+
19+
/**
20+
* @return array<int, string|object>
21+
*/
22+
function returnsStringOrObjectArray(): array
23+
{
24+
25+
}
26+
27+
assertType('*ERROR*', array_count_values(returnsStringOrObjectArray()));

0 commit comments

Comments
 (0)