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 24, 2020
1 parent fb95fab commit db4aba0
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 0 deletions.
1 change: 1 addition & 0 deletions conf/bleedingEdge.neon
Expand Up @@ -7,5 +7,6 @@ parameters:
fileWhitespace: true
unusedClassElements: true
readComposerPhpVersion: true
dateTimeInstantiation: true
stubFiles:
- ../stubs/SplObjectStorage.stub
4 changes: 4 additions & 0 deletions conf/config.level5.neon
Expand Up @@ -4,6 +4,8 @@ includes:
conditionalTags:
PHPStan\Rules\Functions\RandomIntParametersRule:
phpstan.rules.rule: %featureToggles.randomIntParameters%
PHPStan\Rules\DateTimeInstantiationRule:
phpstan.rules.rule: %featureToggles.dateTimeInstantiation%

parameters:
checkFunctionArgumentTypes: true
Expand All @@ -15,3 +17,5 @@ services:
class: PHPStan\Rules\Functions\RandomIntParametersRule
arguments:
reportMaybes: %reportMaybes%
-
class: PHPStan\Rules\DateTimeInstantiationRule
2 changes: 2 additions & 0 deletions conf/config.neon
Expand Up @@ -19,6 +19,7 @@ parameters:
fileWhitespace: false
unusedClassElements: false
readComposerPhpVersion: false
dateTimeInstantiation: false
fileExtensions:
- php
checkAlwaysTrueCheckTypeFunctionCall: false
Expand Down Expand Up @@ -151,6 +152,7 @@ parametersSchema:
fileWhitespace: bool(),
unusedClassElements: bool(),
readComposerPhpVersion: bool()
dateTimeInstantiation: bool()
])
fileExtensions: listOf(string())
checkAlwaysTrueCheckTypeFunctionCall: bool()
Expand Down
70 changes: 70 additions & 0 deletions src/Rules/DateTimeInstantiationRule.php
@@ -0,0 +1,70 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules;

use DateTime;
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)
|| \count($node->args) === 0
|| !\in_array(strtolower((string) $node->class), ['datetime', 'datetimeimmutable'], true)
) {
return [];
}

$arg = $scope->getType($node->args[0]->value);
if (!($arg 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
}
$lastErrors = DateTime::getLastErrors();
if ($lastErrors !== false) {
foreach ($lastErrors['warnings'] as $warning) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Instantiating %s with %s produces a warning: %s',
(string) $node->class,
$dateString,
$warning
))->build();
}
foreach ($lastErrors['errors'] as $error) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Instantiating %s with %s produces an error: %s',
(string) $node->class,
$dateString,
$error
))->build();
}
}

return $errors;
}

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

namespace PHPStan\Rules;

/**
* @extends \PHPStan\Testing\RuleTestCase<DateTimeInstantiationRule>
*/
class DateTimeInstantiationRuleTest 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 db4aba0

Please sign in to comment.