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',