Skip to content

Commit

Permalink
str_repeat() function return type extension
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jul 15, 2021
1 parent ce80724 commit 57d765f
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 1 deletion.
5 changes: 5 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,11 @@ services:
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension

-
class: PHPStan\Type\Php\StrRepeatFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension

-
class: PHPStan\Type\Php\ParseUrlFunctionDynamicReturnTypeExtension
tags:
Expand Down
54 changes: 54 additions & 0 deletions src/Type/Php/StrRepeatFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Php;

use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\StringType;

class StrRepeatFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
{

public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return $functionReflection->getName() === 'str_repeat';
}

public function getTypeFromFunctionCall(
FunctionReflection $functionReflection,
FuncCall $functionCall,
Scope $scope
): \PHPStan\Type\Type
{
$args = $functionCall->args;
if (count($args) < 2) {
return new StringType();
}

$inputType = $scope->getType($args[0]->value);
$multiplierType = $scope->getType($args[1]->value);

if ((new ConstantIntegerType(0))->isSuperTypeOf($multiplierType)->yes()) {
return new ConstantStringType('');
}

if ($inputType->isNonEmptyString()->yes()) {
if (IntegerRangeType::fromInterval(1, null)->isSuperTypeOf($multiplierType)->yes()) {
return new IntersectionType([
new StringType(),
new AccessoryNonEmptyStringType(),
]);
}
}

return new StringType();
}

}
9 changes: 8 additions & 1 deletion tests/PHPStan/Analyser/data/non-empty-string.php
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ class MoreNonEmptyStringFunctions
/**
* @param non-empty-string $nonEmpty
*/
public function doFoo(string $s, string $nonEmpty)
public function doFoo(string $s, string $nonEmpty, int $i)
{
assertType('string', strtoupper($s));
assertType('non-empty-string', strtoupper($nonEmpty));
Expand Down Expand Up @@ -309,6 +309,13 @@ public function doFoo(string $s, string $nonEmpty)
assertType('non-empty-string', str_pad($nonEmpty, 1));
assertType('string', str_pad($s, 0));
assertType('non-empty-string', str_pad($s, 1));

assertType('non-empty-string', str_repeat($nonEmpty, 1));
assertType('\'\'', str_repeat($nonEmpty, 0));
assertType('string', str_repeat($nonEmpty, $i));
assertType('\'\'', str_repeat($s, 0));
assertType('string', str_repeat($s, 1));
assertType('string', str_repeat($s, $i));
}

}

0 comments on commit 57d765f

Please sign in to comment.