Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit fc35414

Browse files
vranaondrejmirtes
authored andcommitted
Improve idate() return types
1 parent 330ca96 commit fc35414

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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\Constant\ConstantBooleanType;
10+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
11+
use PHPStan\Type\Type;
12+
13+
#[AutowiredService]
14+
final class IdateFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
15+
{
16+
17+
public function __construct(private IdateFunctionReturnTypeHelper $idateFunctionReturnTypeHelper)
18+
{
19+
}
20+
21+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
22+
{
23+
return $functionReflection->getName() === 'idate';
24+
}
25+
26+
public function getTypeFromFunctionCall(
27+
FunctionReflection $functionReflection,
28+
FuncCall $functionCall,
29+
Scope $scope,
30+
): ?Type
31+
{
32+
$args = $functionCall->getArgs();
33+
if ($args === []) {
34+
return new ConstantBooleanType(false);
35+
}
36+
37+
return $this->idateFunctionReturnTypeHelper->getTypeFromFormatType(
38+
$scope->getType($args[0]->value),
39+
);
40+
}
41+
42+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PHPStan\DependencyInjection\AutowiredService;
6+
use PHPStan\Type\Constant\ConstantBooleanType;
7+
use PHPStan\Type\IntegerRangeType;
8+
use PHPStan\Type\IntegerType;
9+
use PHPStan\Type\Type;
10+
use PHPStan\Type\TypeCombinator;
11+
12+
#[AutowiredService]
13+
final class IdateFunctionReturnTypeHelper
14+
{
15+
16+
public function getTypeFromFormatType(Type $formatType): ?Type
17+
{
18+
$types = [];
19+
foreach ($formatType->getConstantStrings() as $formatString) {
20+
$types[] = $this->buildReturnTypeFromFormat($formatString->getValue());
21+
}
22+
23+
if ($types === []) {
24+
return null;
25+
}
26+
27+
return TypeCombinator::union(...$types);
28+
}
29+
30+
public function buildReturnTypeFromFormat(string $formatString): Type
31+
{
32+
// see https://www.php.net/idate
33+
switch ($formatString) {
34+
case 'd':
35+
return IntegerRangeType::fromInterval(1, 31);
36+
case 'h':
37+
return IntegerRangeType::fromInterval(1, 12);
38+
case 'H':
39+
return IntegerRangeType::fromInterval(0, 23);
40+
case 'i':
41+
return IntegerRangeType::fromInterval(0, 59);
42+
case 'I':
43+
return IntegerRangeType::fromInterval(0, 1);
44+
case 'L':
45+
return IntegerRangeType::fromInterval(0, 1);
46+
case 'm':
47+
return IntegerRangeType::fromInterval(1, 12);
48+
case 'N':
49+
return IntegerRangeType::fromInterval(1, 7);
50+
case 's':
51+
return IntegerRangeType::fromInterval(0, 59);
52+
case 't':
53+
return IntegerRangeType::fromInterval(28, 31);
54+
case 'w':
55+
return IntegerRangeType::fromInterval(0, 6);
56+
case 'W':
57+
return IntegerRangeType::fromInterval(1, 53);
58+
case 'y':
59+
return IntegerRangeType::fromInterval(0, 99);
60+
case 'z':
61+
return IntegerRangeType::fromInterval(0, 365);
62+
case 'B':
63+
case 'o':
64+
case 'U':
65+
case 'Y':
66+
case 'Z':
67+
return new IntegerType();
68+
default:
69+
return new ConstantBooleanType(false);
70+
}
71+
}
72+
73+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace PHPStan\Analyser\nsrt;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
10+
/**
11+
* @param 'h'|'H' $hour
12+
* @param 'm'|string $format
13+
*/
14+
public function doFoo(string $string, string $hour, string $format): void
15+
{
16+
assertType('int<1, 7>', idate('N'));
17+
assertType('int', idate('Y'));
18+
assertType('false', idate('wrong'));
19+
assertType('false', idate(''));
20+
assertType('int|false', idate($string));
21+
assertType('int<0, 23>', idate($hour));
22+
assertType('int|false', idate($format));
23+
}
24+
25+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /