Skip to content

Commit

Permalink
Check datetime instantiation
Browse files Browse the repository at this point in the history
  • Loading branch information
b1rdex committed Nov 23, 2020
1 parent 41ac212 commit 8515526
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 0 deletions.
1 change: 1 addition & 0 deletions conf/config.level0.neon
Expand Up @@ -55,6 +55,7 @@ rules:
- PHPStan\Rules\Properties\AccessStaticPropertiesInAssignRule
- PHPStan\Rules\Properties\PropertyAttributesRule
- PHPStan\Rules\Variables\UnsetRule
- PHPStan\Rules\DateTimeInstantiationRule

services:
-
Expand Down
65 changes: 65 additions & 0 deletions src/Rules/DateTimeInstantiationRule.php
@@ -0,0 +1,65 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules;

use DateTime;
use DateTimeImmutable;
use PhpParser\Node;
use PhpParser\Node\Expr\New_;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Constant\ConstantStringType;

/**
* @implements \PHPStan\Rules\Rule<\PhpParser\Node\Expr\New_>
*/
class DateTimeInstantiationRule implements \PHPStan\Rules\Rule
{

public function getNodeType(): string
{
return New_::class;
}

/**
* @param New_ $node
*/
public function processNode(Node $node, Scope $scope): array
{
if (
!($node->class instanceof \PhpParser\Node\Name)
|| !\in_array((string) $node->class, [DateTime::class, DateTimeImmutable::class], true)
|| \count($node->args) === 0
|| !($arg = $scope->getType($node->args[0]->value)) instanceof ConstantStringType
) {
return [];
}

$errors = [];
$dateString = $arg->getValue();
try {
new DateTime($dateString);
} catch (\Throwable $e) {
// an exception is thrown for errors only but we want to catch warnings too
}
$result = DateTime::getLastErrors();
foreach ($result['warnings'] as $warning) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Instantiating %s with %s produces a warning: %s',
(string) $node->class,
$dateString,
$warning
))->build();
}
foreach ($result['errors'] as $error) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Instantiating %s with %s produces an error: %s',
(string) $node->class,
$dateString,
$error
))->build();
}

return $errors;
}

}
34 changes: 34 additions & 0 deletions tests/PHPStan/Rules/DateTimeConstructorRuleTest.php
@@ -0,0 +1,34 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules;

/**
* @extends \PHPStan\Testing\RuleTestCase<DateTimeInstantiationRule>
*/
class DateTimeConstructorRuleTest extends \PHPStan\Testing\RuleTestCase
{

protected function getRule(): \PHPStan\Rules\Rule
{
return new DateTimeInstantiationRule();
}

public function test(): void
{
$this->analyse(
[__DIR__ . '/data/datetime-instantiation.php'],
[
['Instantiating DateTime with 2020.11.17 produces an error: Double time specification', 3],
['Instantiating DateTimeImmutable with asdfasdf produces a warning: Double timezone specification', 5],
[
'Instantiating DateTimeImmutable with asdfasdf produces an error: The timezone could not be found in the database',
5,
],
['Instantiating DateTimeImmutable with 2020.11.17 produces an error: Double time specification', 10],
['Instantiating DateTimeImmutable with 2020.11.18 produces an error: Double time specification', 17],
['Instantiating DateTime with 2020-04-31 produces a warning: The parsed date was invalid', 20],
]
);
}

}
20 changes: 20 additions & 0 deletions tests/PHPStan/Rules/data/datetime-instantiation.php
@@ -0,0 +1,20 @@
<?php

new DateTime('2020.11.17');

new DateTimeImmutable('asdfasdf');

new DateTime('');

$test = '2020.11.17';
new DateTimeImmutable($test);

/**
* @param '2020.11.18' $date2
*/
function foo(string $date, string $date2): void {
new DateTime($date);
new DateTimeImmutable($date2);
}

new DateTime('2020-04-31');

0 comments on commit 8515526

Please sign in to comment.