diff --git a/README.md b/README.md index faccddf..9142703 100644 --- a/README.md +++ b/README.md @@ -82,21 +82,14 @@ echo $generator->everyFifteenMinutes(); // */15 * * * * echo $generator->everyThirtyMinutes(); // 0,30 * * * * // Every minute -echo $generator->minutes('*'); // * * * * * +echo $generator->set(new \Butschster\CronExpression\Parts\Minutes\EveryMinute()); // * * * * * +echo $generator->set(new \Butschster\CronExpression\Parts\Minutes\EveryMinute(2)); // * */2 * * * -// Every ten minutes -echo $generator->minutes('*/10'); // */10 * * * * - -// Every hh:10, hh:11, ..., hh:30 minutes -echo $generator->minutes('10-30'); // 10-30 * * * * - -// Hourly at hh:01 -echo $generator->minutes(1); // 1 * * * * +// Specific minutes +echo $generator->set(new \Butschster\CronExpression\Parts\Minutes\SpecificMinutes(2, 3, 10)); // * 2,3,10 * * * -// Hourly at hh:05, hh:10, hh:15 -echo $generator->minutes(5, 10, 15, ...); // 5,10,15,... * * * * - -echo $generator->minutes(61); // Throws an exception: Invalid CRON field value 61 at position 0 +// Between minutes +echo $generator->set(new \Butschster\CronExpression\Parts\Minutes\BetweenMinutes(0, 30)); // * 0-30 * * * ``` ### Manipulate hours @@ -122,13 +115,15 @@ echo $generator->everyFourHours(); // 0 */4 * * * // Every six hours echo $generator->everySixHours(); // 0 */6 * * * -// Every 1, 2, 3 hours at 15 minutes -echo $generator->hourlyAt(15)->hours(1, 2, 3); // 15 */3 * * * +// Every 1, 2, 3 hours +echo $generator->set(new \Butschster\CronExpression\Parts\Hours\SpecificHours(1, 2, 3)); // * 1,2,3 * * * // Every three hours -echo $generator->hours('*/3'); // * */3 * * * +echo $generator->set(new \Butschster\CronExpression\Parts\Hours\EveryHour()); // * * * * * +echo $generator->set(new \Butschster\CronExpression\Parts\Hours\EveryHour(3)); // * */3 * * * -echo $generator->hours(25); // Throws an exception: Invalid CRON field value 25 at position 1 +// Between hours +echo $generator->set(new \Butschster\CronExpression\Parts\Hours\BetweenHours(0, 12)); // * 0-12 * * * ``` ### Manipulate days @@ -153,6 +148,38 @@ echo $generator->twiceDaily(3, 15); // 0 3,15 * * * // Every day at 03:05, 15:05 echo $generator->twiceDailyAt(3, 15, 5); // 5 3,15 * * * + +// Every month on the last day at 00:00 +echo $generator->lastDayOfMonth(); // 0 0 L * * + +// Every month on the last day at 12:00 +echo $generator->lastDayOfMonth(12); // 0 12 L * * + +// Every month on the last day at 12:30 +echo $generator->lastDayOfMonth(12, 30); // 30 12 L * * + +// Every month on the last weekday at 00:00 +echo $generator->lastWeekdayOfMonth(); // 0 0 LW * * + +// Every month on the last weekday at 12:00 +echo $generator->lastWeekdayOfMonth(12); // 0 12 LW * * + +// Every month on the last weekday at 12:30 +echo $generator->lastWeekdayOfMonth(12, 30); // 30 12 LW * * + +// Every 1, 2, 3 days +echo $generator->set(new \Butschster\CronExpression\Parts\Days\SpecificDays(1, 2, 3)); // * * 1,2,3 * * + +echo $generator->set(new \Butschster\CronExpression\Parts\Days\EveryDay()); // * * * * * + +// Every three days +echo $generator->set(new \Butschster\CronExpression\Parts\Days\EveryDay(3)); // * * */3 * * + +// Between days +echo $generator->set(new \Butschster\CronExpression\Parts\Days\BetweenDays(0, 12)); // * * 0-12 * * + +// Last day of month +echo $generator->set(new \Butschster\CronExpression\Parts\Days\LastDayOfMonth()); // * * L * * ``` ### Manipulate days of week @@ -214,6 +241,31 @@ echo $generator->weeklyOn(Generator::MONDAY, 8); // 0 8 * * 1 // Every monday at 08:06 echo $generator->weeklyOn(Generator::MONDAY, 8, 6); // 6 8 * * 1 + +// Every day of a week +echo $generator->set(new \Butschster\CronExpression\Parts\DaysOfWeek\EveryDaysOfWeek()); // * * * * * + +// Every two days of a week +echo $generator->set(new \Butschster\CronExpression\Parts\DaysOfWeek\EveryDaysOfWeek(2)); // * * * * */2 + + +// Every Monday,Wednesday, Friday +echo $generator->set(new \Butschster\CronExpression\Parts\DaysOfWeek\SpecificDaysOfWeek(Generator::MONDAY, Generator::WEDNESDAY, Generator::FRIDAY)); // * * * * 1,3,5 + +// Between days of a week +echo $generator->set(new \Butschster\CronExpression\Parts\DaysOfWeek\BetweenDayOfWeek(Generator::MONDAY, Generator::FRIDAY)); // * * * * 1-5 + +// Last monday of a week +echo $generator->set(new \Butschster\CronExpression\Parts\DaysOfWeek\LastDayOwWeek()); // * * * * 1L + +// Last friday of a week +echo $generator->set(new \Butschster\CronExpression\Parts\DaysOfWeek\LastDayOwWeek(Generator::FRIDAY)); // * * * * 5L + +// Every first monday of every month +echo $generator->set(new \Butschster\CronExpression\Parts\DaysOfWeek\NthDayOfWeek()); // * * * * 1#1 + +// Every third friday of every month +echo $generator->set(new \Butschster\CronExpression\Parts\DaysOfWeek\NthDayOfWeek(Generator::FRIDAY, 3)); // * * * * 5#3 ``` ### Manipulate months @@ -245,19 +297,6 @@ echo $generator->twiceMonthly(15, 24, 10, 30); // 30 10 15,24 * * // Every month three times on 12, 24, 30 day at 10:345 echo $generator->dailyAt(10, 45)->daysOfMonth(12, 24, 30); // 45 10 12,24,30 * * -// Every month on the last day at 00:00 -echo $generator->lastDayOfMonth(); // 0 0 31 * * - -// Every month on the last day at 12:00 -echo $generator->lastDayOfMonth(12); // 0 12 31 * * - -// Every month on the last day at 12:30 -echo $generator->lastDayOfMonth(12, 30); // 30 12 31 * * - -// Every month on the last day at specific date -$date = new DateTime('2021-02-05'); -echo $generator->lastDayOfMonth(12, 30, $date); // 30 12 28 * * - // Every quarter yyyy-01,03,06,09-01 00:00 echo $generator->quarterly(); // 0 0 1 1-12/3 * @@ -275,6 +314,21 @@ echo $generator->yearlyOn(Generator::APR, 5, 8); // 0 8 5 4 * // Every year yyyy-04-05 08:30 echo $generator->yearlyOn(Generator::APR, 5, 8, 30); // 30 8 5 4 * + +// Every month +echo $generator->set(new \Butschster\CronExpression\Parts\Months\EveryMonth()); // * * * * * + +// Every two months +echo $generator->set(new \Butschster\CronExpression\Parts\Months\EveryMonth(2)); // * * * */2 * + +// Specific months: april and december +echo $generator->set(new \Butschster\CronExpression\Parts\Months\SpecificMonths(Generator::APR, Generator::DEC)); // * * * 4,12 * + +// Between april and december +echo $generator->set(new \Butschster\CronExpression\Parts\Months\BetweenMonths(Generator::APR, Generator::DEC)); // * * * 4-12 * + +// Quarterly +echo $generator->set(new \Butschster\CronExpression\Parts\Months\Quarterly()); // * * * 1-12/3 * ``` ### Specific time @@ -283,11 +337,21 @@ $date = new DateTime('2021-02-05 12:34:26'); // Every year yyyy-02-05 12:34 echo $generator->on($date); // 34 12 5 2 * +// or +echo $generator->set(new \Butschster\CronExpression\Parts\DateTime($date)); // 34 12 5 2 * ``` ### Custom expression + ```php +use Butschster\CronExpression\Parts\Days\SpecificDays; +use Butschster\CronExpression\Parts\DaysOfWeek\SpecificDaysOfWeek; +use Butschster\CronExpression\Parts\Hours\EveryHour; +use Butschster\CronExpression\Parts\Minutes\EveryMinute; +use Butschster\CronExpression\Parts\Months\SpecificMonths; + // * */2 5,10,15,20,25,30 3,6,9,12 1,3,5,0 + echo $generator ->yearly() ->months(Generator::MAR, Generator::JUN, Generator::SEP, Generator::DEC) @@ -295,6 +359,17 @@ echo $generator ->daysOfWeek(Generator::MONDAY, Generator::WEDNESDAY, Generator::FRIDAY, Generator::SUNDAY) ->everyTwoHours() ->everyMinute(); + +// or + +echo $generator + ->set( + new SpecificMonths(Generator::MAR, Generator::JUN, Generator::SEP, Generator::DEC), + new SpecificDays(5, 10, 15, 20, 25, 30), + new SpecificDaysOfWeek(Generator::MONDAY, Generator::WEDNESDAY, Generator::FRIDAY, Generator::SUNDAY), + new EveryHour(2), + new EveryMinute() + ); ``` ### Gets next run date @@ -327,6 +402,66 @@ class Kernel extends ConsoleKernel } ``` +## Create custom expressions +To create a custom expression class you need implement `Butschster\CronExpression\PartValueInterface` + +#### Example 1 +```php +use Butschster\CronExpression\PartValueInterface; +use Cron\CronExpression; + +class Quarterly implements PartValueInterface +{ + public function updateExpression(CronExpression $expression): void + { + $expression->setPart(CronExpression::MONTH, '1-12/3'); + } +} +``` + +Using +```php +echo \Butschster\CronExpression\Generator::create()->set(new Quarterly()); // * * * 1-12/3 * +``` + +#### Example 2 + +```php +use Butschster\CronExpression\Parts\Days\SpecificDays; +use Butschster\CronExpression\Parts\Hours\SpecificHours; +use Butschster\CronExpression\Parts\Minutes\SpecificMinutes; +use Butschster\CronExpression\Parts\Months\SpecificMonths; +use Butschster\CronExpression\PartValueInterface; +use Cron\CronExpression; +use DateTimeInterface; + +class DateTime implements PartValueInterface +{ + public function __construct(private DateTimeInterface $time) + { + } + + public function updateExpression(CronExpression $expression): void + { + $parts = [ + new SpecificMinutes((int)$this->time->format('i')), + new SpecificHours((int)$this->time->format('G')), + new SpecificDays((int)$this->time->format('j')), + new SpecificMonths((int)$this->time->format('n')) + ]; + + foreach ($parts as $part) { + $part->updateExpression($expression); + } + } +} +``` + +Using +```php +echo \Butschster\CronExpression\Generator::create()->set(new DateTime(new \DateTime('2021-02-05 12:34:26'))); // 34 12 5 2 * +``` + ## Testing ```bash diff --git a/src/Expression.php b/src/Expression.php deleted file mode 100644 index e001062..0000000 --- a/src/Expression.php +++ /dev/null @@ -1,56 +0,0 @@ -setPart(self::HOUR, implode(',', $hours)); - - return $expression; - } - - public function minutes(string | int ...$minutes): self - { - $expression = clone $this; - $expression->setPart(self::MINUTE, implode(',', $minutes)); - - return $expression; - } - - public function daysOfWeek(string | int...$dayOfWeek): self - { - $dayOfWeek = $dayOfWeek ?: [Generator::MONDAY]; - - $expression = clone $this; - $expression->setPart(CronExpression::WEEKDAY, implode(',', $dayOfWeek)); - - return $expression; - } - - public function daysOfMonth(string | int ...$days): self - { - $days = $days ?: [1]; - - $expression = clone $this; - $expression->setPart(CronExpression::DAY, implode(',', $days)); - - return $expression; - } - - public function months(string | int ...$months): self - { - $months = $months ?: [Generator::JAN]; - - $expression = clone $this; - $expression->setPart(CronExpression::MONTH, implode(',', $months)); - - return $expression; - } -} diff --git a/src/Generator.php b/src/Generator.php index d6327f1..f356679 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -4,6 +4,7 @@ namespace Butschster\CronExpression; +use Butschster\CronExpression\Parts\DateTime; use Butschster\CronExpression\Traits\Days; use Butschster\CronExpression\Traits\Hours; use Butschster\CronExpression\Traits\Minutes; @@ -45,35 +46,42 @@ class Generator private const DEFAULT = '* * * * *'; - private Expression $expression; + private CronExpression $expression; - public static function create(?Expression $expression = null): self + public static function create(?CronExpression $expression = null): self { return new self($expression); } - public function __construct(?Expression $expression = null) + public function __construct(?CronExpression $expression = null) { - $this->expression = $expression ?? new Expression(self::DEFAULT); + $this->expression = $expression ?? new CronExpression(self::DEFAULT); } public function cron(string $expression): self { - return self::create(new Expression($expression)); + return self::create(new CronExpression($expression)); + } + + public function set(PartValueInterface ...$values): self + { + $expression = clone $this->expression; + + foreach ($values as $value) { + $value->updateExpression($expression); + } + + return new self($expression); } public function on(DateTimeInterface $time): self { - return $this - ->minutes((int) $time->format('i')) - ->hours($time->format('G')) - ->daysOfMonth($time->format('j')) - ->months($time->format('n')); + return $this->set(new DateTime($time)); } public function __toString(): string { - return (string) $this->toExpression(); + return $this->toExpression(); } public function toExpression(): string @@ -81,7 +89,7 @@ public function toExpression(): string return $this->getExpression()->getExpression(); } - public function setExpresion(Expression $expression): self + public function setExpresion(CronExpression $expression): self { $this->expression = $expression; diff --git a/src/PartValueInterface.php b/src/PartValueInterface.php new file mode 100644 index 0000000..d0aff32 --- /dev/null +++ b/src/PartValueInterface.php @@ -0,0 +1,12 @@ +setPart( + $this->index(), + $this->min . '-' . $this->max + ); + } +} diff --git a/src/Parts/DateTime.php b/src/Parts/DateTime.php new file mode 100644 index 0000000..834883a --- /dev/null +++ b/src/Parts/DateTime.php @@ -0,0 +1,34 @@ +time->format('i')), + new SpecificHours((int)$this->time->format('G')), + new SpecificDays((int)$this->time->format('j')), + new SpecificMonths((int)$this->time->format('n')) + ]; + + foreach ($parts as $part) { + $part->updateExpression($expression); + } + } +} diff --git a/src/Parts/Days/BetweenDays.php b/src/Parts/Days/BetweenDays.php new file mode 100644 index 0000000..0c1cced --- /dev/null +++ b/src/Parts/Days/BetweenDays.php @@ -0,0 +1,12 @@ +setPart(CronExpression::DAY, 'L'); + } +} diff --git a/src/Parts/Days/LastWeekday.php b/src/Parts/Days/LastWeekday.php new file mode 100644 index 0000000..f283ecc --- /dev/null +++ b/src/Parts/Days/LastWeekday.php @@ -0,0 +1,16 @@ +setPart(CronExpression::DAY, 'LW'); + } +} diff --git a/src/Parts/Days/SpecificDays.php b/src/Parts/Days/SpecificDays.php new file mode 100644 index 0000000..2bf9355 --- /dev/null +++ b/src/Parts/Days/SpecificDays.php @@ -0,0 +1,12 @@ +setPart(CronExpression::WEEKDAY, $this->dayOfWeek . 'L'); + } +} diff --git a/src/Parts/DaysOfWeek/NthDayOfWeek.php b/src/Parts/DaysOfWeek/NthDayOfWeek.php new file mode 100644 index 0000000..14259e4 --- /dev/null +++ b/src/Parts/DaysOfWeek/NthDayOfWeek.php @@ -0,0 +1,21 @@ +setPart(CronExpression::WEEKDAY, $this->dayOfWeek . '#' . $this->nth); + } +} diff --git a/src/Parts/DaysOfWeek/SpecificDaysOfWeek.php b/src/Parts/DaysOfWeek/SpecificDaysOfWeek.php new file mode 100644 index 0000000..f2cb866 --- /dev/null +++ b/src/Parts/DaysOfWeek/SpecificDaysOfWeek.php @@ -0,0 +1,12 @@ +setPart( + $this->index(), + $this->interval ? '*/' . $this->interval : '*' + ); + } +} diff --git a/src/Parts/Hours/BetweenHours.php b/src/Parts/Hours/BetweenHours.php new file mode 100644 index 0000000..f2896c0 --- /dev/null +++ b/src/Parts/Hours/BetweenHours.php @@ -0,0 +1,12 @@ +setPart(CronExpression::MONTH, '1-12/3'); + } +} diff --git a/src/Parts/Months/SpecificMonths.php b/src/Parts/Months/SpecificMonths.php new file mode 100644 index 0000000..c8e542e --- /dev/null +++ b/src/Parts/Months/SpecificMonths.php @@ -0,0 +1,12 @@ + */ + private array $values; + + public function __construct(int ...$values) + { + $this->values = $values; + } + + abstract public function index(): int; + + public function updateExpression(CronExpression $expression): void + { + $expression->setPart( + $this->index(), + implode(',', $this->values) + ); + } +} diff --git a/src/Traits/Days.php b/src/Traits/Days.php index 87767e7..78b9a65 100644 --- a/src/Traits/Days.php +++ b/src/Traits/Days.php @@ -4,20 +4,26 @@ namespace Butschster\CronExpression\Traits; +use Butschster\CronExpression\Parts\Days\LastDayOfMonth; +use Butschster\CronExpression\Parts\Days\LastWeekday; +use Butschster\CronExpression\Parts\Hours\SpecificHours; +use Butschster\CronExpression\Parts\Minutes\SpecificMinutes; + trait Days { public function daily(int ...$hours): self { - $hours = $hours ? implode(',', $hours) : 0; + $hours = $hours ?: [0]; - return $this->minutes(0) - ->hours($hours); + return $this->hourly()->set(new SpecificHours(...$hours)); } public function dailyAt(int $hour, int $minute = 0): self { - return $this->minutes($minute) - ->hours($hour); + return $this->set( + new SpecificMinutes($minute), + new SpecificHours($hour) + ); } public function twiceDaily($first = 1, $second = 13): self @@ -27,7 +33,17 @@ public function twiceDaily($first = 1, $second = 13): self public function twiceDailyAt($first = 1, $second = 13, $minute = 0): self { - return $this->daily($first, $second) - ->minutes($minute); + return $this->daily($first, $second)->set(new SpecificMinutes($minute)); + } + + public function lastDayOfMonth(int $hour = 0, int $minute = 0): self + { + return $this->dailyAt($hour, $minute)->set(new LastDayOfMonth()); + } + + public function lastWeekdayOfMonth(int $hour = 0, int $minute = 0): self + { + return $this->dailyAt($hour, $minute) + ->set(new LastWeekday()); } } diff --git a/src/Traits/Hours.php b/src/Traits/Hours.php index 3317112..da18567 100644 --- a/src/Traits/Hours.php +++ b/src/Traits/Hours.php @@ -4,46 +4,43 @@ namespace Butschster\CronExpression\Traits; +use Butschster\CronExpression\Parts\Hours\EveryHour; +use Butschster\CronExpression\Parts\Minutes\SpecificMinutes; + trait Hours { public function hourly(): self { - return $this->minutes(0); + return $this->set(new SpecificMinutes(0)); } public function hourlyAt(int ...$minute): self { - return $this->minutes(...$minute); + return $this->set(new SpecificMinutes(...$minute)); + } + + public function everyHours(?int $hour = null): self + { + return $this->hourly()->set(new EveryHour($hour)); } public function everyTwoHours(): self { - return $this->minutes(0) - ->hours('*/2'); + return $this->everyHours(2); } public function everyThreeHours(): self { - return $this->minutes(0) - ->hours('*/3'); + return $this->everyHours(3); } public function everyFourHours(): self { - return $this->minutes(0) - ->hours('*/4'); + return $this->everyHours(4); } public function everySixHours(): self { - return $this->minutes(0) - ->hours('*/6'); - } - - public function hours(string | int ...$hours): self - { - return self::create( - $this->expression->hours(...$hours) - ); + return $this->everyHours(6); } } diff --git a/src/Traits/Minutes.php b/src/Traits/Minutes.php index 4708eab..f514592 100644 --- a/src/Traits/Minutes.php +++ b/src/Traits/Minutes.php @@ -4,11 +4,14 @@ namespace Butschster\CronExpression\Traits; +use Butschster\CronExpression\Parts\Minutes\EveryMinute; +use Butschster\CronExpression\Parts\Minutes\SpecificMinutes; + trait Minutes { - public function everyMinute(): self + public function everyMinute(?int $minute = null): self { - return $this->minutes('*'); + return $this->set(new EveryMinute($minute)); } public function everyEvenMinute(): self @@ -18,43 +21,36 @@ public function everyEvenMinute(): self public function everyTwoMinutes(): self { - return $this->minutes('*/2'); + return $this->everyMinute(2); } public function everyThreeMinutes(): self { - return $this->minutes('*/3'); + return $this->everyMinute(3); } public function everyFourMinutes(): self { - return $this->minutes('*/4'); + return $this->everyMinute(4); } public function everyFiveMinutes(): self { - return $this->minutes('*/5'); + return $this->everyMinute(5); } public function everyTenMinutes(): self { - return $this->minutes('*/10'); + return $this->everyMinute(10); } public function everyFifteenMinutes(): self { - return $this->minutes('*/15'); + return $this->everyMinute(15); } public function everyThirtyMinutes(): self { - return $this->minutes(0, 30); - } - - public function minutes(string | int ...$minutes): self - { - return self::create( - $this->expression->minutes(...$minutes) - ); + return $this->set(new SpecificMinutes(0, 30)); } } diff --git a/src/Traits/Months.php b/src/Traits/Months.php index 6fc5f98..4cfa5ae 100644 --- a/src/Traits/Months.php +++ b/src/Traits/Months.php @@ -4,16 +4,15 @@ namespace Butschster\CronExpression\Traits; -use DateTime; -use DateTimeInterface; +use Butschster\CronExpression\Parts\Days\SpecificDays; +use Butschster\CronExpression\Parts\DaysOfWeek\LastDayOwWeek; +use Butschster\CronExpression\Parts\DaysOfWeek\NthDayOfWeek; trait Months { - public function daysOfMonth(string | int ...$days): self + public function daysOfMonth(int ...$days): self { - return self::create( - $this->expression->daysOfMonth(...$days) - ); + return $this->set(new SpecificDays(...$days)); } public function monthly(int $hour = 0, int $minute = 0): self @@ -35,11 +34,13 @@ public function twiceMonthly(int $first = 1, int $second = 16, int $hour = 0, in ->daysOfMonth($first, $second); } - public function lastDayOfMonth(int $hour = 0, int $minute = 0, ?DateTimeInterface $time = null): self + public function lastDayOfWeekEveryMonth(int $dayOfWeek = self::MONDAY, int $hour = 0, int $minute = 0): self { - $time = $time ?? new DateTime(); + return $this->dailyAt($hour, $minute)->set(new LastDayOwWeek($dayOfWeek)); + } - return $this->dailyAt($hour, $minute) - ->daysOfMonth((int)$time->format('t')); + public function nthDayOfWeekEveryMonth(int $dayOfWeek = self::MONDAY, int $nth = 1, int $hour = 0, int $minute = 0): self + { + return $this->dailyAt($hour, $minute)->set(new NthDayOfWeek($dayOfWeek, $nth)); } } diff --git a/src/Traits/Weeks.php b/src/Traits/Weeks.php index 16bd5b8..753efa3 100644 --- a/src/Traits/Weeks.php +++ b/src/Traits/Weeks.php @@ -4,31 +4,31 @@ namespace Butschster\CronExpression\Traits; +use Butschster\CronExpression\Parts\DaysOfWeek\BetweenDayOfWeek; +use Butschster\CronExpression\Parts\DaysOfWeek\SpecificDaysOfWeek; + trait Weeks { - public function daysOfWeek(string | int...$dayOfWeek): self + public function daysOfWeek(int...$dayOfWeek): self { - return self::create( - $this->expression->daysOfWeek(...$dayOfWeek) - ); + return $this->set(new SpecificDaysOfWeek(...$dayOfWeek)); } public function weekly(int ...$dayOfWeek): self { $dayOfWeek = $dayOfWeek ?: [0]; - return $this->daily() - ->daysOfWeek(...$dayOfWeek); + return $this->daily()->daysOfWeek(...$dayOfWeek); } public function weekdays(): self { - return $this->daysOfWeek(self::MONDAY . '-' . self::FRIDAY); + return $this->set(new BetweenDayOfWeek(self::MONDAY, self::FRIDAY)); } public function weekends(): self { - return $this->daysOfWeek(self::SATURDAY . ',' . self::SUNDAY); + return $this->daysOfWeek(self::SATURDAY, self::SUNDAY); } public function mondays(): self diff --git a/src/Traits/Years.php b/src/Traits/Years.php index ec95e90..2fddff9 100644 --- a/src/Traits/Years.php +++ b/src/Traits/Years.php @@ -4,25 +4,26 @@ namespace Butschster\CronExpression\Traits; +use Butschster\CronExpression\Parts\Months\Quarterly; +use Butschster\CronExpression\Parts\Months\SpecificMonths; + trait Years { - public function months(string | int ...$months): self + public function months(int ...$months): self { - return self::create( - $this->expression->months(...$months) - ); + $months = $months ?: [self::JAN]; + + return $this->set(new SpecificMonths(...$months)); } public function quarterly(): self { - return $this->monthly() - ->months('1-12/3'); + return $this->monthly()->set(new Quarterly()); } public function yearly(int ...$months): self { - return $this->monthly() - ->months(...$months); + return $this->monthly()->months(...$months); } public function yearlyOn(int $month = self::JAN, int $dayOfMonth = 1, int $hour = 0, int $minute = 0): self diff --git a/tests/GeneratorTest.php b/tests/GeneratorTest.php index 17fbdf2..950470b 100644 --- a/tests/GeneratorTest.php +++ b/tests/GeneratorTest.php @@ -5,6 +5,15 @@ namespace Butschster\CronExpression\Tests; use Butschster\CronExpression\Generator; +use Butschster\CronExpression\Parts\Days\SpecificDays; +use Butschster\CronExpression\Parts\DaysOfWeek\SpecificDaysOfWeek; +use Butschster\CronExpression\Parts\Hours\BetweenHours; +use Butschster\CronExpression\Parts\Hours\EveryHour; +use Butschster\CronExpression\Parts\Hours\SpecificHours; +use Butschster\CronExpression\Parts\Minutes\BetweenMinutes; +use Butschster\CronExpression\Parts\Minutes\EveryMinute; +use Butschster\CronExpression\Parts\Minutes\SpecificMinutes; +use Butschster\CronExpression\Parts\Months\SpecificMonths; use DateTime; use InvalidArgumentException; @@ -39,9 +48,9 @@ public function testEveryXMinutes() public function testSetsMinutes() { - $this->assertExpression('15,30,45 * * * *', $this->generator->minutes(15, 30, 45)); - $this->assertExpression('*/3 * * * *', $this->generator->minutes('*/3')); - $this->assertExpression('10-30 * * * *', $this->generator->minutes('10-30')); + $this->assertExpression('15,30,45 * * * *', $this->generator->set(new SpecificMinutes(15, 30, 45))); + $this->assertExpression('*/3 * * * *', $this->generator->set(new EveryMinute(3))); + $this->assertExpression('10-30 * * * *', $this->generator->set(new BetweenMinutes(10, 30))); } public function testInvalidMinutesShouldThrowAnException() @@ -49,7 +58,7 @@ public function testInvalidMinutesShouldThrowAnException() $this->expectException(InvalidArgumentException::class); $this->expectErrorMessage('Invalid CRON field value 61 at position 0'); - $this->generator->minutes(61); + $this->generator->set(new SpecificMinutes(61)); } public function testHourly() @@ -74,10 +83,11 @@ public function testEveryXHours() public function testSetsHours() { - $this->assertExpression('* 1,2,3 * * *', $this->generator->hours(1, 2, 3)); - $this->assertExpression('* */3 * * *', $this->generator->hours('*/3')); + $this->assertExpression('* 1,2,3 * * *', $this->generator->set(new SpecificHours(1, 2, 3))); + $this->assertExpression('* */3 * * *', $this->generator->set(new EveryHour(3))); + $this->assertExpression('* 3-6 * * *', $this->generator->set(new BetweenHours(3, 6))); - $this->assertExpression('15 1,2,3 * * *', $this->generator->hourlyAt(15)->hours(1, 2, 3)); + $this->assertExpression('15 1,2,3 * * *', $this->generator->hourlyAt(15)->set(new SpecificHours(1, 2, 3))); } public function testInvalidHoursShouldThrowAnException() @@ -85,7 +95,7 @@ public function testInvalidHoursShouldThrowAnException() $this->expectException(InvalidArgumentException::class); $this->expectErrorMessage('Invalid CRON field value 25 at position 1'); - $this->generator->hours(25); + $this->generator->set(new SpecificHours(25)); } public function testDaily() @@ -190,12 +200,31 @@ public function testMonthlyOn() public function testLastDayOfMonth() { - $currentDate = new DateTime(); - $this->assertExpression('0 0 ' . $currentDate->format('t') . ' * *', $this->generator->lastDayOfMonth()); - $this->assertExpression('31 12 ' . $currentDate->format('t') . ' * *', $this->generator->lastDayOfMonth(12, 31)); + $this->assertExpression('0 0 L * *', $this->generator->lastDayOfMonth()); + $this->assertExpression('31 12 L * *', $this->generator->lastDayOfMonth(12, 31)); + } + + public function testLastWeekdayOfMonth() + { + $this->assertExpression('0 0 LW * *', $this->generator->lastWeekdayOfMonth()); + $this->assertExpression('31 12 LW * *', $this->generator->lastWeekdayOfMonth(12, 31)); + } - $date = new DateTime('2021-02-05'); - $this->assertExpression('31 12 28 * *', $this->generator->lastDayOfMonth(12, 31, $date)); + public function testLastDayOfWeekEveryMonth() + { + $this->assertExpression('0 0 * * 1L', $this->generator->lastDayOfWeekEveryMonth()); + $this->assertExpression('0 0 * * 5L', $this->generator->lastDayOfWeekEveryMonth(Generator::FRIDAY)); + $this->assertExpression('0 12 * * 5L', $this->generator->lastDayOfWeekEveryMonth(Generator::FRIDAY, 12)); + $this->assertExpression('30 12 * * 5L', $this->generator->lastDayOfWeekEveryMonth(Generator::FRIDAY, 12, 30)); + } + + public function testNthDayOfWeekEveryMonth() + { + $this->assertExpression('0 0 * * 1#1', $this->generator->nthDayOfWeekEveryMonth()); + $this->assertExpression('0 0 * * 5#1', $this->generator->nthDayOfWeekEveryMonth(Generator::FRIDAY)); + $this->assertExpression('0 0 * * 5#3', $this->generator->nthDayOfWeekEveryMonth(Generator::FRIDAY, 3)); + $this->assertExpression('0 12 * * 5#3', $this->generator->nthDayOfWeekEveryMonth(Generator::FRIDAY, 3, 12)); + $this->assertExpression('39 12 * * 5#3', $this->generator->nthDayOfWeekEveryMonth(Generator::FRIDAY, 3, 12, 39)); } public function testQuarterly() @@ -241,4 +270,19 @@ public function testMixedExpression() ->everyMinute() ); } + + public function testMixedExpressionFromParts() + { + $this->assertExpression( + '* */2 5,10,15,20,25,30 3,6,9,12 1,3,5,0', + $this->generator + ->set( + new SpecificMonths(Generator::MAR, Generator::JUN, Generator::SEP, Generator::DEC), + new SpecificDays(5, 10, 15, 20, 25, 30), + new SpecificDaysOfWeek(Generator::MONDAY, Generator::WEDNESDAY, Generator::FRIDAY, Generator::SUNDAY), + new EveryHour(2), + new EveryMinute() + ) + ); + } }