Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions resources/functionMap_php83delta.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
'new' => [
'DateTime::modify' => ['static', 'modify'=>'string'],
'DateTimeImmutable::modify' => ['static', 'modify'=>'string'],
'DateInterval::createFromDateString' => ['DateInterval', 'time'=>'string'],
'str_decrement' => ['non-empty-string', 'string'=>'non-empty-string'],
'str_increment' => ['non-falsy-string', 'string'=>'non-empty-string'],
'gc_status' => ['array{running:bool,protected:bool,full:bool,runs:int,collected:int,threshold:int,buffer_size:int,roots:int,application_time:float,collector_time:float,destructor_time:float,free_time:float}'],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Php;

use DateInterval;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\DynamicStaticMethodThrowTypeExtension;
use PHPStan\Type\NeverType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function count;

#[AutowiredService]
final class DateIntervalCreateFromDateStringMethodThrowTypeExtension implements DynamicStaticMethodThrowTypeExtension
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this extension needs testing.

see e.g. 5645c04#diff-0ec4f831901651eabb90c05586b3404e02c820361f0df02b7789a381c65d1f7d on how other *ThrowTypeExtensions have been tested in the past

{

public function __construct(private PhpVersion $phpVersion)
{
}

public function isStaticMethodSupported(MethodReflection $methodReflection): bool
{
return $methodReflection->getName() === 'createFromDateString' && $methodReflection->getDeclaringClass()->getName() === DateInterval::class;
}

public function getThrowTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): ?Type
{
if (count($methodCall->getArgs()) === 0) {
return null;
}

if (!$this->phpVersion->hasDateTimeExceptions()) {
return null;
}

$valueType = $scope->getType($methodCall->getArgs()[0]->value);
$constantStrings = $valueType->getConstantStrings();

foreach ($constantStrings as $constantString) {
try {
DateInterval::createFromDateString($constantString->getValue());
} catch (\Exception) { // phpcs:ignore
return $methodReflection->getThrowType();
}

$valueType = TypeCombinator::remove($valueType, $constantString);
}

if (!$valueType instanceof NeverType) {
return $methodReflection->getThrowType();
}

return null;
}

}
13 changes: 13 additions & 0 deletions src/Type/Php/DateIntervalDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Php\PhpVersion;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
use PHPStan\Type\NeverType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Throwable;
Expand All @@ -20,6 +22,10 @@
implements DynamicStaticMethodReturnTypeExtension
{

public function __construct(private PhpVersion $phpVersion)
{
}

public function getClass(): string
{
return DateInterval::class;
Expand All @@ -44,13 +50,20 @@
foreach ($strings as $string) {
try {
$result = @DateInterval::createFromDateString($string->getValue());
if ($this->phpVersion->hasDateTimeExceptions()) {
return new ObjectType(DateInterval::class);
}
} catch (Throwable) {
if ($this->phpVersion->hasDateTimeExceptions()) {
return new NeverType();
}
$possibleReturnTypes[] = false;
continue;
}
// @phpstan-ignore instanceof.alwaysTrue (should only run for < 8.3 and then statement isn't true)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can get arround this by moving the above newly added return cases after the try/catch into the if/else block, which also does the return-type determination for the PHP < 8.3 case

$possibleReturnTypes[] = $result instanceof DateInterval ? DateInterval::class : false;

Check failure on line 64 in src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

View workflow job for this annotation

GitHub Actions / PHPStan with result cache (8.2)

No error with identifier instanceof.alwaysTrue is reported on line 64.

Check failure on line 64 in src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.2, ubuntu-latest)

No error with identifier instanceof.alwaysTrue is reported on line 64.

Check failure on line 64 in src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

View workflow job for this annotation

GitHub Actions / Generate PHP baseline

No error with identifier instanceof.alwaysTrue is reported on line 64.

Check failure on line 64 in src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.0, ubuntu-latest)

No error with identifier instanceof.alwaysTrue is reported on line 64.

Check failure on line 64 in src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.1, ubuntu-latest)

No error with identifier instanceof.alwaysTrue is reported on line 64.

Check failure on line 64 in src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.2, windows-latest)

No error with identifier instanceof.alwaysTrue is reported on line 64.

Check failure on line 64 in src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.1, windows-latest)

No error with identifier instanceof.alwaysTrue is reported on line 64.

Check failure on line 64 in src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

View workflow job for this annotation

GitHub Actions / PHPStan (8.0, windows-latest)

No error with identifier instanceof.alwaysTrue is reported on line 64.
}

Check failure on line 66 in src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

View workflow job for this annotation

GitHub Actions / PHPStan (7.4, ubuntu-latest)

No error with identifier instanceof.alwaysTrue is reported on line 66.

Check failure on line 66 in src/Type/Php/DateIntervalDynamicReturnTypeExtension.php

View workflow job for this annotation

GitHub Actions / PHPStan (7.4, windows-latest)

No error with identifier instanceof.alwaysTrue is reported on line 66.
// the error case, when wrong types are passed
if (count($possibleReturnTypes) === 0) {
return null;
Expand Down
13 changes: 13 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-14479-83.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php // lint >= 8.3

namespace Bug14479Php83;

use function PHPStan\Testing\assertType;

function test(string $input) {
assertType(\DateInterval::class, \DateInterval::createFromDateString($input));
}

function test2() {
assertType('*NEVER*', \DateInterval::createFromDateString('foo'));
}
Loading