| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 | 
							- <?php
 
- /**
 
-  * @file
 
-  *
 
-  * This class parses cron rules and determines last execution time using least case integer comparison.
 
-  */
 
- class CronRule {
 
-   public $rule = NULL;
 
-   public $allow_shorthand = FALSE;
 
-   private static $ranges = array(
 
-     'minutes' => array(0, 59),
 
-     'hours' => array(0, 23),
 
-     'days' => array(1, 31),
 
-     'months' => array(1, 12),
 
-     'weekdays' => array(0, 6),
 
-   );
 
-   private $parsed_rule = array();
 
-   public $offset = 0;
 
-   /**
 
-    * Constructor
 
-    */
 
-   function __construct($rule = NULL) {
 
-     $this->rule = $rule;
 
-   }
 
-   /**
 
-    * Expand interval from cronrule part
 
-    *
 
-    * @param $matches (e.g. 4-43/5+2)
 
-    *   array of matches:
 
-    *     [1] = lower
 
-    *     [2] = upper
 
-    *     [5] = step
 
-    *     [7] = offset
 
-    *
 
-    * @return
 
-    *   (string) comma-separated list of values
 
-    */
 
-   function expandInterval($matches) {
 
-     $result = array();
 
-     $lower = $matches[1];
 
-     $upper = $matches[2];
 
-     $step = isset($matches[5]) ? $matches[5] : 1;
 
-     $offset = isset($matches[7]) ? $matches[7] : 0;
 
-     if ($step <= 0) return '';
 
-     $step = ($step > 0) ? $step : 1;
 
-     for ($i = $lower; $i <= $upper; $i+=$step) {
 
-       $result[] = ($i + $offset) % ($upper + 1);
 
-     }
 
-     return implode(',', $result);
 
-   }
 
-   /**
 
-    * Expand range from cronrule part
 
-    *
 
-    * @param $rule
 
-    *   (string) cronrule part, e.g.: 1,2,3,4-43/5
 
-    * @param $max
 
-    *   (string) boundaries, e.g.: 0-59
 
-    * @param $digits
 
-    *   (int) number of digits of value (leading zeroes)
 
-    * @return
 
-    *   (array) array of valid values
 
-    */
 
-   function expandRange($rule, $type) {
 
-     $max = implode('-', self::$ranges[$type]);
 
-     $rule = str_replace("*", $max, $rule);
 
-     $rule = str_replace("@", $this->offset % (self::$ranges[$type][1] + 1), $rule);
 
-     $this->parsed_rule[$type] = $rule;
 
-     $rule = preg_replace_callback('!(\d+)-(\d+)((/(\d+))?(\+(\d+))?)?!', array($this, 'expandInterval'), $rule);
 
-     if (!preg_match('/([^0-9\,])/', $rule)) {
 
-       $rule = explode(',', $rule);
 
-       rsort($rule);
 
-     }
 
-     else {
 
-       $rule = array();
 
-     }
 
-     return $rule;
 
-   }
 
-   /**
 
-    * Pre process rule.
 
-    *
 
-    * @param array &$parts
 
-    */
 
-   function preProcessRule(&$parts) {
 
-     // Allow JAN-DEC
 
-     $months = array(1 => 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec');
 
-     $parts[3] = strtr(strtolower($parts[3]), array_flip($months));
 
-     // Allow SUN-SUN
 
-     $days = array('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat');
 
-     $parts[4] = strtr(strtolower($parts[4]), array_flip($days));
 
-     $parts[4] = str_replace('7', '0', $parts[4]);
 
-   }
 
-   /**
 
-    * Post process rule
 
-    *
 
-    * @param array $intervals
 
-    */
 
-   function postProcessRule(&$intervals) {
 
-   }
 
-   /**
 
-    * Generate regex rules
 
-    *
 
-    * @param $rule
 
-    *   (string) cronrule, e.g: 1,2,3,4-43/5 * * * 2,5
 
-    * @return
 
-    *   (array) date and time regular expression for mathing rule
 
-    */
 
-   function getIntervals($rule = NULL) {
 
-     $parts = preg_split('/\s+/', isset($rule) ? $rule : $this->rule);
 
-     if ($this->allow_shorthand) $parts += array('*', '*', '*', '*', '*'); // Allow short rules by appending wildcards?
 
-     if (count($parts) != 5) return FALSE;
 
-     $this->preProcessRule($parts);
 
-     $intervals = array();
 
-     $intervals['minutes']  = $this->expandRange($parts[0], 'minutes');
 
-     if (empty($intervals['minutes'])) return FALSE;
 
-     $intervals['hours']    = $this->expandRange($parts[1], 'hours');
 
-     if (empty($intervals['hours'])) return FALSE;
 
-     $intervals['days']     = $this->expandRange($parts[2], 'days');
 
-     if (empty($intervals['days'])) return FALSE;
 
-     $intervals['months']   = $this->expandRange($parts[3], 'months');
 
-     if (empty($intervals['months'])) return FALSE;
 
-     $intervals['weekdays'] = $this->expandRange($parts[4], 'weekdays');
 
-     if (empty($intervals['weekdays'])) return FALSE;
 
-     $intervals['weekdays'] = array_flip($intervals['weekdays']);
 
-     $this->postProcessRule($intervals);
 
-     return $intervals;
 
-   }
 
-   /**
 
-    * Convert intervals back into crontab rule format
 
-    */
 
-   function rebuildRule($intervals) {
 
-     $parts = array();
 
-     foreach ($intervals as $type => $interval) {
 
-       $parts[] = $this->parsed_rule[$type];
 
-     }
 
-     return implode(' ', $parts);
 
-   }
 
-   /**
 
-    * Parse rule. Run through parser expanding expression, and recombine into crontab syntax.
 
-    */
 
-   function parseRule() {
 
-     return $this->rebuildRule($this->getIntervals());
 
-   }
 
-   /**
 
-    * Get last execution time of rule in unix timestamp format
 
-    *
 
-    * @param $time
 
-    *   (int) time to use as relative time (default now)
 
-    * @return
 
-    *   (int) unix timestamp of last execution time
 
-    */
 
-   function getLastRan($time = NULL) {
 
-     // Current time round to last minute
 
-     if (!isset($time)) $time = time();
 
-     $time = floor($time / 60) * 60;
 
-     // Generate regular expressions from rule
 
-     $intervals = $this->getIntervals();
 
-     if ($intervals === FALSE) return FALSE;
 
-     // Get starting points
 
-     $start_year   = date('Y', $time);
 
-     $end_year     = $start_year - 28; // Go back max 28 years (leapyear * weekdays)
 
-     $start_month  = date('n', $time);
 
-     $start_day    = date('j', $time);
 
-     $start_hour   = date('G', $time);
 
-     $start_minute = (int)date('i', $time);
 
-     // If both weekday and days are restricted, then use either or
 
-     // otherwise, use and ... when using or, we have to try out all the days in the month
 
-     // and not just to the ones restricted
 
-     $check_both = (count($intervals['days']) != 31 && count($intervals['weekdays']) != 7) ? FALSE : TRUE;
 
-     $days = $check_both ? $intervals['days'] : range(31, 1);
 
-     // Find last date and time this rule was run
 
-     for ($year = $start_year; $year > $end_year; $year--) {
 
-       foreach ($intervals['months'] as $month) {
 
-         if ($month < 1 || $month > 12) continue;
 
-         if ($year >= $start_year && $month > $start_month) continue;
 
-         foreach ($days as $day) {
 
-           if ($day < 1 || $day > 31) continue;
 
-           if ($year >= $start_year && $month >= $start_month && $day > $start_day) continue;
 
-           if (!checkdate($month, $day, $year)) continue;
 
-           // Check days and weekdays using and/or logic
 
-           $date_array = getdate(mktime(0, 0, 0, $month, $day, $year));
 
-           if ($check_both) {
 
-             if (!isset($intervals['weekdays'][$date_array['wday']])) continue;
 
-           }
 
-           else {
 
-             if (
 
-               !in_array($day, $intervals['days']) &&
 
-               !isset($intervals['weekdays'][$date_array['wday']])
 
-             ) continue;
 
-           }
 
-           if ($day != $start_day || $month != $start_month || $year != $start_year) {
 
-             $start_hour = 23;
 
-             $start_minute = 59;
 
-           }
 
-           foreach ($intervals['hours'] as $hour) {
 
-             if ($hour < 0 || $hour > 23) continue;
 
-             if ($hour > $start_hour) continue;
 
-             if ($hour < $start_hour) $start_minute = 59;
 
-             foreach ($intervals['minutes'] as $minute) {
 
-               if ($minute < 0 || $minute > 59) continue;
 
-               if ($minute > $start_minute) continue;
 
-               break 5;
 
-             }
 
-           }
 
-         }
 
-       }
 
-     }
 
-     // Create unix timestamp from derived date+time
 
-     $time = mktime($hour, $minute, 0, $month, $day, $year);
 
-     return $time;
 
-   }
 
-   /**
 
-    * Check if a rule is valid
 
-    */
 
-   function isValid($time = NULL) {
 
-     return $this->getLastRan($time) === FALSE ? FALSE : TRUE;
 
-   }
 
- }
 
 
  |