Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
99.34% covered (success)
99.34%
303 / 305
96.08% covered (success)
96.08%
49 / 51
CRAP
0.00% covered (danger)
0.00%
0 / 1
Cron
99.34% covered (success)
99.34%
303 / 305
96.08% covered (success)
96.08%
49 / 51
135
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 create
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setBuffer
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getBuffer
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasSeconds
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSeconds
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMinutes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHours
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDaysOfTheMonth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMonths
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDaysOfTheWeek
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 schedule
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
3
 getSchedule
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasSchedule
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 updateSchedule
96.55% covered (success)
96.55%
28 / 29
0.00% covered (danger)
0.00%
0 / 1
14
 everySecond
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 every5Seconds
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 every10Seconds
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 every15Seconds
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 every20Seconds
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 every30Seconds
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 seconds
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
4
 everyMinute
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 every5Minutes
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 every10Minutes
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 every15Minutes
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 every20Minutes
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 every30Minutes
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 minutes
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 hours
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
5
 hourly
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 daily
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 dailyAt
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 weekly
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 monthly
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 quarterly
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 yearly
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
 weekdays
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 weekends
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 sundays
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 mondays
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 tuesdays
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 wednesdays
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 thursdays
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 fridays
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 saturdays
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 between
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 render
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 evaluate
100.00% covered (success)
100.00%
47 / 47
100.00% covered (success)
100.00%
1 / 1
43
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 evaluateExpression
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
5.03
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Queue\Process;
15
16/**
17 * Cron class
18 *
19 * @category   Pop
20 * @package    Pop\Queue
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    2.0.0
25 */
26class Cron
27{
28
29    /**
30     * Schedule string
31     * @var ?string
32     */
33    protected ?string $schedule = null;
34
35    /**
36     * Seconds
37     *  - Not a standard cron unit of time. The smallest time interval supported by cron is 1 minute.
38     *    This is to support time intervals less than minute and down to 1 second.
39     * @var array
40     */
41    protected array $seconds = [];
42
43    /**
44     * Minutes
45     * @var array
46     */
47    protected array $minutes = [];
48
49    /**
50     * Hours
51     * @var array
52     */
53    protected array $hours = [];
54
55    /**
56     * Days of the month
57     * @var array
58     */
59    protected array $daysOfTheMonth = [];
60
61    /**
62     * Months
63     * @var array
64     */
65    protected array $months = [];
66
67    /**
68     * Days of the week
69     * @var array
70     */
71    protected array $daysOfTheWeek = [];
72
73    /**
74     * Time buffer
75     * @var int
76     */
77    protected int $buffer = 0;
78
79    /**
80     * Constructor
81     *
82     * Instantiate the cron  object
83     *
84     * @param  ?string $schedule
85     * @param  int     $buffer
86     */
87    public function __construct(?string $schedule = null, int $buffer = 0)
88    {
89        if ($schedule !== null) {
90            $this->schedule($schedule);
91        }
92        $this->setBuffer($buffer);
93    }
94
95    /**
96     * Factory
97     *
98     * @param  ?string $schedule
99     * @return Cron
100     */
101    public static function create(?string $schedule = null): Cron
102    {
103        return new self($schedule);
104    }
105
106    /**
107     * Set buffer
108     *
109     * @param  int $buffer
110     * @return Cron
111     */
112    public function setBuffer(int $buffer): Cron
113    {
114        $this->buffer = $buffer;
115        return $this;
116    }
117
118    /**
119     * Get buffer
120     *
121     * @return int
122     */
123    public function getBuffer(): int
124    {
125        return $this->buffer;
126    }
127
128    /**
129     * Has seconds
130     *
131     * @return bool
132     */
133    public function hasSeconds(): bool
134    {
135        return !empty($this->seconds);
136    }
137
138    /**
139     * Get seconds
140     *
141     * @return array
142     */
143    public function getSeconds(): array
144    {
145        return $this->seconds;
146    }
147
148    /**
149     * Get minutes
150     *
151     * @return array
152     */
153    public function getMinutes(): array
154    {
155        return $this->minutes;
156    }
157
158    /**
159     * Get hours
160     *
161     * @return array
162     */
163    public function getHours(): array
164    {
165        return $this->hours;
166    }
167
168    /**
169     * Get days of the month
170     *
171     * @return array
172     */
173    public function getDaysOfTheMonth(): array
174    {
175        return $this->daysOfTheMonth;
176    }
177
178    /**
179     * Get months
180     *
181     * @return array
182     */
183    public function getMonths(): array
184    {
185        return $this->months;
186    }
187
188    /**
189     * Get days of the week
190     *
191     * @return array
192     */
193    public function getDaysOfTheWeek(): array
194    {
195        return $this->daysOfTheWeek;
196    }
197
198    /**
199     * Set cron schedule
200     *
201     *   min  hour  dom  month  dow
202     *    *    *     *     *     *
203     *
204     *      - OR non-standard -
205     *
206     *    sec  min  hour  dom  month  dow
207     *     *    *    *     *     *     *
208     *
209     * @param  string $schedule
210     * @return Cron
211     */
212    public function schedule(string $schedule): Cron
213    {
214        $schedule = preg_replace('!\s+!', ' ', trim($schedule));
215
216        if (substr_count($schedule, ' ') >= 4) {
217            $this->schedule = $schedule;
218
219            if (substr_count($schedule, ' ') == 5) {
220                list($sec, $min, $hour, $dom, $month, $dow) = explode(' ', $this->schedule);
221                $this->seconds = [$sec];
222            } else {
223                list($min, $hour, $dom, $month, $dow) = explode(' ', $this->schedule);
224            }
225
226            $this->minutes        = [$min];
227            $this->hours          = [$hour];
228            $this->daysOfTheMonth = [$dom];
229            $this->months         = [$month];
230            $this->daysOfTheWeek  = [$dow];
231        }
232
233        return $this;
234    }
235
236    /**
237     * Get schedule string
238     *
239     * @return ?string
240     */
241    public function getSchedule(): ?string
242    {
243        return $this->schedule;
244    }
245
246    /**
247     * Has schedule string
248     *
249     * @return bool
250     */
251    public function hasSchedule(): bool
252    {
253        return ($this->schedule !== null);
254    }
255
256    /**
257     * Update cron schedule
258     * @return Cron
259     */
260    public function updateSchedule(): Cron
261    {
262        $schedule = [];
263
264        // Minutes
265        if (count($this->seconds) > 1) {
266            $schedule[] = implode(',', $this->seconds);
267        } else if (isset($this->seconds[0])) {
268            $schedule[] = $this->seconds[0];
269        }
270
271        // Minutes
272        if (count($this->minutes) > 1) {
273            $schedule[] = implode(',', $this->minutes);
274        } else if (isset($this->minutes[0])) {
275            $schedule[] = $this->minutes[0];
276        }
277
278        // Hours
279        if (count($this->hours) > 1) {
280            $schedule[] = implode(',', $this->hours);
281        } else if (isset($this->hours[0])) {
282            $schedule[] = $this->hours[0];
283        }
284
285        // DOM
286        if (count($this->daysOfTheMonth) > 1) {
287            $schedule[] = implode(',', $this->daysOfTheMonth);
288        } else if (isset($this->daysOfTheMonth[0])) {
289            $schedule[] = $this->daysOfTheMonth[0];
290        }
291
292        // Months
293        if (count($this->months) > 1) {
294            $schedule[] = implode(',', $this->months);
295        } else if (isset($this->months[0])) {
296            $schedule[] = $this->months[0];
297        }
298
299        // DOW
300        if (count($this->daysOfTheWeek) > 1) {
301            $schedule[] = implode(',', $this->daysOfTheWeek);
302        } else if (isset($this->daysOfTheWeek[0])) {
303            $schedule[] = $this->daysOfTheWeek[0];
304        }
305
306        if (empty($schedule)) {
307            throw new Exception('Error: The cron schedule has not been set.');
308        }
309
310        $this->schedule = implode(' ', $schedule);
311        return $this;
312    }
313
314    /**
315     * Set job schedule to every second
316     *
317     * @return Cron
318     */
319    public function everySecond(): Cron
320    {
321        $this->seconds        = ['*'];
322        $this->minutes        = ['*'];
323        $this->hours          = ['*'];
324        $this->daysOfTheMonth = ['*'];
325        $this->months         = ['*'];
326        $this->daysOfTheWeek  = ['*'];
327
328        return $this->updateSchedule();
329    }
330
331    /**
332     * Set job schedule to every 5 seconds
333     *
334     * @return Cron
335     */
336    public function every5Seconds(): Cron
337    {
338        $this->seconds        = ['*/5'];
339        $this->minutes        = ['*'];
340        $this->hours          = ['*'];
341        $this->daysOfTheMonth = ['*'];
342        $this->months         = ['*'];
343        $this->daysOfTheWeek  = ['*'];
344
345        return $this->updateSchedule();
346    }
347
348    /**
349     * Set job schedule to every 10 seconds
350     *
351     * @return Cron
352     */
353    public function every10Seconds(): Cron
354    {
355        $this->seconds        = ['*/10'];
356        $this->minutes        = ['*'];
357        $this->hours          = ['*'];
358        $this->daysOfTheMonth = ['*'];
359        $this->months         = ['*'];
360        $this->daysOfTheWeek  = ['*'];
361
362        return $this->updateSchedule();
363    }
364
365    /**
366     * Set job schedule to every 15 seconds
367     *
368     * @return Cron
369     */
370    public function every15Seconds(): Cron
371    {
372        $this->seconds        = ['*/15'];
373        $this->minutes        = ['*'];
374        $this->hours          = ['*'];
375        $this->daysOfTheMonth = ['*'];
376        $this->months         = ['*'];
377        $this->daysOfTheWeek  = ['*'];
378
379        return $this->updateSchedule();
380    }
381
382    /**
383     * Set job schedule to every 20 seconds
384     *
385     * @return Cron
386     */
387    public function every20Seconds(): Cron
388    {
389        $this->seconds        = ['*/20'];
390        $this->minutes        = ['*'];
391        $this->hours          = ['*'];
392        $this->daysOfTheMonth = ['*'];
393        $this->months         = ['*'];
394        $this->daysOfTheWeek  = ['*'];
395
396        return $this->updateSchedule();
397    }
398
399    /**
400     * Set job schedule to every 30 seconds
401     *
402     * @return Cron
403     */
404    public function every30Seconds(): Cron
405    {
406        $this->seconds        = ['*/30'];
407        $this->minutes        = ['*'];
408        $this->hours          = ['*'];
409        $this->daysOfTheMonth = ['*'];
410        $this->months         = ['*'];
411        $this->daysOfTheWeek  = ['*'];
412
413        return $this->updateSchedule();
414    }
415
416    /**
417     * Set job schedule to by specific seconds
418     *
419     * @param  mixed $seconds
420     * @return Cron
421     */
422    public function seconds(mixed $seconds): Cron
423    {
424        if (is_string($seconds) && (str_contains($seconds, ','))) {
425            $seconds = explode(',' , $seconds);
426        } else if (is_numeric($seconds)) {
427            $seconds = [(int)$seconds];
428        } else {
429            $seconds = [$seconds];
430        }
431
432        $this->seconds        = array_map('trim', $seconds);
433        $this->minutes        = ['*'];
434        $this->hours          = ['*'];
435        $this->daysOfTheMonth = ['*'];
436        $this->months         = ['*'];
437        $this->daysOfTheWeek  = ['*'];
438
439        return $this->updateSchedule();
440    }
441
442    /**
443     * Set job schedule to every minute
444     *
445     * @return Cron
446     */
447    public function everyMinute(): Cron
448    {
449        $this->minutes        = ['*'];
450        $this->hours          = ['*'];
451        $this->daysOfTheMonth = ['*'];
452        $this->months         = ['*'];
453        $this->daysOfTheWeek  = ['*'];
454
455        return $this->updateSchedule();
456    }
457
458    /**
459     * Set job schedule to every 5 minutes
460     *
461     * @return Cron
462     */
463    public function every5Minutes(): Cron
464    {
465        $this->minutes        = ['*/5'];
466        $this->hours          = ['*'];
467        $this->daysOfTheMonth = ['*'];
468        $this->months         = ['*'];
469        $this->daysOfTheWeek  = ['*'];
470
471        return $this->updateSchedule();
472    }
473
474    /**
475     * Set job schedule to every 10 minutes
476     *
477     * @return Cron
478     */
479    public function every10Minutes(): Cron
480    {
481        $this->minutes        = ['*/10'];
482        $this->hours          = ['*'];
483        $this->daysOfTheMonth = ['*'];
484        $this->months         = ['*'];
485        $this->daysOfTheWeek  = ['*'];
486
487        return $this->updateSchedule();
488    }
489
490    /**
491     * Set job schedule to every 15 minutes
492     *
493     * @return Cron
494     */
495    public function every15Minutes(): Cron
496    {
497        $this->minutes        = ['*/15'];
498        $this->hours          = ['*'];
499        $this->daysOfTheMonth = ['*'];
500        $this->months         = ['*'];
501        $this->daysOfTheWeek  = ['*'];
502
503        return $this->updateSchedule();
504    }
505
506    /**
507     * Set job schedule to every 20 minutes
508     *
509     * @return Cron
510     */
511    public function every20Minutes(): Cron
512    {
513        $this->minutes        = ['*/20'];
514        $this->hours          = ['*'];
515        $this->daysOfTheMonth = ['*'];
516        $this->months         = ['*'];
517        $this->daysOfTheWeek  = ['*'];
518
519        return $this->updateSchedule();
520    }
521
522    /**
523     * Set job schedule to every 30 minutes
524     *
525     * @return Cron
526     */
527    public function every30Minutes(): Cron
528    {
529        $this->minutes        = ['*/30'];
530        $this->hours          = ['*'];
531        $this->daysOfTheMonth = ['*'];
532        $this->months         = ['*'];
533        $this->daysOfTheWeek  = ['*'];
534
535        return $this->updateSchedule();
536    }
537
538    /**
539     * Set job schedule to by specific minutes
540     *
541     * @param  mixed $minutes
542     * @return Cron
543     */
544    public function minutes(mixed $minutes): Cron
545    {
546        if (is_string($minutes) && (str_contains($minutes, ','))) {
547            $minutes = explode(',' , $minutes);
548        } else if (is_numeric($minutes)) {
549            $minutes = [(int)$minutes];
550        } else {
551            $minutes = [$minutes];
552        }
553
554        $this->minutes        = array_map('trim', $minutes);
555        $this->hours          = ['*'];
556        $this->daysOfTheMonth = ['*'];
557        $this->months         = ['*'];
558        $this->daysOfTheWeek  = ['*'];
559
560        return $this->updateSchedule();
561    }
562
563    /**
564     * Set job schedule to by specific hours
565     *
566     * @param  mixed $hours
567     * @param  mixed $minutes
568     * @return Cron
569     */
570    public function hours(mixed $hours, mixed $minutes = null): Cron
571    {
572        if ($minutes !== null) {
573            $this->minutes($minutes);
574        } else {
575            $this->minutes = [0];
576        }
577
578        if (is_string($hours) && (str_contains($hours, ','))) {
579            $hours = explode(',' , $hours);
580        } else if (is_numeric($hours)) {
581            $hours = [(int)$hours];
582        } else {
583            $hours = [$hours];
584        }
585
586        $this->hours          = array_map('trim', $hours);
587        $this->daysOfTheMonth = ['*'];
588        $this->months         = ['*'];
589        $this->daysOfTheWeek  = ['*'];
590
591        return $this->updateSchedule();
592    }
593
594    /**
595     * Set job schedule to hourly
596     *
597     * @param  mixed $minutes
598     * @return Cron
599     */
600    public function hourly(mixed $minutes = null): Cron
601    {
602        if ($minutes !== null) {
603            $this->minutes($minutes);
604        } else {
605            $this->minutes = [0];            
606        }
607
608        $this->hours          = ['*'];
609        $this->daysOfTheMonth = ['*'];
610        $this->months         = ['*'];
611        $this->daysOfTheWeek  = ['*'];
612
613        return $this->updateSchedule();
614    }
615
616    /**
617     * Set job schedule to daily (alias to hours)
618     *
619     * @param  mixed $hours
620     * @param  mixed $minutes
621     * @return Cron
622     */
623    public function daily(mixed $hours, mixed $minutes = null): Cron
624    {
625        return $this->hours($hours, $minutes);
626    }
627
628    /**
629     * Set job schedule to daily at specific time, i.e. 14:30
630     *
631     * @param  string $time
632     * @return Cron
633     */
634    public function dailyAt(string $time): Cron
635    {
636        list($hour, $minute) = explode(':', $time);
637        $this->daily($hour, $minute);
638        return $this;
639    }
640
641    /**
642     * Set job schedule to weekly
643     *
644     * @param  mixed $day
645     * @param  mixed $hours
646     * @param  mixed $minutes
647     * @return Cron
648     */
649    public function weekly(mixed $day, mixed $hours = null, mixed $minutes = null): Cron
650    {
651        if ($minutes !== null) {
652            $this->minutes($minutes);
653        } else {
654            $this->minutes = [0];
655        }
656
657        if ($hours !== null) {
658            $this->hours = [$hours];
659        } else {
660            $this->hours = [0];
661        }
662
663        $this->daysOfTheMonth = ['*'];
664        $this->months         = ['*'];
665        $this->daysOfTheWeek  = [$day];
666
667        return $this->updateSchedule();
668    }
669
670    /**
671     * Set job schedule to monthly
672     *
673     * @param  mixed $day
674     * @param  mixed $hours
675     * @param  mixed $minutes
676     * @return Cron
677     */
678    public function monthly(mixed $day, mixed $hours = null, mixed $minutes = null): Cron
679    {
680        if ($minutes !== null) {
681            $this->minutes($minutes);
682        } else {
683            $this->minutes = [0];
684        }
685
686        if ($hours !== null) {
687            $this->hours = [$hours];
688        } else {
689            $this->hours = [0];
690        }
691
692        $this->daysOfTheMonth = [$day];
693        $this->months         = ['*'];
694        $this->daysOfTheWeek  = ['*'];
695
696        return $this->updateSchedule();
697    }
698
699    /**
700     * Set job schedule to quarterly
701     *
702     * @param  mixed $hours
703     * @param  mixed $minutes
704     * @return Cron
705     */
706    public function quarterly(mixed $hours = null, mixed $minutes = null): Cron
707    {
708        if ($minutes !== null) {
709            $this->minutes($minutes);
710        } else {
711            $this->minutes = [0];
712        }
713
714        if ($hours !== null) {
715            $this->hours = [$hours];
716        } else {
717            $this->hours = [0];
718        }
719
720        $this->daysOfTheMonth = ['1'];
721        $this->months         = [1,4,7,10];
722        $this->daysOfTheWeek  = ['*'];
723
724        return $this->updateSchedule();
725    }
726
727    /**
728     * Set job schedule to yearly
729     *
730     * @param  bool $endOfYear
731     * @param  mixed $hours
732     * @param  mixed $minutes
733     * @return Cron
734     */
735    public function yearly(bool $endOfYear = false, mixed $hours = null, mixed $minutes = null): Cron
736    {
737        if ($minutes !== null) {
738            $this->minutes($minutes);
739        } else {
740            $this->minutes = [0];
741        }
742
743        if ($hours !== null) {
744            $this->hours = [$hours];
745        } else {
746            $this->hours = [0];
747        }
748
749        $this->daysOfTheMonth = ($endOfYear) ? ['31'] : ['1'];
750        $this->months         = ($endOfYear) ? ['12'] : ['1'];
751        $this->daysOfTheWeek  = ['*'];
752
753        return $this->updateSchedule();
754    }
755
756    /**
757     * Set job schedule to weekdays
758     *
759     * @return Cron
760     */
761    public function weekdays(): Cron
762    {
763        $this->daysOfTheWeek = ['1', '2', '3', '4', '5'];
764        return $this->updateSchedule();
765    }
766
767    /**
768     * Set job schedule to weekends
769     *
770     * @return Cron
771     */
772    public function weekends(): Cron
773    {
774        $this->daysOfTheWeek = ['0', '6'];
775        return $this->updateSchedule();
776    }
777
778    /**
779     * Set job schedule to Sundays
780     *
781     * @return Cron
782     */
783    public function sundays(): Cron
784    {
785        $this->daysOfTheWeek = ['0'];
786        return $this->updateSchedule();
787    }
788
789    /**
790     * Set job schedule to Mondays
791     *
792     * @return Cron
793     */
794    public function mondays(): Cron
795    {
796        $this->daysOfTheWeek = ['1'];
797        return $this->updateSchedule();
798    }
799
800    /**
801     * Set job schedule to Tuesdays
802     *
803     * @return Cron
804     */
805    public function tuesdays(): Cron
806    {
807        $this->daysOfTheWeek = ['2'];
808        return $this->updateSchedule();
809    }
810
811    /**
812     * Set job schedule to Wednesdays
813     *
814     * @return Cron
815     */
816    public function wednesdays(): Cron
817    {
818        $this->daysOfTheWeek = ['3'];
819        return $this->updateSchedule();
820    }
821
822    /**
823     * Set job schedule to Thursdays
824     *
825     * @return Cron
826     */
827    public function thursdays(): Cron
828    {
829        $this->daysOfTheWeek = ['4'];
830        return $this->updateSchedule();
831    }
832
833    /**
834     * Set job schedule to Fridays
835     *
836     * @return Cron
837     */
838    public function fridays(): Cron
839    {
840        $this->daysOfTheWeek = ['5'];
841        return $this->updateSchedule();
842    }
843
844    /**
845     * Set job schedule to Saturdays
846     *
847     * @return Cron
848     */
849    public function saturdays(): Cron
850    {
851        $this->daysOfTheWeek = ['6'];
852        return $this->updateSchedule();
853    }
854
855    /**
856     * Set job schedule to between two hours
857     *
858     * @param  int $start
859     * @param  int $end
860     * @return Cron
861     */
862    public function between(int $start, int $end): Cron
863    {
864        $this->hours = [$start . '-' . $end];
865        return $this->updateSchedule();
866    }
867
868    /**
869     * Render the cron schedule string
870     *
871     * @return string
872     */
873    public function render(): string
874    {
875        if (empty($this->schedule)) {
876            $this->updateSchedule();
877        }
878        return $this->schedule;
879    }
880
881    /**
882     * Evaluate the set cron schedule value against a time value
883     *
884     * $buffer = 0;      strict evaluation to the 00 second
885     * $buffer = 1-59;   gives up to a minute buffer to account for any delay in processing
886     * $buffer = -1;     disregards the seconds value for a loose evaluation
887     *
888     * @param  mixed $time
889     * @param  ?int  $buffer
890     * @throws Exception
891     * @return bool
892     */
893    public function evaluate(mixed $time = null, ?int $buffer = null): bool
894    {
895        if ($time === null) {
896            $time = time();
897        } else if (is_string($time)) {
898            $time = strtotime($time);
899            if ($time === false) {
900                throw new Exception('Error: That time value is not valid.');
901            }
902        }
903
904        if ($buffer !== null) {
905            $this->setBuffer($buffer);
906        }
907
908        $second        = (int)date('s', $time);
909        $minute        = (int)date('i', $time);
910        $hour          = (int)date('G', $time);
911        $dayOfTheMonth = (int)date('j', $time);
912        $month         = (int)date('n', $time);
913        $dayOfTheWeek  = (int)date('w', $time);
914        $secondsPassed = (in_array($second, $this->seconds) || ($this->seconds == ['*']));
915        $minutesPassed = (in_array($minute, $this->minutes) || ($this->minutes == ['*']));
916        $hoursPassed   = (in_array($hour, $this->hours) || ($this->hours == ['*']));
917        $domPassed     = (in_array($dayOfTheMonth, $this->daysOfTheMonth) || ($this->daysOfTheMonth == ['*']));
918        $monthPassed   = (in_array($month, $this->months) || ($this->months == ['*']));
919        $dowPassed     = (in_array($dayOfTheWeek, $this->daysOfTheWeek) || ($this->daysOfTheWeek == ['*']));
920
921        if ((!$secondsPassed) && (count($this->seconds) == 1) && is_string($this->seconds[0])) {
922            $secondsPassed = (($this->evaluateExpression($this->seconds[0], $second)));
923        }
924        if ((!$minutesPassed) && (count($this->minutes) == 1) && is_string($this->minutes[0])) {
925            $minutesPassed = (($this->evaluateExpression($this->minutes[0], $minute)));
926        }
927        if ((!$hoursPassed) && (count($this->hours) == 1) && is_string($this->hours[0])) {
928            $hoursPassed = (($this->evaluateExpression($this->hours[0], $hour)));
929        }
930        if ((!$domPassed) && (count($this->daysOfTheMonth) == 1) && is_string($this->daysOfTheMonth[0])) {
931            $domPassed = (($this->evaluateExpression($this->daysOfTheMonth[0], $dayOfTheMonth)));
932        }
933        if ((!$monthPassed) && (count($this->months) == 1) && is_string($this->months[0])) {
934            $monthPassed = (($this->evaluateExpression($this->months[0], $month)));
935        }
936        if ((!$dowPassed) && (count($this->daysOfTheWeek) == 1) && is_string($this->daysOfTheWeek[0])) {
937            $dowPassed = (($this->evaluateExpression($this->daysOfTheWeek[0], $dayOfTheWeek)));
938        }
939
940        if ($this->hasSeconds()) {
941            return (($dowPassed) &&
942                ($monthPassed) &&
943                ($domPassed) &&
944                ($hoursPassed) &&
945                ($minutesPassed) &&
946                ($secondsPassed));
947        } else {
948            // Check every minute schedule
949            if (($this->schedule == '* * * * *')) {
950                return (($this->buffer < 0) || ($second <= $this->buffer));
951            // Validate the schedule
952            } else {
953                return (($dowPassed) &&
954                    ($monthPassed) &&
955                    ($domPassed) &&
956                    ($hoursPassed) &&
957                    ($minutesPassed) &&
958                    (($this->buffer < 0) || ($second <= $this->buffer)));
959            }
960        }
961    }
962
963    /**
964     * To string method
965     *
966     * @return string
967     */
968    public function __toString(): string
969    {
970        return $this->render();
971    }
972
973    /**
974     * Determine if the value satisfies the schedule expression
975     *
976     * @param  string $expression
977     * @param  mixed  $value
978     * @return bool
979     */
980    protected function evaluateExpression(string $expression, mixed $value): bool
981    {
982        if (str_contains($expression, ',')) {
983            $values = array_map('trim', explode(',', $expression));
984            return in_array($value, $values);
985        } else if (str_contains($expression, '/')) {
986            $step = (int)substr($expression, (strpos($expression, '/') + 1));
987            return (($value % $step) == 0);
988        } else if (str_contains($expression, '-')) {
989            list($min, $max) = explode('-', $expression);
990            return (($value >= $min) && ($value <= $max));
991        }
992
993        return false;
994    }
995
996}