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