Skip to content

Commit

Permalink
Add DatePeriod::getEndDate dynamic return type
Browse files Browse the repository at this point in the history
  • Loading branch information
Alban-io committed Nov 9, 2021
1 parent 9488d34 commit eb401c8
Show file tree
Hide file tree
Showing 5 changed files with 76 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 @@ -1051,6 +1051,11 @@ services:
tags:
- phpstan.dynamicStaticMethodThrowTypeExtension

-
class: PHPStan\Type\Php\DatePeriodGetEndDateMethodReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension

-
class: PHPStan\Type\Php\DsMapDynamicReturnTypeExtension
tags:
Expand Down
2 changes: 1 addition & 1 deletion resources/functionMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -1591,7 +1591,7 @@
'DatePeriod::__construct\'2' => ['void', 'iso'=>'string', 'options='=>'int'],
'DatePeriod::__wakeup' => ['void'],
'DatePeriod::getDateInterval' => ['DateInterval'],
'DatePeriod::getEndDate' => ['DateTimeInterface'],
'DatePeriod::getEndDate' => ['?DateTimeInterface'],
'DatePeriod::getStartDate' => ['DateTimeInterface'],
'DateTime::__construct' => ['void', 'time='=>'string', 'timezone='=>'?DateTimeZone'],
'DateTime::__set_state' => ['static', 'array'=>'array'],
Expand Down
56 changes: 56 additions & 0 deletions src/Type/Php/DatePeriodGetEndDateMethodReturnTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Php;

use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\IntegerType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;

class DatePeriodGetEndDateMethodReturnTypeExtension implements DynamicMethodReturnTypeExtension
{

public function getClass(): string
{
return \DatePeriod::class;
}

public function isMethodSupported(MethodReflection $methodReflection): bool
{
return $methodReflection->getName() === 'getEndDate';
}

public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
{
/** @var New_ $new */
$new = $methodCall->var;
$argument = $new->getRawArgs()[2] ?? null;

// initialize with iso parameter
if ($argument === null) {
return new NullType();
}

$dateTimeType = new ObjectType(\DateTimeInterface::class);
$argumentType = $scope->getType($argument->value);

// initialize with end parameter
if ($dateTimeType->isSuperTypeOf($argumentType)->yes()) {
return $dateTimeType;
}

// initialize with recurrences parameter
if ((new IntegerType())->isSuperTypeOf($argumentType)->yes()) {
return new NullType();
}

return TypeCombinator::addNull($dateTimeType);
}

}
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-2760.php');

yield from $this->gatherAssertTypes(__DIR__ . '/data/filesystem-functions.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/DatePeriodGetEndDateDynamicReturnType.php');
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types=1);

use function PHPStan\Testing\assertType;

$start = new DateTime('2012-07-01');
$interval = new DateInterval('P7D');
$end = new DateTime('2012-07-31');
$recurrences = 4;
$iso = 'R4/2012-07-01T00:00:00Z/P7D';

assertType('null', (new DatePeriod($start, $interval, $recurrences))->getEndDate());
assertType(\DateTimeInterface::class, (new DatePeriod($start, $interval, $end))->getEndDate());
assertType('null', (new DatePeriod($iso))->getEndDate());

0 comments on commit eb401c8

Please sign in to comment.