Skip to content

Commit 97efd4d

Browse files
fix returntype for DateIntervall::createFromDateString
1 parent 8c2be72 commit 97efd4d

File tree

5 files changed

+109
-1
lines changed

5 files changed

+109
-1
lines changed

conf/config.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,11 @@ services:
13281328
tags:
13291329
- phpstan.dynamicStaticMethodThrowTypeExtension
13301330

1331+
-
1332+
class: PHPStan\Type\Php\DateIntervalDynamicReturnTypeExtension
1333+
tags:
1334+
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
1335+
13311336
-
13321337
class: PHPStan\Type\Php\DateTimeCreateDynamicReturnTypeExtension
13331338
tags:

resources/functionMap.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1583,7 +1583,7 @@
15831583
'DateInterval::__construct' => ['void', 'spec'=>'string'],
15841584
'DateInterval::__set_state' => ['DateInterval', 'array'=>'array'],
15851585
'DateInterval::__wakeup' => ['void'],
1586-
'DateInterval::createFromDateString' => ['DateInterval', 'time'=>'string'],
1586+
'DateInterval::createFromDateString' => ['DateInterval|false', 'time'=>'string'],
15871587
'DateInterval::format' => ['string', 'format'=>'string'],
15881588
'DatePeriod::__construct' => ['void', 'start'=>'DateTimeInterface', 'interval'=>'DateInterval', 'recur'=>'int', 'options='=>'int'],
15891589
'DatePeriod::__construct\'1' => ['void', 'start'=>'DateTimeInterface', 'interval'=>'DateInterval', 'end'=>'DateTimeInterface', 'options='=>'int'],
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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\Reflection\MethodReflection;
9+
use PHPStan\Type\Constant\ConstantBooleanType;
10+
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
11+
use PHPStan\Type\ObjectType;
12+
use PHPStan\Type\Type;
13+
use function count;
14+
use function in_array;
15+
16+
class DateIntervalDynamicReturnTypeExtension
17+
implements DynamicStaticMethodReturnTypeExtension
18+
{
19+
20+
public function getClass(): string
21+
{
22+
return DateInterval::class;
23+
}
24+
25+
public function isStaticMethodSupported(MethodReflection $methodReflection): bool
26+
{
27+
return $methodReflection->getName() === 'createFromDateString';
28+
}
29+
30+
public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): ?Type
31+
{
32+
$arguments = $methodCall->getArgs();
33+
34+
if (!isset($arguments[0])) {
35+
return null;
36+
}
37+
38+
$strings = $scope->getType($arguments[0]->value)->getConstantStrings();
39+
40+
$possibleReturnTypes = [];
41+
foreach ($strings as $string) {
42+
$possibleReturnTypes[] = @DateInterval::createFromDateString($string->getValue()) instanceof DateInterval ? DateInterval::class : false;
43+
}
44+
45+
// the error case, when wrong types are passed
46+
if (count($possibleReturnTypes) === 0) {
47+
return null;
48+
}
49+
50+
if (in_array(false, $possibleReturnTypes, true) && in_array(DateInterval::class, $possibleReturnTypes, true)) {
51+
return null;
52+
}
53+
54+
if (in_array(false, $possibleReturnTypes, true)) {
55+
return new ConstantBooleanType(false);
56+
}
57+
58+
return new ObjectType(DateInterval::class);
59+
}
60+
61+
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,7 @@ public function dataFileAsserts(): iterable
11351135
yield from $this->gatherAssertTypes(__DIR__ . '/data/imagick-pixel.php');
11361136
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-8467a.php');
11371137
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8467b.php');
1138+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8442.php');
11381139
yield from $this->gatherAssertTypes(__DIR__ . '/data/PDOStatement.php');
11391140
yield from $this->gatherAssertTypes(__DIR__ . '/data/discussion-8447.php');
11401141
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7805.php');
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug8442;
4+
5+
use stdClass;
6+
use function PHPStan\Testing\assertType;
7+
use DateInterval;
8+
9+
function () {
10+
assertType('false', DateInterval::createFromDateString('foo'));
11+
assertType('DateInterval', DateInterval::createFromDateString('1 Day'));
12+
13+
if (rand(0,1)) {
14+
$interval = '1 day';
15+
} else {
16+
$interval = '2 day';
17+
}
18+
19+
assertType('DateInterval', DateInterval::createFromDateString($interval));
20+
21+
if (rand(0,1)) {
22+
$interval = 'foo';
23+
} else {
24+
$interval = '2 day';
25+
}
26+
27+
assertType('DateInterval|false', DateInterval::createFromDateString($interval));
28+
29+
if (rand(0,1)) {
30+
$interval = 'foo';
31+
} else {
32+
$interval = 'foo';
33+
}
34+
35+
assertType('false', DateInterval::createFromDateString($interval));
36+
37+
assertType('DateInterval|false',DateInterval::createFromDateString(str_shuffle('1 day')));
38+
assertType('DateInterval|false',DateInterval::createFromDateString());
39+
assertType('DateInterval|false',DateInterval::createFromDateString(new stdClass()));
40+
};
41+

0 commit comments

Comments
 (0)