xref: /plugin/combo/vendor/dragonmantank/cron-expression/src/Cron/DayOfWeekField.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
1*37748cd8SNickeau<?php
2*37748cd8SNickeau
3*37748cd8SNickeaunamespace Cron;
4*37748cd8SNickeau
5*37748cd8SNickeauuse DateTime;
6*37748cd8SNickeauuse InvalidArgumentException;
7*37748cd8SNickeau
8*37748cd8SNickeau
9*37748cd8SNickeau/**
10*37748cd8SNickeau * Day of week field.  Allows: * / , - ? L #
11*37748cd8SNickeau *
12*37748cd8SNickeau * Days of the week can be represented as a number 0-7 (0|7 = Sunday)
13*37748cd8SNickeau * or as a three letter string: SUN, MON, TUE, WED, THU, FRI, SAT.
14*37748cd8SNickeau *
15*37748cd8SNickeau * 'L' stands for "last". It allows you to specify constructs such as
16*37748cd8SNickeau * "the last Friday" of a given month.
17*37748cd8SNickeau *
18*37748cd8SNickeau * '#' is allowed for the day-of-week field, and must be followed by a
19*37748cd8SNickeau * number between one and five. It allows you to specify constructs such as
20*37748cd8SNickeau * "the second Friday" of a given month.
21*37748cd8SNickeau */
22*37748cd8SNickeauclass DayOfWeekField extends AbstractField
23*37748cd8SNickeau{
24*37748cd8SNickeau    public function isSatisfiedBy(DateTime $date, $value)
25*37748cd8SNickeau    {
26*37748cd8SNickeau        if ($value == '?') {
27*37748cd8SNickeau            return true;
28*37748cd8SNickeau        }
29*37748cd8SNickeau
30*37748cd8SNickeau        // Convert text day of the week values to integers
31*37748cd8SNickeau        $value = $this->convertLiterals($value);
32*37748cd8SNickeau
33*37748cd8SNickeau        $currentYear = $date->format('Y');
34*37748cd8SNickeau        $currentMonth = $date->format('m');
35*37748cd8SNickeau        $lastDayOfMonth = $date->format('t');
36*37748cd8SNickeau
37*37748cd8SNickeau        // Find out if this is the last specific weekday of the month
38*37748cd8SNickeau        if (strpos($value, 'L')) {
39*37748cd8SNickeau            $weekday = str_replace('7', '0', substr($value, 0, strpos($value, 'L')));
40*37748cd8SNickeau            $tdate = clone $date;
41*37748cd8SNickeau            $tdate->setDate($currentYear, $currentMonth, $lastDayOfMonth);
42*37748cd8SNickeau            while ($tdate->format('w') != $weekday) {
43*37748cd8SNickeau                $tdateClone = new DateTime();
44*37748cd8SNickeau                $tdate = $tdateClone
45*37748cd8SNickeau                    ->setTimezone($tdate->getTimezone())
46*37748cd8SNickeau                    ->setDate($currentYear, $currentMonth, --$lastDayOfMonth);
47*37748cd8SNickeau            }
48*37748cd8SNickeau
49*37748cd8SNickeau            return $date->format('j') == $lastDayOfMonth;
50*37748cd8SNickeau        }
51*37748cd8SNickeau
52*37748cd8SNickeau        // Handle # hash tokens
53*37748cd8SNickeau        if (strpos($value, '#')) {
54*37748cd8SNickeau            list($weekday, $nth) = explode('#', $value);
55*37748cd8SNickeau
56*37748cd8SNickeau            // 0 and 7 are both Sunday, however 7 matches date('N') format ISO-8601
57*37748cd8SNickeau            if ($weekday === '0') {
58*37748cd8SNickeau                $weekday = 7;
59*37748cd8SNickeau            }
60*37748cd8SNickeau
61*37748cd8SNickeau            // Validate the hash fields
62*37748cd8SNickeau            if ($weekday < 0 || $weekday > 7) {
63*37748cd8SNickeau                throw new InvalidArgumentException("Weekday must be a value between 0 and 7. {$weekday} given");
64*37748cd8SNickeau            }
65*37748cd8SNickeau            if ($nth > 5) {
66*37748cd8SNickeau                throw new InvalidArgumentException('There are never more than 5 of a given weekday in a month');
67*37748cd8SNickeau            }
68*37748cd8SNickeau            // The current weekday must match the targeted weekday to proceed
69*37748cd8SNickeau            if ($date->format('N') != $weekday) {
70*37748cd8SNickeau                return false;
71*37748cd8SNickeau            }
72*37748cd8SNickeau
73*37748cd8SNickeau            $tdate = clone $date;
74*37748cd8SNickeau            $tdate->setDate($currentYear, $currentMonth, 1);
75*37748cd8SNickeau            $dayCount = 0;
76*37748cd8SNickeau            $currentDay = 1;
77*37748cd8SNickeau            while ($currentDay < $lastDayOfMonth + 1) {
78*37748cd8SNickeau                if ($tdate->format('N') == $weekday) {
79*37748cd8SNickeau                    if (++$dayCount >= $nth) {
80*37748cd8SNickeau                        break;
81*37748cd8SNickeau                    }
82*37748cd8SNickeau                }
83*37748cd8SNickeau                $tdate->setDate($currentYear, $currentMonth, ++$currentDay);
84*37748cd8SNickeau            }
85*37748cd8SNickeau
86*37748cd8SNickeau            return $date->format('j') == $currentDay;
87*37748cd8SNickeau        }
88*37748cd8SNickeau
89*37748cd8SNickeau        // Handle day of the week values
90*37748cd8SNickeau        if (strpos($value, '-')) {
91*37748cd8SNickeau            $parts = explode('-', $value);
92*37748cd8SNickeau            if ($parts[0] == '7') {
93*37748cd8SNickeau                $parts[0] = '0';
94*37748cd8SNickeau            } elseif ($parts[1] == '0') {
95*37748cd8SNickeau                $parts[1] = '7';
96*37748cd8SNickeau            }
97*37748cd8SNickeau            $value = implode('-', $parts);
98*37748cd8SNickeau        }
99*37748cd8SNickeau
100*37748cd8SNickeau        // Test to see which Sunday to use -- 0 == 7 == Sunday
101*37748cd8SNickeau        $format = in_array(7, str_split($value)) ? 'N' : 'w';
102*37748cd8SNickeau        $fieldValue = $date->format($format);
103*37748cd8SNickeau
104*37748cd8SNickeau        return $this->isSatisfied($fieldValue, $value);
105*37748cd8SNickeau    }
106*37748cd8SNickeau
107*37748cd8SNickeau    public function increment(DateTime $date, $invert = false)
108*37748cd8SNickeau    {
109*37748cd8SNickeau        if ($invert) {
110*37748cd8SNickeau            $date->modify('-1 day');
111*37748cd8SNickeau            $date->setTime(23, 59, 0);
112*37748cd8SNickeau        } else {
113*37748cd8SNickeau            $date->modify('+1 day');
114*37748cd8SNickeau            $date->setTime(0, 0, 0);
115*37748cd8SNickeau        }
116*37748cd8SNickeau
117*37748cd8SNickeau        return $this;
118*37748cd8SNickeau    }
119*37748cd8SNickeau
120*37748cd8SNickeau    public function validate($value)
121*37748cd8SNickeau    {
122*37748cd8SNickeau        $value = $this->convertLiterals($value);
123*37748cd8SNickeau
124*37748cd8SNickeau        foreach (explode(',', $value) as $expr) {
125*37748cd8SNickeau            if (!preg_match('/^(\*|[0-7](L?|#[1-5]))([\/\,\-][0-7]+)*$/', $expr)) {
126*37748cd8SNickeau                return false;
127*37748cd8SNickeau            }
128*37748cd8SNickeau        }
129*37748cd8SNickeau
130*37748cd8SNickeau        return true;
131*37748cd8SNickeau    }
132*37748cd8SNickeau
133*37748cd8SNickeau    private function convertLiterals($string)
134*37748cd8SNickeau    {
135*37748cd8SNickeau        return str_ireplace(
136*37748cd8SNickeau            array('SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'),
137*37748cd8SNickeau            range(0, 6),
138*37748cd8SNickeau            $string
139*37748cd8SNickeau        );
140*37748cd8SNickeau    }
141*37748cd8SNickeau}
142