Skip to content

Commit c92f528

Browse files
committed
count(non-empty-array, COUNT_RECURSIVE) is int<1, max>
1 parent d1f76fc commit c92f528

File tree

2 files changed

+84
-4
lines changed

2 files changed

+84
-4
lines changed

src/Type/Php/CountFunctionReturnTypeExtension.php

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\AutowiredService;
88
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\TrinaryLogic;
910
use PHPStan\Type\Constant\ConstantIntegerType;
1011
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
12+
use PHPStan\Type\IntegerRangeType;
1113
use PHPStan\Type\Type;
1214
use function count;
1315
use function in_array;
@@ -32,14 +34,26 @@ public function getTypeFromFunctionCall(
3234
return null;
3335
}
3436

35-
if (count($functionCall->getArgs()) > 1) {
36-
$mode = $scope->getType($functionCall->getArgs()[1]->value);
37-
if ($mode->isSuperTypeOf(new ConstantIntegerType(COUNT_RECURSIVE))->yes()) {
38-
return null;
37+
$arrayType = $scope->getType($functionCall->getArgs()[0]->value);
38+
if (!$this->isNormalCount($functionCall, $arrayType, $scope)->yes()) {
39+
if ($arrayType->isIterableAtLeastOnce()->yes()) {
40+
return IntegerRangeType::fromInterval(1, null);
3941
}
42+
return null;
4043
}
4144

4245
return $scope->getType($functionCall->getArgs()[0]->value)->getArraySize();
4346
}
4447

48+
private function isNormalCount(FuncCall $countFuncCall, Type $countedType, Scope $scope,): TrinaryLogic
49+
{
50+
if (count($countFuncCall->getArgs()) === 1) {
51+
$isNormalCount = TrinaryLogic::createYes();
52+
} else {
53+
$mode = $scope->getType($countFuncCall->getArgs()[1]->value);
54+
$isNormalCount = (new ConstantIntegerType(COUNT_NORMAL))->isSuperTypeOf($mode)->result->or($countedType->getIterableValueType()->isArray()->negate());
55+
}
56+
return $isNormalCount;
57+
}
58+
4559
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
use function PHPStan\Testing\assertType;
4+
5+
class HelloWorld
6+
{
7+
public function countArray(array $arr): void
8+
{
9+
if (count($arr) > 2) {
10+
assertType('non-empty-array', $arr);
11+
assertType('int<3, max>', count($arr));
12+
assertType('int<1, max>', count($arr, COUNT_NORMAL)); // could be int<3, max>
13+
assertType('int<1, max>', count($arr, COUNT_RECURSIVE));
14+
}
15+
}
16+
17+
public function countArrayNormal(array $arr): void
18+
{
19+
if (count($arr, COUNT_NORMAL) > 2) {
20+
assertType('non-empty-array', $arr);
21+
assertType('int<1, max>', count($arr)); // could be int<3, max>
22+
assertType('int<3, max>', count($arr, COUNT_NORMAL));
23+
assertType('int<1, max>', count($arr, COUNT_RECURSIVE));
24+
}
25+
}
26+
27+
public function countArrayRecursive(array $arr): void
28+
{
29+
if (count($arr, COUNT_RECURSIVE) > 2) {
30+
assertType('non-empty-array', $arr);
31+
assertType('int<1, max>', count($arr));
32+
assertType('int<1, max>', count($arr, COUNT_NORMAL));
33+
assertType('int<3, max>', count($arr, COUNT_RECURSIVE));
34+
}
35+
}
36+
37+
/** @param list<int> $list */
38+
public function countList($list): void
39+
{
40+
if (count($list) > 2) {
41+
assertType('int<3, max>', count($list));
42+
assertType('int<1, max>', count($list, COUNT_NORMAL));
43+
assertType('int<1, max>', count($list, COUNT_RECURSIVE));
44+
}
45+
}
46+
47+
/** @param list<int> $list */
48+
public function countListNormal($list): void
49+
{
50+
if (count($list, COUNT_NORMAL) > 2) {
51+
assertType('int<1, max>', count($list));
52+
assertType('int<3, max>', count($list, COUNT_NORMAL));
53+
assertType('int<1, max>', count($list, COUNT_RECURSIVE));
54+
}
55+
}
56+
57+
/** @param list<int> $list */
58+
public function countListRecursive($list): void
59+
{
60+
if (count($list, COUNT_RECURSIVE) > 2) {
61+
assertType('int<1, max>', count($list));
62+
assertType('int<1, max>', count($list, COUNT_NORMAL));
63+
assertType('int<3, max>', count($list, COUNT_RECURSIVE));
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)