diff --git a/config/set/datetime-to-carbon.php b/config/set/datetime-to-carbon.php new file mode 100644 index 00000000000..49b83c79d73 --- /dev/null +++ b/config/set/datetime-to-carbon.php @@ -0,0 +1,12 @@ +rules([DateFuncCallToCarbonRector::class, DateTimeInstanceToCarbonRector::class, DateTimeMethodCallToCarbonRector::class]); +}; diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index b4d2bff116c..f9aeae86bfb 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# 371 Rules Overview +# 374 Rules Overview
@@ -6,6 +6,8 @@ - [Arguments](#arguments) (4) +- [Carbon](#carbon) (3) + - [CodeQuality](#codequality) (75) - [CodingStyle](#codingstyle) (28) @@ -142,6 +144,59 @@ Replaces defined map of arguments in defined methods and their calls.
+## Carbon + +### DateFuncCallToCarbonRector + +Convert `date()` function call to Carbon::*() + +- class: [`Rector\Carbon\Rector\FuncCall\DateFuncCallToCarbonRector`](../rules/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector.php) + +```diff + class SomeClass + { + public function run() + { +- $date = date('Y-m-d'); ++ $date = \Carbon\Carbon::now()->format('Y-m-d') + } + } +``` + +
+ +### DateTimeInstanceToCarbonRector + +Convert new `DateTime()` to Carbon::*() + +- class: [`Rector\Carbon\Rector\New_\DateTimeInstanceToCarbonRector`](../rules/Carbon/Rector/New_/DateTimeInstanceToCarbonRector.php) + +```diff +-$date = new \DateTime('today'); ++$date = \Carbon\Carbon::today(); +``` + +
+ +### DateTimeMethodCallToCarbonRector + +Convert new `DateTime()` with a method call to Carbon::*() + +- class: [`Rector\Carbon\Rector\MethodCall\DateTimeMethodCallToCarbonRector`](../rules/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector.php) + +```diff + class SomeClass + { + public function run() + { +- $date = (new \DateTime('today +20 day'))->format('Y-m-d'); ++ $date = \Carbon\Carbon::today()->addDays(20)->format('Y-m-d') + } + } +``` + +
+ ## CodeQuality ### AbsolutizeRequireAndIncludePathRector diff --git a/rules/Carbon/NodeFactory/CarbonCallFactory.php b/rules/Carbon/NodeFactory/CarbonCallFactory.php new file mode 100644 index 00000000000..01a1368c886 --- /dev/null +++ b/rules/Carbon/NodeFactory/CarbonCallFactory.php @@ -0,0 +1,58 @@ +\\d+)(\\s+)?(day|days)#'; + /** + * @var string + * @see https://regex101.com/r/6VUUQF/1 + */ + private const MONTH_COUNT_REGEX = '#\\+(\\s+)?(?\\d+)(\\s+)?(month|months)#'; + /** + * @var array + */ + private const REGEX_TO_METHOD_NAME_MAP = [self::DAY_COUNT_REGEX => 'addDays', self::MONTH_COUNT_REGEX => 'addMonths']; + /** + * @return \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall + */ + public function createFromDateTimeString(FullyQualified $carbonFullyQualified, String_ $string) + { + $dateTimeValue = $string->value; + if ($dateTimeValue === 'now') { + return new StaticCall($carbonFullyQualified, new Identifier('now')); + } + if ($dateTimeValue === 'today') { + return new StaticCall($carbonFullyQualified, new Identifier('today')); + } + $hasToday = Strings::match($dateTimeValue, '#today#'); + if ($hasToday !== null) { + $carbonCall = new StaticCall($carbonFullyQualified, new Identifier('today')); + } else { + $carbonCall = new StaticCall($carbonFullyQualified, new Identifier('now')); + } + foreach (self::REGEX_TO_METHOD_NAME_MAP as $regex => $methodName) { + $match = Strings::match($dateTimeValue, $regex); + if ($match === null) { + continue; + } + $countLNumber = new LNumber((int) $match['count']); + $carbonCall = new MethodCall($carbonCall, new Identifier($methodName), [new Arg($countLNumber)]); + } + return $carbonCall; + } +} diff --git a/rules/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector.php b/rules/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector.php new file mode 100644 index 00000000000..03eb447aaaa --- /dev/null +++ b/rules/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector.php @@ -0,0 +1,69 @@ +format('Y-m-d') + } +} +CODE_SAMPLE +)]); + } + /** + * @return array> + */ + public function getNodeTypes() : array + { + return [FuncCall::class]; + } + /** + * @param FuncCall $node + */ + public function refactor(Node $node) : ?Node + { + if (!$this->isName($node->name, 'date')) { + return null; + } + if (\count($node->getArgs()) !== 1) { + return null; + } + $firstArg = $node->getArgs()[0]; + if (!$firstArg->value instanceof String_) { + return null; + } + // create now and format() + $nowStaticCall = new StaticCall(new FullyQualified('Carbon\\Carbon'), 'now'); + return new MethodCall($nowStaticCall, 'format', [new Arg($firstArg->value)]); + } +} diff --git a/rules/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector.php b/rules/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector.php new file mode 100644 index 00000000000..5dbc6267046 --- /dev/null +++ b/rules/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector.php @@ -0,0 +1,87 @@ +carbonCallFactory = $carbonCallFactory; + } + public function getRuleDefinition() : RuleDefinition + { + return new RuleDefinition('Convert new DateTime() with a method call to Carbon::*()', [new CodeSample(<<<'CODE_SAMPLE' +class SomeClass +{ + public function run() + { + $date = (new \DateTime('today +20 day'))->format('Y-m-d'); + } +} +CODE_SAMPLE +, <<<'CODE_SAMPLE' +class SomeClass +{ + public function run() + { + $date = \Carbon\Carbon::today()->addDays(20)->format('Y-m-d') + } +} +CODE_SAMPLE +)]); + } + /** + * @return array> + */ + public function getNodeTypes() : array + { + return [MethodCall::class]; + } + /** + * @param MethodCall $node + */ + public function refactor(Node $node) : ?Node + { + if (!$node->var instanceof New_) { + return null; + } + $new = $node->var; + if (!$new->class instanceof Name) { + return null; + } + if (!$this->isName($new->class, 'DateTime')) { + return null; + } + if (\count($new->getArgs()) !== 1) { + // @todo handle in separate static call + return null; + } + $firstArg = $new->getArgs()[0]; + if (!$firstArg->value instanceof String_) { + return null; + } + $carbonFullyQualified = new FullyQualified('Carbon\\Carbon'); + $carbonCall = $this->carbonCallFactory->createFromDateTimeString($carbonFullyQualified, $firstArg->value); + $node->var = $carbonCall; + return $node; + } +} diff --git a/rules/Carbon/Rector/New_/DateTimeInstanceToCarbonRector.php b/rules/Carbon/Rector/New_/DateTimeInstanceToCarbonRector.php new file mode 100644 index 00000000000..e5b0287cd06 --- /dev/null +++ b/rules/Carbon/Rector/New_/DateTimeInstanceToCarbonRector.php @@ -0,0 +1,70 @@ +carbonCallFactory = $carbonCallFactory; + } + public function getRuleDefinition() : RuleDefinition + { + return new RuleDefinition('Convert new DateTime() to Carbon::*()', [new CodeSample(<<<'CODE_SAMPLE' +$date = new \DateTime('today'); +CODE_SAMPLE +, <<<'CODE_SAMPLE' +$date = \Carbon\Carbon::today(); +CODE_SAMPLE +)]); + } + /** + * @return array> + */ + public function getNodeTypes() : array + { + return [New_::class]; + } + /** + * @param New_ $node + */ + public function refactor(Node $node) : ?Node + { + if (!$this->isName($node->class, 'DateTime')) { + return null; + } + // no arg? ::now() + $carbonFullyQualified = new FullyQualified('Carbon\\Carbon'); + if ($node->args === []) { + return new StaticCall($carbonFullyQualified, new Identifier('now')); + } + if (\count($node->getArgs()) === 1) { + $firstArg = $node->getArgs()[0]; + if ($firstArg->value instanceof String_) { + return $this->carbonCallFactory->createFromDateTimeString($carbonFullyQualified, $firstArg->value); + } + } + return null; + } +} diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index db23ad6def9..588fae83b77 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -19,12 +19,12 @@ final class VersionResolver * @api * @var string */ - public const PACKAGE_VERSION = '5df95d9a955523a1737442a26f4a412cafc41226'; + public const PACKAGE_VERSION = '575921d2bca4a0ef36d55c6fa4ebf1ea01bacc4d'; /** * @api * @var string */ - public const RELEASE_DATE = '2024-05-18 19:46:02'; + public const RELEASE_DATE = '2024-05-18 15:27:32'; /** * @var int */ diff --git a/src/Configuration/RectorConfigBuilder.php b/src/Configuration/RectorConfigBuilder.php index a2ca1f3de85..aca921a399b 100644 --- a/src/Configuration/RectorConfigBuilder.php +++ b/src/Configuration/RectorConfigBuilder.php @@ -375,7 +375,7 @@ final class RectorConfigBuilder } return $this; } - public function withPreparedSets(bool $deadCode = \false, bool $codeQuality = \false, bool $codingStyle = \false, bool $typeDeclarations = \false, bool $privatization = \false, bool $naming = \false, bool $instanceOf = \false, bool $earlyReturn = \false, bool $strictBooleans = \false) : self + public function withPreparedSets(bool $deadCode = \false, bool $codeQuality = \false, bool $codingStyle = \false, bool $typeDeclarations = \false, bool $privatization = \false, bool $naming = \false, bool $instanceOf = \false, bool $earlyReturn = \false, bool $strictBooleans = \false, bool $carbon = \false) : self { if ($deadCode) { $this->sets[] = SetList::DEAD_CODE; @@ -404,6 +404,9 @@ final class RectorConfigBuilder if ($strictBooleans) { $this->sets[] = SetList::STRICT_BOOLEANS; } + if ($carbon) { + $this->sets[] = SetList::CARBON; + } return $this; } /** diff --git a/src/Set/ValueObject/SetList.php b/src/Set/ValueObject/SetList.php index aa42e85c42f..0a685394659 100644 --- a/src/Set/ValueObject/SetList.php +++ b/src/Set/ValueObject/SetList.php @@ -109,4 +109,8 @@ final class SetList implements SetListInterface * @var string */ public const INSTANCEOF = __DIR__ . '/../../../config/set/instanceof.php'; + /** + * @var string + */ + public const CARBON = __DIR__ . '/../../../config/set/datetime-to-carbon.php'; } diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index d64ba29ca47..f444111fa57 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -1053,6 +1053,10 @@ return array( 'Rector\\Caching\\ValueObject\\CacheItem' => $baseDir . '/src/Caching/ValueObject/CacheItem.php', 'Rector\\Caching\\ValueObject\\Storage\\FileCacheStorage' => $baseDir . '/src/Caching/ValueObject/Storage/FileCacheStorage.php', 'Rector\\Caching\\ValueObject\\Storage\\MemoryCacheStorage' => $baseDir . '/src/Caching/ValueObject/Storage/MemoryCacheStorage.php', + 'Rector\\Carbon\\NodeFactory\\CarbonCallFactory' => $baseDir . '/rules/Carbon/NodeFactory/CarbonCallFactory.php', + 'Rector\\Carbon\\Rector\\FuncCall\\DateFuncCallToCarbonRector' => $baseDir . '/rules/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector.php', + 'Rector\\Carbon\\Rector\\MethodCall\\DateTimeMethodCallToCarbonRector' => $baseDir . '/rules/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector.php', + 'Rector\\Carbon\\Rector\\New_\\DateTimeInstanceToCarbonRector' => $baseDir . '/rules/Carbon/Rector/New_/DateTimeInstanceToCarbonRector.php', 'Rector\\ChangesReporting\\Annotation\\AnnotationExtractor' => $baseDir . '/src/ChangesReporting/Annotation/AnnotationExtractor.php', 'Rector\\ChangesReporting\\Annotation\\RectorsChangelogResolver' => $baseDir . '/src/ChangesReporting/Annotation/RectorsChangelogResolver.php', 'Rector\\ChangesReporting\\Contract\\Output\\OutputFormatterInterface' => $baseDir . '/src/ChangesReporting/Contract/Output/OutputFormatterInterface.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 6838272468d..dd7da1f55b6 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -1272,6 +1272,10 @@ class ComposerStaticInitc5b31423e044e1f31bdf2918ae19cbd3 'Rector\\Caching\\ValueObject\\CacheItem' => __DIR__ . '/../..' . '/src/Caching/ValueObject/CacheItem.php', 'Rector\\Caching\\ValueObject\\Storage\\FileCacheStorage' => __DIR__ . '/../..' . '/src/Caching/ValueObject/Storage/FileCacheStorage.php', 'Rector\\Caching\\ValueObject\\Storage\\MemoryCacheStorage' => __DIR__ . '/../..' . '/src/Caching/ValueObject/Storage/MemoryCacheStorage.php', + 'Rector\\Carbon\\NodeFactory\\CarbonCallFactory' => __DIR__ . '/../..' . '/rules/Carbon/NodeFactory/CarbonCallFactory.php', + 'Rector\\Carbon\\Rector\\FuncCall\\DateFuncCallToCarbonRector' => __DIR__ . '/../..' . '/rules/Carbon/Rector/FuncCall/DateFuncCallToCarbonRector.php', + 'Rector\\Carbon\\Rector\\MethodCall\\DateTimeMethodCallToCarbonRector' => __DIR__ . '/../..' . '/rules/Carbon/Rector/MethodCall/DateTimeMethodCallToCarbonRector.php', + 'Rector\\Carbon\\Rector\\New_\\DateTimeInstanceToCarbonRector' => __DIR__ . '/../..' . '/rules/Carbon/Rector/New_/DateTimeInstanceToCarbonRector.php', 'Rector\\ChangesReporting\\Annotation\\AnnotationExtractor' => __DIR__ . '/../..' . '/src/ChangesReporting/Annotation/AnnotationExtractor.php', 'Rector\\ChangesReporting\\Annotation\\RectorsChangelogResolver' => __DIR__ . '/../..' . '/src/ChangesReporting/Annotation/RectorsChangelogResolver.php', 'Rector\\ChangesReporting\\Contract\\Output\\OutputFormatterInterface' => __DIR__ . '/../..' . '/src/ChangesReporting/Contract/Output/OutputFormatterInterface.php',