Add analytics
[bus.git] / busui / owa / includes / CronParser.php
blob:a/busui/owa/includes/CronParser.php -> blob:b/busui/owa/includes/CronParser.php
  <?php /* $Id: CronParser.php,v 1.7 2005/09/12 01:04:05 ns Exp $ */
   
  /**####################################################################################################**\
  Version: V1.01
  Release Date: 12 Sep 2005
  Licence: GPL
  By: Nikol S
  Please send bug reports to ns@eyo.com.au
  \**####################################################################################################**/
   
  /* This class is based on the concept in the CronParser class written by Mick Sear http://www.ecreate.co.uk
  * The following functions are direct copies from or based on the original class:
  * getLastRan(), getDebug(), debug(), expand_ranges()
  *
  * Who can use this class?
  * This class is idea for people who can not use the traditional Unix cron through shell.
  * One way of using is embedding the calling script in a web page which is often visited.
  * The script will work out the last due time, by comparing with run log timestamp. The scrip
  * will envoke any scripts needed to run, be it deleting older table records, or updating prices.
  * It can parse the same cron string used by Unix.
  */
   
  /* Usage example:
   
  $cron_str0 = "0,12,30-51 3,21-23,10 1-25 9-12,1 0,3-7";
  require_once("CronParser.php");
  $cron = new CronParser();
  $cron->calcLastRan($cron_str0);
  // $cron->getLastRanUnix() returns an Unix timestamp
  echo "Cron '$cron_str0' last due at: " . date('r', $cron->getLastRanUnix()) . "<p>";
  // $cron->getLastRan() returns last due time in an array
  print_r($cron->getLastRan());
  echo "Debug:<br>" . nl2br($cron->getDebug());
   
  $cron_str1 = "3 12 * * *";
  if ($cron->calcLastRan($cron_str1))
  {
  echo "<p>Cron '$cron_str1' last due at: " . date('r', $cron->getLastRanUnix()) . "<p>";
  print_r($cron->getLastRan());
  }
  else
  {
  echo "Error parsing";
  }
  echo "Debug:<br>" . nl2br($cron->getDebug());
   
  *#######################################################################################################
  */
   
  class CronParser
  {
   
  var $bits = Array(); //exploded String like 0 1 * * *
  var $now = Array(); //Array of cron-style entries for time()
  var $lastRan; //Timestamp of last ran time.
  var $taken;
  var $debug;
  var $year;
  var $month;
  var $day;
  var $hour;
  var $minute;
  var $minutes_arr = array(); //minutes array based on cron string
  var $hours_arr = array(); //hours array based on cron string
  var $months_arr = array(); //months array based on cron string
   
  function getLastRan()
  {
  return explode(",", strftime("%M,%H,%d,%m,%w,%Y", $this->lastRan)); //Get the values for now in a format we can use
  }
   
  function getLastRanUnix()
  {
  return $this->lastRan;
  }
   
  function getDebug()
  {
  return $this->debug;
  }
   
  function debug($str)
  {
  if (is_array($str))
  {
  $this->debug .= "\nArray: ";
  foreach($str as $k=>$v)
  {
  $this->debug .= "$k=>$v, ";
  }
   
  }
  else
  {
  $this->debug .= "\n$str";
  }
  //echo nl2br($this->debug);
  }
   
  /**
  * Assumes that value is not *, and creates an array of valid numbers that
  * the string represents. Returns an array.
  */
  function expand_ranges($str)
  {
  if (strstr($str, ","))
  {
  $arParts = explode(',', $str);
  foreach ($arParts AS $part)
  {
  if (strstr($part, '-'))
  {
  $arRange = explode('-', $part);
  for ($i = $arRange[0]; $i <= $arRange[1]; $i++)
  {
  $ret[] = $i;
  }
  }
  else
  {
  $ret[] = $part;
  }
  }
  }
  elseif (strstr($str, '-'))
  {
  $arRange = explode('-', $str);
  for ($i = $arRange[0]; $i <= $arRange[1]; $i++)
  {
  $ret[] = $i;
  }
  }
  else
  {
  $ret[] = $str;
  }
  $ret = array_unique($ret);
  sort($ret);
  return $ret;
  }
   
  function daysinmonth($month, $year)
  {
  return date('t', mktime(0, 0, 0, $month, 1, $year));
  }
   
  /**
  * Calculate the last due time before this moment
  */
  function calcLastRan($string)
  {
   
  $tstart = microtime();
  $this->debug = "";
  $this->lastRan = 0;
  $this->year = NULL;
  $this->month = NULL;
  $this->day = NULL;
  $this->hour = NULL;
  $this->minute = NULL;
  $this->hours_arr = array();
  $this->minutes_arr = array();
  $this->months_arr = array();
   
  $string = preg_replace('/[\s]{2,}/', ' ', $string);
   
  if (preg_match('/[^-,* \\d]/', $string) !== 0)
  {
  $this->debug("Cron String contains invalid character");
  return false;
  }
   
  $this->debug("<b>Working on cron schedule: $string</b>");
  $this->bits = @explode(" ", $string);
   
  if (count($this->bits) != 5)
  {
  $this->debug("Cron string is invalid. Too many or too little sections after explode");
  return false;
  }
   
  //put the current time into an array
  $t = strftime("%M,%H,%d,%m,%w,%Y", time());
  $this->now = explode(",", $t);
   
  $this->year = $this->now[5];
   
  $arMonths = $this->_getMonthsArray();
   
  do
  {
  $this->month = array_pop($arMonths);
  }
  while ($this->month > $this->now[3]);
   
  if ($this->month === NULL)
  {
  $this->year = $this->year - 1;
  $this->debug("Not due within this year. So checking the previous year " . $this->year);
  $arMonths = $this->_getMonthsArray();
  $this->_prevMonth($arMonths);
  }
  elseif ($this->month == $this->now[3]) //now Sep, month = array(7,9,12)
  {
  $this->debug("Cron is due this month, getting days array.");
  $arDays = $this->_getDaysArray($this->month, $this->year);
   
  do
  {
  $this->day = array_pop($arDays);
  }
  while ($this->day > $this->now[2]);
   
  if ($this->day === NULL)
  {
  $this->debug("Smallest day is even greater than today");
  $this->_prevMonth($arMonths);
  }
  elseif ($this->day == $this->now[2])
  {
  $this->debug("Due to run today");
  $arHours = $this->_getHoursArray();
   
  do
  {
  $this->hour = array_pop($arHours);
  }
  while ($this->hour > $this->now[1]);
   
  if ($this->hour === NULL) // now =2, arHours = array(3,5,7)
  {
  $this->debug("Not due this hour and some earlier hours, so go for previous day");
  $this->_prevDay($arDays, $arMonths);
  }
  elseif ($this->hour < $this->now[1]) //now =2, arHours = array(1,3,5)
  {
  $this->minute = $this->_getLastMinute();
  }
  else // now =2, arHours = array(1,2,5)
  {
  $this->debug("Due this hour");
  $arMinutes = $this->_getMinutesArray();
  do
  {
  $this->minute = array_pop($arMinutes);
  }
  while ($this->minute > $this->now[0]);
   
  if ($this->minute === NULL)
  {
  $this->debug("Not due this minute, so go for previous hour.");
  $this->_prevHour($arHours, $arDays, $arMonths);
  }
  else
  {
  $this->debug("Due this very minute or some earlier minutes before this moment within this hour.");
  }
  }
  }
  else
  {
  $this->debug("Cron was due on " . $this->day . " of this month");
  $this->hour = $this->_getLastHour();
  $this->minute = $this->_getLastMinute();
  }
  }
  else //now Sep, arrMonths=array(7, 10)
  {
  $this->debug("Cron was due before this month. Previous month is: " . $this->year . '-' . $this->month);
  $this->day = $this->_getLastDay($this->month, $this->year);
  if ($this->day === NULL)
  {
  //No scheduled date within this month. So we will try the previous month in the month array
  $this->_prevMonth($arMonths);
  }
  else
  {
  $this->hour = $this->_getLastHour();
  $this->minute = $this->_getLastMinute();
  }
  }
   
  $tend = microtime();
  $this->taken = $tend - $tstart;
  $this->debug("Parsing $string taken " . $this->taken . " seconds");
   
  //if the last due is beyond 1970
  if ($this->minute === NULL)
  {
  $this->debug("Error calculating last due time");
  return false;
  }
  else
  {
  $this->debug("LAST DUE: " . $this->hour . ":" . $this->minute . " on " . $this->day . "/" . $this->month . "/" . $this->year);
  $this->lastRan = mktime($this->hour, $this->minute, 0, $this->month, $this->day, $this->year);
  return true;
  }
  }
   
  //get the due time before current month
  function _prevMonth($arMonths)
  {
  $this->month = array_pop($arMonths);
  if ($this->month === NULL)
  {
  $this->year = $this->year -1;
  if ($this->year <= 1970)
  {
  $this->debug("Can not calculate last due time. At least not before 1970..");
  }
  else
  {
  $this->debug("Have to go for previous year " . $this->year);
  $arMonths = $this->_getMonthsArray();
  $this->_prevMonth($arMonths);
  }
  }
  else
  {
  $this->debug("Getting the last day for previous month: " . $this->year . '-' . $this->month);
  $this->day = $this->_getLastDay($this->month, $this->year);
   
  if ($this->day === NULL)
  {
  //no available date schedule in this month
  $this->_prevMonth($arMonths);
  }
  else
  {
  $this->hour = $this->_getLastHour();
  $this->minute = $this->_getLastMinute();
  }
  }
   
  }
   
  //get the due time before current day
  function _prevDay($arDays, $arMonths)
  {
  $this->debug("Go for the previous day");
  $this->day = array_pop($arDays);
  if ($this->day === NULL)
  {
  $this->debug("Have to go for previous month");
  $this->_prevMonth($arMonths);
  }
  else
  {
  $this->hour = $this->_getLastHour();
  $this->minute = $this->_getLastMinute();
  }
  }
   
  //get the due time before current hour
  function _prevHour($arHours, $arDays, $arMonths)
  {
  $this->debug("Going for previous hour");
  $this->hour = array_pop($arHours);
  if ($this->hour === NULL)
  {
  $this->debug("Have to go for previous day");
  $this->_prevDay($arDays, $arMonths);
  }
  else
  {
  $this->minute = $this->_getLastMinute();
  }
  }
   
  //not used at the moment
  function _getLastMonth()
  {
  $months = $this->_getMonthsArray();
  $month = array_pop($months);
   
  return $month;
  }
   
  function _getLastDay($month, $year)
  {
  //put the available days for that month into an array
  $days = $this->_getDaysArray($month, $year);
  $day = array_pop($days);
   
  return $day;
  }
   
  function _getLastHour()
  {
  $hours = $this->_getHoursArray();
  $hour = array_pop($hours);