Skip to content

Commit 2f991d1

Browse files
authored
StrlenFunctionReturnTypeExtension: cover more scalar types
1 parent d5b2c88 commit 2f991d1

File tree

3 files changed

+44
-3
lines changed

3 files changed

+44
-3
lines changed

src/Type/Php/StrlenFunctionReturnTypeExtension.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Reflection\FunctionReflection;
88
use PHPStan\Reflection\ParametersAcceptorSelector;
9+
use PHPStan\Type\BooleanType;
910
use PHPStan\Type\Constant\ConstantIntegerType;
1011
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
1112
use PHPStan\Type\IntegerRangeType;
13+
use PHPStan\Type\IntegerType;
14+
use PHPStan\Type\TypeUtils;
1215

1316
class StrlenFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
1417
{
@@ -30,8 +33,41 @@ public function getTypeFromFunctionCall(
3033
}
3134

3235
$argType = $scope->getType($args[0]->value);
36+
37+
$constantStrings = TypeUtils::getConstantStrings($argType);
38+
$min = null;
39+
$max = null;
40+
foreach ($constantStrings as $constantString) {
41+
$len = strlen($constantString->getValue());
42+
43+
if ($min === null) {
44+
$min = $len;
45+
$max = $len;
46+
}
47+
48+
if ($len < $min) {
49+
$min = $len;
50+
}
51+
if ($len <= $max) {
52+
continue;
53+
}
54+
55+
$max = $len;
56+
}
57+
58+
// $max is always != null, when $min is != null
59+
if ($min !== null) {
60+
return IntegerRangeType::fromInterval($min, $max);
61+
}
62+
63+
$bool = new BooleanType();
64+
if ($bool->isSuperTypeOf($argType)->yes()) {
65+
return IntegerRangeType::fromInterval(0, 1);
66+
}
67+
3368
$isNonEmpty = $argType->isNonEmptyString();
34-
if ($isNonEmpty->yes()) {
69+
$integer = new IntegerType();
70+
if ($isNonEmpty->yes() || $integer->isSuperTypeOf($argType)->yes()) {
3571
return IntegerRangeType::fromInterval(1, null);
3672
}
3773

tests/PHPStan/Analyser/data/bug-5129.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public function sayHello(string $s): void
2020
assertType('0', strlen($this->foo));
2121

2222
$this->foo = 'x';
23-
assertType('int<1, max>', strlen($this->foo));
23+
assertType('1', strlen($this->foo));
2424
if (strlen($this->foo) > 0) {
2525
return;
2626
}

tests/PHPStan/Analyser/data/non-empty-string.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,9 @@ class MoreNonEmptyStringFunctions
302302

303303
/**
304304
* @param non-empty-string $nonEmpty
305+
* @param '1'|'2'|'5'|'10' $constUnion
305306
*/
306-
public function doFoo(string $s, string $nonEmpty, int $i)
307+
public function doFoo(string $s, string $nonEmpty, int $i, bool $bool, $constUnion)
307308
{
308309
assertType('string', addslashes($s));
309310
assertType('non-empty-string', addslashes($nonEmpty));
@@ -349,8 +350,12 @@ public function doFoo(string $s, string $nonEmpty, int $i)
349350
assertType('non-empty-string', vsprintf($nonEmpty, []));
350351

351352
assertType('0', strlen(''));
353+
assertType('5', strlen('hallo'));
354+
assertType('int<0, 1>', strlen($bool));
355+
assertType('int<1, max>', strlen($i));
352356
assertType('int<0, max>', strlen($s));
353357
assertType('int<1, max>', strlen($nonEmpty));
358+
assertType('int<1, 2>', strlen($constUnion));
354359

355360
assertType('non-empty-string', str_pad($nonEmpty, 0));
356361
assertType('non-empty-string', str_pad($nonEmpty, 1));

0 commit comments

Comments
 (0)