Skip to content

Commit 8fda6d3

Browse files
Add dateIntervalExtension (#142)
1 parent c590c05 commit 8fda6d3

File tree

4 files changed

+83
-0
lines changed

4 files changed

+83
-0
lines changed

extension.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ services:
5757
tags:
5858
- exceptionRules.dynamicConstructorThrowTypeExtension
5959

60+
-
61+
class: Pepakriz\PHPStanExceptionRules\Extension\DateIntervalExtension
62+
tags:
63+
- exceptionRules.dynamicConstructorThrowTypeExtension
64+
6065
-
6166
class: Pepakriz\PHPStanExceptionRules\Extension\SplFileObjectExtension
6267
tags:
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Pepakriz\PHPStanExceptionRules\Extension;
4+
5+
use DateInterval;
6+
use Exception;
7+
use Pepakriz\PHPStanExceptionRules\DynamicConstructorThrowTypeExtension;
8+
use Pepakriz\PHPStanExceptionRules\UnsupportedClassException;
9+
use PhpParser\Node\Arg;
10+
use PhpParser\Node\Expr\New_;
11+
use PHPStan\Analyser\Scope;
12+
use PHPStan\Reflection\MethodReflection;
13+
use PHPStan\Type\NeverType;
14+
use PHPStan\Type\ObjectType;
15+
use PHPStan\Type\Type;
16+
use PHPStan\Type\TypeCombinator;
17+
use PHPStan\Type\TypeUtils;
18+
use PHPStan\Type\VoidType;
19+
use function is_a;
20+
21+
class DateIntervalExtension implements DynamicConstructorThrowTypeExtension
22+
{
23+
24+
/**
25+
* @throws UnsupportedClassException
26+
*/
27+
public function getThrowTypeFromConstructor(MethodReflection $methodReflection, New_ $newNode, Scope $scope): Type
28+
{
29+
if (is_a($methodReflection->getDeclaringClass()->getName(), DateInterval::class, true)) {
30+
return $this->resolveThrowType($newNode->args, $scope);
31+
}
32+
33+
throw new UnsupportedClassException();
34+
}
35+
36+
/**
37+
* @param Arg[] $args
38+
*/
39+
private function resolveThrowType(array $args, Scope $scope): Type
40+
{
41+
$exceptionType = new ObjectType(Exception::class);
42+
if (!isset($args[0])) {
43+
return $exceptionType;
44+
}
45+
46+
$valueType = $scope->getType($args[0]->value);
47+
foreach (TypeUtils::getConstantStrings($valueType) as $constantString) {
48+
try {
49+
new DateInterval($constantString->getValue());
50+
} catch (Exception $e) {
51+
return $exceptionType;
52+
}
53+
54+
$valueType = TypeCombinator::remove($valueType, $constantString);
55+
}
56+
57+
if (!$valueType instanceof NeverType) {
58+
return $exceptionType;
59+
}
60+
61+
return new VoidType();
62+
}
63+
64+
}

tests/src/Rules/PhpInternalsTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Pepakriz\PHPStanExceptionRules\CheckedExceptionService;
66
use Pepakriz\PHPStanExceptionRules\DefaultThrowTypeService;
77
use Pepakriz\PHPStanExceptionRules\DynamicThrowTypeService;
8+
use Pepakriz\PHPStanExceptionRules\Extension\DateIntervalExtension;
89
use Pepakriz\PHPStanExceptionRules\Extension\DateTimeExtension;
910
use Pepakriz\PHPStanExceptionRules\Extension\DOMDocumentExtension;
1011
use Pepakriz\PHPStanExceptionRules\Extension\IntdivExtension;
@@ -24,6 +25,7 @@ class PhpInternalsTest extends RuleTestCase
2425
protected function getRule(): Rule
2526
{
2627
$reflectionClassExtension = new ReflectionExtension($this->createBroker());
28+
$dateIntervalExtension = new DateIntervalExtension();
2729
$dateTimeExtension = new DateTimeExtension();
2830
$splFileObjectExtension = new SplFileObjectExtension();
2931
$jsonEncodeDecodeExtension = new JsonEncodeDecodeExtension();
@@ -43,6 +45,7 @@ protected function getRule(): Rule
4345
[],
4446
[
4547
$reflectionClassExtension,
48+
$dateIntervalExtension,
4649
$dateTimeExtension,
4750
$splFileObjectExtension,
4851
],

tests/src/Rules/data/throws-php-internal-functions.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Pepakriz\PHPStanExceptionRules\Rules\PhpInternalFunctions;
44

5+
use DateInterval;
56
use DateTime;
67
use DateTimeImmutable;
78
use DOMDocument;
@@ -105,6 +106,16 @@ public function testDateTime(): void
105106
new DateTime(rand(0, 1) === 0 ? '2018-01-01' : 123); // error: Missing @throws Exception annotation
106107
}
107108

109+
/**
110+
* @requires PHP 7.3
111+
*/
112+
public function testDateInterval(): void
113+
{
114+
new DateInterval(null); // error: Missing @throws Exception annotation
115+
new DateInterval('P10D');
116+
new DateInterval('invalid format'); // error: Missing @throws Exception annotation
117+
}
118+
108119
public function testSplFileObject(): void
109120
{
110121
new SplFileObject(); // error: Missing @throws LogicException annotation; Missing @throws RuntimeException annotation

0 commit comments

Comments
 (0)