diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_now_add_sub.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_now_add_sub.php.inc new file mode 100644 index 00000000000..79297d6279f --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_now_add_sub.php.inc @@ -0,0 +1,55 @@ + +----- +addSeconds(5); + $addMinutes = \Carbon\Carbon::now()->addMinutes(5); + $addHours = \Carbon\Carbon::now()->addHours(5); + $addDays = \Carbon\Carbon::now()->addDays(5); + $addWeeks = \Carbon\Carbon::now()->addWeeks(5); + $addMonths = \Carbon\Carbon::now()->addMonths(5); + $addYears = \Carbon\Carbon::now()->addYears(5); + + $subSeconds = \Carbon\Carbon::now()->subSeconds(5); + $subMinuts = \Carbon\Carbon::now()->subMinutes(5); + $subHours = \Carbon\Carbon::now()->subHours(5); + $subDays = \Carbon\Carbon::now()->subDays(5); + $subWeeks = \Carbon\Carbon::now()->subWeeks(5); + $subMonths = \Carbon\Carbon::now()->subMonths(5); + $subYears = \Carbon\Carbon::now()->subYears(5); + } +} + +?> diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_parse.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_parse.php.inc new file mode 100644 index 00000000000..e58bcef300b --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_parse.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today.php.inc index 0202ae10583..f507eaa1264 100644 --- a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today.php.inc +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_today.php.inc @@ -2,11 +2,11 @@ namespace Rector\Tests\Carbon\Rector\New_\DateTimeInstanceToCarbonRector\Fixture; -final class DateTimeWithMonths +final class DateTimeToday { public function run() { - $date = new \DateTime('+ 2 months'); + $date = new \DateTime('today'); } } @@ -16,11 +16,11 @@ final class DateTimeWithMonths namespace Rector\Tests\Carbon\Rector\New_\DateTimeInstanceToCarbonRector\Fixture; -final class DateTimeWithMonths +final class DateTimeToday { public function run() { - $date = \Carbon\Carbon::now()->addMonths(2); + $date = \Carbon\Carbon::today(); } } diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_months.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_tomorrow.php.inc similarity index 65% rename from rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_months.php.inc rename to rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_tomorrow.php.inc index 55be7e7de77..efa02e57bd7 100644 --- a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_months.php.inc +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_tomorrow.php.inc @@ -2,11 +2,11 @@ namespace Rector\Tests\Carbon\Rector\New_\DateTimeInstanceToCarbonRector\Fixture; -final class DateTimeNow +final class DateTimeTomorrow { public function run() { - $date = new \DateTime('now'); + $date = new \DateTime('tomorrow'); } } @@ -16,11 +16,11 @@ final class DateTimeNow namespace Rector\Tests\Carbon\Rector\New_\DateTimeInstanceToCarbonRector\Fixture; -final class DateTimeNow +final class DateTimeTomorrow { public function run() { - $date = \Carbon\Carbon::now(); + $date = \Carbon\Carbon::tomorrow(); } } diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_date_time.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_date_time.php.inc new file mode 100644 index 00000000000..040724664a6 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_date_time.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_today_sub_time.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_today_sub_time.php.inc deleted file mode 100644 index b3e5f8c5200..00000000000 --- a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_with_today_sub_time.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ -addHours(5); - $addMinutes = \Carbon\Carbon::now()->addMinutes(5); - $addSeconds = \Carbon\Carbon::now()->addSeconds(5); - - $subHours = \Carbon\Carbon::now()->subHours(5); - $subMinuts = \Carbon\Carbon::now()->subMinutes(5); - $subSeconds = \Carbon\Carbon::now()->subSeconds(5); - } -} - -?> diff --git a/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_yesterday.php.inc b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_yesterday.php.inc new file mode 100644 index 00000000000..fc08ec57e79 --- /dev/null +++ b/rules-tests/Carbon/Rector/New_/DateTimeInstanceToCarbonRector/Fixture/datetime_yesterday.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules/Carbon/NodeFactory/CarbonCallFactory.php b/rules/Carbon/NodeFactory/CarbonCallFactory.php index e11e3d6a80b..fddbf749793 100644 --- a/rules/Carbon/NodeFactory/CarbonCallFactory.php +++ b/rules/Carbon/NodeFactory/CarbonCallFactory.php @@ -1,7 +1,6 @@ \d+)(\s+)?(day|days)#'; + private const PLUS_MINUS_COUNT_REGEX = '#(?\+|-)(\\s+)?(?\\d+)(\s+)?(?seconds|second|sec|minutes|minute|min|hours|hour|days|day|weeks|week|months|month|years|year)#'; /** * @var string - * @see https://regex101.com/r/pqOPg6/1 + * @see https://regex101.com/r/IhxHTO/1 */ - private const MINUS_DAY_COUNT_REGEX = '#-(\s+)?(?\d+)(\s+)?(day|days)#'; + private const STATIC_DATE_REGEX = '#now|yesterday|today|tomorrow#'; /** - * @var string - * @see https://regex101.com/r/6VUUQF/1 + * @return \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall */ - private const PLUS_MONTH_COUNT_REGEX = '#\+(\s+)?(?\d+)(\s+)?(month|months)#'; + public function createFromDateTimeString(FullyQualified $carbonFullyQualified, String_ $string) + { + $carbonCall = $this->createStaticCall($carbonFullyQualified, $string); + $string->value = Strings::replace($string->value, self::STATIC_DATE_REGEX); + + // Handle add/sub multiple times + while ($match = Strings::match($string->value, self::PLUS_MINUS_COUNT_REGEX)) { + $methodCall = $this->createModifyMethodCall($carbonCall, new LNumber((int) $match['count']), $match['unit'], $match['operator']); + if ($methodCall) { + $carbonCall = $methodCall; + $string->value = Strings::replace($string->value, self::PLUS_MINUS_COUNT_REGEX, '', 1); + } + } - /** - * @var string - * @see https://regex101.com/r/dWRjk5/1 - */ - private const PLUS_HOUR_COUNT_REGEX = '#\+(\s+)?(?\d+)(\s+)?(hour|hours)#'; + // If we still have something in the string, we go back to the first method and replace this with a parse + if (($rest = Strings::trim($string->value)) !== '') { + $currentCall = $carbonCall; + $callStack = []; + while ($currentCall instanceof MethodCall) { + $callStack[] = $currentCall; + $currentCall = $currentCall->var; + } - /** - * @var string - * @see https://regex101.com/r/dfK0Ri/1 - */ - private const PLUS_MINUTE_COUNT_REGEX = '#\+(\s+)?(?\d+)(\s+)?(minute|minuts)#'; + if (! $currentCall instanceof StaticCall) { + return $carbonCall; + } - /** - * @var string - * @see https://regex101.com/r/o7QDYL/1 - */ - private const PLUS_SECOND_COUNT_REGEX = '#\+(\s+)?(?\d+)(\s+)?(second|seconds)#'; + // If we fallback to a parse we want to include tomorrow/today/yesterday etc + if ($currentCall->name instanceof Identifier) { + if ($currentCall->name->name != 'now') { + $rest .= ' ' . $currentCall->name->name; + } + } - /** - * @var string - * @see https://regex101.com/r/IvyT7w/1 - */ - private const MINUS_MONTH_COUNT_REGEX = '#-(\s+)?(?\d+)(\s+)?(month|months)#'; + $currentCall->name = new Identifier('parse'); + $currentCall->args = [new Arg(new String_($rest))]; - /** - * @var string - * @see https://regex101.com/r/bICKg6/1 - */ - private const MINUS_HOUR_COUNT_REGEX = '#-(\s+)?(?\d+)(\s+)?(hour|hours)#'; + // Rebuild original call from callstack + $carbonCall = $this->rebuildCallStack($currentCall, $callStack); + } - /** - * @var string - * @see https://regex101.com/r/WILFvX/1 - */ - private const MINUS_MINUTE_COUNT_REGEX = '#-(\s+)?(?\d+)(\s+)?(minute|minutes)#'; + return $carbonCall; + } - /** - * @var string - * @see https://regex101.com/r/FwCUup/1 - */ - private const MINUS_SECOND_COUNT_REGEX = '#-(\s+)?(?\d+)(\s+)?(second|seconds)#'; + private function createStaticCall(FullyQualified $carbonFullyQualified, String_ $string): StaticCall + { + $startDate = Strings::match($string->value, self::STATIC_DATE_REGEX)[0] ?? 'now'; + $carbonCall = new StaticCall($carbonFullyQualified, new Identifier($startDate)); - /** - * @var array - */ - private const REGEX_TO_METHOD_NAME_MAP = [ - self::PLUS_DAY_COUNT_REGEX => 'addDays', - self::MINUS_DAY_COUNT_REGEX => 'subDays', - self::PLUS_MONTH_COUNT_REGEX => 'addMonths', - self::MINUS_MONTH_COUNT_REGEX => 'subMonths', - self::PLUS_HOUR_COUNT_REGEX => 'addHours', - self::MINUS_HOUR_COUNT_REGEX => 'subHours', - self::PLUS_MINUTE_COUNT_REGEX => 'addMinutes', - self::MINUS_MINUTE_COUNT_REGEX => 'subMinutes', - self::PLUS_SECOND_COUNT_REGEX => 'addSeconds', - self::MINUS_SECOND_COUNT_REGEX => 'subSeconds', - ]; - - public function createFromDateTimeString( - FullyQualified $carbonFullyQualified, - String_ $string - ): MethodCall|StaticCall { - $dateTimeValue = $string->value; - if ($dateTimeValue === 'now') { - return new StaticCall($carbonFullyQualified, new Identifier('now')); - } + return $carbonCall; + } - if ($dateTimeValue === 'today') { - return new StaticCall($carbonFullyQualified, new Identifier('today')); + private function createModifyMethodCall(MethodCall|StaticCall $carbonCall, LNumber $countLNumber, string $unit, string $operator): ?MethodCall + { + $unit = match($unit) { + 'sec', 'second', 'seconds' => 'seconds', + 'min', 'minute', 'minutes' => 'minutes', + 'hour', 'hours' => 'hours', + 'day', 'days' => 'days', + 'week', 'weeks' => 'weeks', + 'month', 'months' => 'months', + 'year', 'years' => 'years', + default => null, + }; + + $operator = match($operator) { + '+' => 'add', + '-' => 'sub', + default => null, + }; + + if ($unit === null || $operator === null) { + return null; } - $hasToday = Strings::match($dateTimeValue, '#today#'); - if ($hasToday !== null) { - $carbonCall = new StaticCall($carbonFullyQualified, new Identifier('today')); - } else { - $carbonCall = new StaticCall($carbonFullyQualified, new Identifier('now')); - } + $methodName = $operator . ucfirst($unit); - foreach (self::REGEX_TO_METHOD_NAME_MAP as $regex => $methodName) { - $match = Strings::match($dateTimeValue, $regex); - if ($match === null) { - continue; - } + return new MethodCall($carbonCall, new Identifier($methodName), [new Arg($countLNumber)]); + } - $countLNumber = new LNumber((int) $match['count']); + /** + * @param MethodCall[] $callStack + */ + private function rebuildCallStack(StaticCall $carbonCall, array $callStack): MethodCall|StaticCall + { + if (count($callStack) === 0) { + return $carbonCall; + } - $carbonCall = new MethodCall($carbonCall, new Identifier($methodName), [new Arg($countLNumber)]); + $currentCall = $carbonCall; + $callStack = array_reverse($callStack); + foreach($callStack as $call) { + $call->var = $currentCall; + $currentCall = $call; } - return $carbonCall; + return $currentCall; } }