Skip to content

Commit 014a349

Browse files
committed
Update DateIntervalDynamicReturnTypeExtension to handle 8.3 exceptions
1 parent 7d17216 commit 014a349

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use DateInterval;
6+
use PhpParser\Node\Expr\StaticCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\DependencyInjection\AutowiredService;
9+
use PHPStan\Php\PhpVersion;
10+
use PHPStan\Reflection\MethodReflection;
11+
use PHPStan\Type\DynamicStaticMethodThrowTypeExtension;
12+
use PHPStan\Type\NeverType;
13+
use PHPStan\Type\Type;
14+
use PHPStan\Type\TypeCombinator;
15+
use function count;
16+
17+
#[AutowiredService]
18+
final class DateIntervalCreateFromDateStringMethodThrowTypeExtension implements DynamicStaticMethodThrowTypeExtension
19+
{
20+
21+
public function __construct(private PhpVersion $phpVersion)
22+
{
23+
}
24+
25+
public function isStaticMethodSupported(MethodReflection $methodReflection): bool
26+
{
27+
return $methodReflection->getName() === 'createFromDateString' && $methodReflection->getDeclaringClass()->getName() === DateInterval::class;
28+
}
29+
30+
public function getThrowTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): ?Type
31+
{
32+
if (count($methodCall->getArgs()) === 0) {
33+
return null;
34+
}
35+
36+
if (!$this->phpVersion->hasDateTimeExceptions()) {
37+
return null;
38+
}
39+
40+
$valueType = $scope->getType($methodCall->getArgs()[0]->value);
41+
$constantStrings = $valueType->getConstantStrings();
42+
43+
foreach ($constantStrings as $constantString) {
44+
try {
45+
DateInterval::createFromDateString($constantString->getValue());
46+
} catch (\Exception) { // phpcs:ignore
47+
return $methodReflection->getThrowType();
48+
}
49+
50+
$valueType = TypeCombinator::remove($valueType, $constantString);
51+
}
52+
53+
if (!$valueType instanceof NeverType) {
54+
return $methodReflection->getThrowType();
55+
}
56+
57+
return null;
58+
}
59+
60+
}

src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
use PhpParser\Node\Expr\StaticCall;
77
use PHPStan\Analyser\Scope;
88
use PHPStan\DependencyInjection\AutowiredService;
9+
use PHPStan\Php\PhpVersion;
910
use PHPStan\Reflection\MethodReflection;
1011
use PHPStan\Type\Constant\ConstantBooleanType;
1112
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
13+
use PHPStan\Type\NeverType;
1214
use PHPStan\Type\ObjectType;
1315
use PHPStan\Type\Type;
1416
use Throwable;
@@ -20,6 +22,10 @@ final class DateIntervalDynamicReturnTypeExtension
2022
implements DynamicStaticMethodReturnTypeExtension
2123
{
2224

25+
public function __construct(private PhpVersion $phpVersion)
26+
{
27+
}
28+
2329
public function getClass(): string
2430
{
2531
return DateInterval::class;
@@ -44,10 +50,17 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
4450
foreach ($strings as $string) {
4551
try {
4652
$result = @DateInterval::createFromDateString($string->getValue());
53+
if ($this->phpVersion->hasDateTimeExceptions()) {
54+
return new ObjectType(DateInterval::class);
55+
}
4756
} catch (Throwable) {
57+
if ($this->phpVersion->hasDateTimeExceptions()) {
58+
return new NeverType();
59+
}
4860
$possibleReturnTypes[] = false;
4961
continue;
5062
}
63+
// @phpstan-ignore instanceof.alwaysTrue (should only run for < 8.3 and then statement isn't true)
5164
$possibleReturnTypes[] = $result instanceof DateInterval ? DateInterval::class : false;
5265
}
5366

0 commit comments

Comments
 (0)