summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPreston Cody <codeman@gentoo.org>2007-07-06 16:14:50 +0000
committerPreston Cody <codeman@gentoo.org>2007-07-06 16:14:50 +0000
commitf4596d19fcb9cbfd1ec0faf2b5afe612f48bdfe3 (patch)
treec575218b87774db03590cdb3db86565ae281d307 /scire/cron.php
parentmaking some changes from Rodrigo Lazo for SoC: (diff)
downloadscire-f4596d19fcb9cbfd1ec0faf2b5afe612f48bdfe3.tar.gz
scire-f4596d19fcb9cbfd1ec0faf2b5afe612f48bdfe3.tar.bz2
scire-f4596d19fcb9cbfd1ec0faf2b5afe612f48bdfe3.zip
adding 3 cron-handling files from rlazo for Google SoC
svn path=/; revision=226
Diffstat (limited to 'scire/cron.php')
-rw-r--r--scire/cron.php672
1 files changed, 672 insertions, 0 deletions
diff --git a/scire/cron.php b/scire/cron.php
new file mode 100644
index 0000000..10ba80d
--- /dev/null
+++ b/scire/cron.php
@@ -0,0 +1,672 @@
+<?php
+ /*
+ * Mick Sear, eCreate, May 2005
+ * http://www.ecreate.co.uk
+ * Modified by: Rodrigo Lazo, June 2007
+ * License: GPL
+ * Version: 1.1
+ */
+
+ /*
+ * Added functinality :
+ *
+ * - Input validation.
+ * - Support for step argument ("/")
+ * - Cron Exception for non-recoveral errors
+ * - Field expansion (given a month, generate all the possible
+ * values for every other field)
+ */
+
+ /*$cron = "15,33,48 1-10 * * *";
+ $cp = new CronParser($cron);
+ echo "Cron $cron last ran on:";
+ print_r($cp->getLastRan());
+ echo nl2br($cp->getDebug());*/
+
+ //
+ // Know bugs:
+ //
+ // It can't handle too complex step arguments.
+ //
+ // */12 or 1-3/4 are ok
+ // 1,4-5/20,2 or 1,2,3,4,5,10-30/5 isn't supported
+ //
+
+class CronException extends Exception {
+ // Redefine the exception so message isn't optional
+ public function __construct($message, $code = 0) {
+ // some code
+
+ // make sure everything is assigned properly
+ parent::__construct($message, $code);
+ }
+
+ // custom string representation of object
+ public function __toString() {
+ return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
+ }
+
+ }
+
+
+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;
+
+
+ function CronParser($string, $now = false, $validate_only = false){
+ $tstart = microtime();
+ $this->debug("<b>Working on cron schedule: $string</b>");
+ $this->bits = @explode(" ", $this->sanitize_input($string));
+ $this->validate();
+ if ($validate_only)
+ return;
+ if ($now)
+ $this->now = explode(",", $now);
+ else
+ $this->getNow();
+ $this->calcLastRan();
+ $this->calcNextRun();
+ $tend = microtime();
+ $this->taken = $tend-$tstart;
+ $this->debug("Parsing $string taken ".$this->taken." seconds");
+
+ }
+
+ function sanitize_input($string) {
+ $tmp = trim($string);
+ $output = "";
+ $jump = false;
+ for ($i=0; $i<strlen($tmp); $i++) {
+ if ($tmp[$i] != ' ') {
+ $output = $output . $tmp[$i];
+ $jump = false;
+ }
+ else
+ if (!$jump) {
+ $output = $output . $tmp[$i];
+ $jump = true;
+ }
+ }
+ return $output;
+ }
+
+ function validate(){
+ $min = array(0,0,1,1,0);
+ $max = array(59,23,31,12,7);
+ $pattern = "/^\d+((\d+)?(,\d)?(\-\d+($|\/\d+$)?)?)+$/";
+ $range_pattern = "/^\*(\/\d+)?$/";
+ if (count($this->bits) != 5){
+ throw new CronException("Wrong number of parameters.");
+ }
+ $i=0;
+ foreach ($this->bits as $piece){
+ if ($piece == "*" || preg_match($range_pattern, $piece)) {
+ continue;
+ }
+ if (!preg_match($pattern, $piece)){
+ throw new CronException("Bad formatted entry: " . $piece);
+ }
+ foreach (explode(",",$piece) as $molecule) {
+ foreach (explode("-", $molecule) as $atom) {
+ if ($atom > $max[$i] || $atom < $min[$i]){
+ throw new CronException("Parameter out of range: " . $atom);
+ }
+ }
+ }
+ $i++;
+ }
+ }
+
+ function expandValues(){
+ $sol = array();
+ $minutes = $this->bits[0];
+ $hours = $this->bits[1];
+ $months = $this->bits[3];
+
+ if ($minutes == "*") {
+ $minutes = range(0,59);
+ } else if (strstr($minutes, "*/")) {
+ $tmp = explode("/", $minutes);
+ $minutes = $this->expand_ranges("0-59/".$tmp[1]);
+ }
+ else {
+ $minutes = $this->expand_ranges($minutes);
+ }
+
+ if ($hours == "*") {
+ $hours = range(0, 23);
+ } else if (strstr($hours, "*/")) {
+ $tmp = explode("/", $hours);
+ $minutes = $this->expand_ranges("0-23/".$tmp[1]);
+ }
+ else {
+ $hours = $this->expand_ranges($hours);
+ }
+
+ if ($months == "*") {
+ $months = range(1,12);
+ } else if (strstr($months, "*/")) {
+ $tmp = explode("/", $months);
+ $minutes = $this->expand_ranges("0-12/".$tmp[1]);
+ }
+ else {
+ $months = $this->expand_ranges($months);
+ }
+
+ return array("minutes" => $minutes, "hours" => $hours, "months" => $months);
+
+ }
+
+ function getNextArray($arr, $current) {
+ if (is_array($arr)) {
+ foreach ($arr as $v) {
+ if ($v > $current)
+ return $v;
+ }
+ }
+ return false;
+ }
+
+ function calcNextRun(){
+ $lastRan = $this->getLastRan();
+ $values = $this->expandValues();
+ $days = $this->getDaysArray($lastRan[3]);
+
+ var_dump($values);
+ ob_start();var_dump($values);$output=ob_get_contents();ob_end_clean();
+
+ $this->debug("<b>NextRun!</b> dump" . $output);
+
+ ob_start();var_dump($days);$output=ob_get_contents();ob_end_clean();
+ $this->debug("<b>dias:</b> " . $output);
+
+ $minute = $this->getNextArray($values["minutes"], $lastRan[0]);
+ if ($minute) {
+ $this->nextRun = mktime($lastRan[1], $minute, 0, $lastRan[3], $lastRan[2], $lastRan[5]);
+ return;
+ }
+ else {
+ $minute = $values["minutes"][0];
+ }
+ $this->debug("Next minute -> " . $minute);
+
+
+ $hour = $this->getNextArray($values["hours"], $lastRan[1]);
+ if ($hour) {
+ $this->nextRun = mktime($hour, $minute, 0, $lastRan[3], $lastRan[2], $lastRan[5]);
+ return;
+ }
+ else {
+ $hour = $values["hours"][0];
+ }
+
+ $day = $this->getNextArray($days, $lastRan[2]);
+ if ($day) {
+ $this->nextRun = mktime($hour, $minute, 0, $lastRan[3], $day, $lastRan[5]);
+ return;
+ }
+
+ // In this case we don't have an else statement because we
+ // need to calculate the next month first so we know wich day is the needed
+
+ $month = $this->getNextArray($values["months"], $lastRan[3]);
+ if ($month) {
+ $day = $this->getDaysArray($month);
+ $this->nextRun = mktime($hour, $minute, 0,$month, $day, $lastRan[5]);
+ return;
+ }
+ else {
+ $month = $values["months"][0];
+ $day = $this->getDaysArray($month, $lastRan[5]+1);
+ $this->nextRun = mktime($hour, $minute, 0,$month, $day, $lastRan[5]+1);
+ }
+
+ ob_start();var_dump($this->nextRun);$output=ob_get_contents();ob_end_clean();
+ $this->debug("<b>output</b> " . $output);
+
+// var_dump($this->nextRan);
+ }
+
+ function getNow(){
+ $t = strftime("%M,%H,%d,%m,%w,%Y", time()); //Get the values for now in a format we can use
+ $this->now = explode(",", $t); //Make this an array
+ //$this->debug($this->now);
+ }
+
+ 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 getNextRun(){
+ return explode(",", strftime("%M,%H,%d,%m,%w,%Y", $this->nextRun)); //Get the values for now in a format we can use
+ }
+
+ 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);
+
+ }
+
+
+ function getExtremeMonth($extreme){
+
+ if ($extreme == "END"){
+ $year = $this->now[5] - 1;
+ } else {
+ $year = $this->now[5];
+ }
+
+ //Now determine start or end month in the last year
+ if ($this->bits[3] == "*" && $extreme == "END"){//Check month format
+ $month = 12;
+ } else if ($this->bits[3] == "*" && $extreme == "START"){
+ $month = 1;
+ } else {
+ $months = $this->expand_ranges($this->bits[3]);
+ if ($extreme == "END"){
+ sort($months);
+ } else {
+ rsort($months);
+ }
+ $month = array_pop($months);
+ }
+
+ //Now determine the latest day in the specified month
+ $day=$this->getExtremeOfMonth($month, $year, $extreme);
+ $this->debug("Got day $day for $extreme of $month, $year");
+ $hour = $this->getExtremeHour($extreme);
+ $minute = $this->getExtremeMinute($extreme);
+
+ return mktime($hour, $minute, 0, $month, $day, $year);
+ }
+
+ /**
+ * Assumes that value is not *, and creates an array of valid numbers that
+ * the string represents. Returns an array.
+ */
+ function expand_ranges($str){
+ //$this->debug("Expanding $str");
+ //echo var_dump($str);
+
+ if (strstr($str, "/")){
+ $val = explode("/", $str);
+ $str = $val[0];
+ $step = $val[1];
+ if (count($val) != 2) {
+ throw new CronException("Bad formatted entry (step): " . $str);
+ }
+ }
+
+ if (strstr($str, ",")){
+ $tmp1 = explode(",", $str);
+ //$this->debug($tmp1);
+ $count = count($tmp1);
+ for ($i=0;$i<$count;$i++){//Loop through each comma-separated value
+ if (strstr($tmp1[$i], "-")){ //If there's a range in this place, expand that too
+ $tmp2 = explode("-", $tmp1[$i]);
+ //$this->debug("Range:");
+ //$this->debug($tmp2);
+ for ($j=$tmp2[0];$j<=$tmp2[1];$j++){
+ $ret[] = $j;
+ }
+ } else {//Otherwise, just add the value
+ $ret[] = $tmp1[$i];
+ }
+ }
+ } else if (strstr($str, "-")){//There might only be a range, no comma sep values at all. Just loop these
+ $range = explode("-", $str);
+ for ($i=$range[0];$i<=$range[1];$i++){
+ $ret[] = $i;
+ }
+ } else {//Otherwise, it's a single value
+ $ret[] = $str;
+ return $ret;
+ }
+ sort($ret);
+ if (isset($step)) {
+ $sol = array();
+ foreach (range(0,count($ret),$step) as $i) {
+ $sol[] = $ret[$i];
+ }
+ $ret = $sol;
+ }
+ return $ret;
+ }
+
+ /**
+ * Given a string representation of a set of weekdays, returns an array of
+ * possible dates.
+ */
+ function getWeekDays($str, $month, $year){
+ $daysInMonth = $this->daysinmonth($month, $year);
+
+ if (strstr($str, ",")){
+ $tmp1 = explode(",", $str);
+ $count = count($tmp1);
+ for ($i=0;$i<$count;$i++){//Loop through each comma-separated value
+ if (strstr($tmp1[$i], "-")){ //If there's a range in this place, expand that too
+ $tmp2 = explode("-", $tmp1[$i]);
+
+ for ($j=$start;$j<=$tmp2[1];$j++){
+ for ($n=1;$n<=$daysInMonth;$n++){
+ if ($j == jddayofweek(gregoriantojd ( $month, $n, $year),0)){
+ $ret[] = $n;
+ }
+ }
+ }
+ } else {//Otherwise, just add the value
+ for ($n=1;$n<=$daysInMonth;$n++){
+ if ($tmp1[$i] == jddayofweek(gregoriantojd ( $month, $n, $year),0)){
+ $ret[] = $n;
+ }
+ }
+ }
+ }
+ } else if (strstr($str, "-")){//There might only be a range, no comma sep values at all. Just loop these
+ $range = explode("-", $str);
+ for ($i=$start;$i<=$range[1];$i++){
+ for ($n=1;$n<=$daysInMonth;$n++){
+ if ($i == jddayofweek(gregoriantojd ( $month, $n, $year),0)){
+ $ret[] = $n;
+ }
+ }
+ }
+ } else {//Otherwise, it's a single value
+ for ($n=1;$n<=$daysInMonth;$n++){
+ if ($str == jddayofweek(gregoriantojd ( $month, $n, $year),0)){
+ $ret[] = $n;
+ }
+ }
+ }
+
+ return $ret;
+ }
+
+ function daysinmonth($month, $year){
+ if(checkdate($month, 31, $year)) return 31;
+ if(checkdate($month, 30, $year)) return 30;
+ if(checkdate($month, 29, $year)) return 29;
+ if(checkdate($month, 28, $year)) return 28;
+ return 0; // error
+ }
+
+ /**
+ * Get the timestamp of the last ran time.
+ */
+ function calcLastRan(){
+ $values = $this->expandValues();
+ echo "Calculating last ran \n";
+ echo var_dump($this->now);
+ $now = mktime($this->now[1],$this->now[0],0,$this->now[3],$this->now[2],$this->now[5]);
+ echo "Now -> ";
+ echo var_dump($now[0]);
+ if ($now < $this->getExtremeMonth("START")){
+ echo "\nCron is not due to have this year\n";
+ //The cron isn't due to have run this year yet. Getting latest last year
+ $this->debug("Last ran last year");
+ $tsLatestLastYear = $this->getExtremeMonth("END");
+
+ $this->debug("Timestamp of latest scheduled time last year is ".$tsLatestLastYear);
+ $this->lastRan = $tsLatestLastYear;
+
+ $year = date("Y", $this->lastRan);
+ $month = date("m", $this->lastRan);
+ $day = date("d", $this->lastRan);
+ $hour = date("h", $this->lastRan);
+ $minute = date("i", $this->lastRan);
+
+
+ } else { //Cron was due to run this year. Determine when it was last due
+ echo "\nCron is this year\n";
+ $this->debug("Cron was due to run earlier this year");
+ $year = $this->now[5];
+ echo "Anho: ".$year;
+ /*
+ $arMonths = $this->expand_ranges($this->bits[3]);
+ */
+ $arMonths = $values["months"];
+ echo "\nMeses: ";
+ echo var_dump($arMonths);
+ echo "\nValue Meses: ";
+ echo var_dump($values["months"]);
+ if (!in_array($this->now[3], $arMonths) && $this->bits[3] != "*"){//Not due to run this month. Get latest of last month
+ $this->debug("Cron was not due to run this month at all. This month is ".$this->now[3]);
+ $this->debug("Months array: ");
+ $this->debug($arMonths);
+ sort($arMonths);
+ do{
+ $month = array_pop($arMonths);
+ } while($month > $this->now[3]);
+ $day = $this->getExtremeOfMonth($month, $this->now[5], "END");
+ $hour = $this->getExtremeHour("END");
+ $minute = $this->getExtremeMinute("END");
+ } else if ($now < $this->getExtremeOfMonth($this->now[3], $this->now[5], "START")){ //It's due in this month, but not yet.
+ $this->debug("It's due in this month, but not yet.");
+ sort($arMonths);
+ do{
+ $month = array_pop($arMonths);
+ } while($month > $this->now[3]);
+ $day = $this->getExtremeOfMonth($month, $this->now[5], "END");
+ $hour = $this->getExtremeHour("END");
+ $minute = $this->getExtremeMinute("END");
+ } else {//It has been due this month already
+ $this->debug("Cron has already been due to run this month (".$month = $this->now[3].")");
+ $month = $this->now[3];
+ $this->debug("Getting days array");
+ $days = $this->getDaysArray($this->now[3]);
+
+ if (!in_array($this->now[2], $days)){
+ $this->debug("Today not in the schedule. Getting latest last due day");
+ //No - Get latest last scheduled day
+ sort($days);
+ do{
+ $day = array_pop($days);
+ } while($day > $this->now[2]);
+
+ $hour = $this->getExtremeHour("END");
+ $minute = $this->getExtremeMinute("END");
+
+ } else if($this->now[1] < $this->getExtremeHour("START")){//Not due to run today yet
+ $this->debug("Cron due today, but not yet. Getting latest on last day");
+ sort($days);
+ do{
+ $day = array_pop($days);
+ } while($day >= $this->now[2]);
+
+ $hour = $this->getExtremeHour("END");
+ $minute = $this->getExtremeMinute("END");
+ } else {
+ $this->debug("Cron has already been due to run today");
+ $day = $this->now[2];
+ //Yes - Check if this hour is in the schedule?
+
+ $arHours = $this->expand_ranges($this->bits[1]);
+
+ if (!in_array($this->now[1], $arHours) && $this->bits[1] != "*"){
+ $this->debug("Cron not due in this hour, getting latest in last scheduled hour");
+ //No - Get latest last hour
+ sort($arHours);
+ do{
+ $hour = array_pop($arHours);
+ //$this->debug("hour is $hour, now is ".$this->now[1]);
+ } while($hour > $this->now[1]);
+
+ $minute = $this->getExtremeMinute("END");
+
+ } else if ($now < $this->getExtremeMinute("START") && $this->bits[1] != "*"){ //Not due to run this hour yet
+ sort($arHours);
+ do{
+ $hour = array_pop($arHours);
+ } while($hour >= $this->now[1]);
+ $minute = $this->getExtremeMinute("END");
+ } else {
+ //Yes, it is supposed to have run this hour already - Get last minute
+ $hour = $this->now[1];
+ if ($this->bits[0] != "*"){
+ $arMinutes = $this->expand_ranges($this->bits[0]);
+ $this->debug($arMinutes);
+ do{
+ $minute = array_pop($arMinutes);
+ } while($minute >= $this->now[0]);
+
+ //If the first time in the hour that the cron is due to run is later than now, return latest last hour
+ if($minute > $this->now[1] || $minute == ""){
+ $this->debug("Valid minute not set");
+ $minute = $this->getExtremeMinute("END"); //The minute will always be the last valid minute in an hour
+ //Get the last hour.
+ if ($this->bits[1] == "*"){
+ $hour = $this->now[1] - 1;
+ } else {
+ $arHours = $this->expand_ranges($this->bits[1]);
+ $this->debug("Array of hours:");
+ $this->debug($arHours);
+ sort($arHours);
+ do{
+ $hour = array_pop($arHours);
+ } while($hour >= $this->now[1]);
+ }
+ }
+
+ } else {
+ $minute = $this->now[0] -1;
+ }
+ }
+
+ }
+
+ }
+ }
+ echo "Last Ran calculated";
+ echo "\n##########################\n\n";
+ $this->debug("LAST RAN: $hour:$minute on $day/$month/$year");
+ //echo var_dump($this->getDebug());
+ $this->lastRan = mktime($hour, $minute, 0, $month, $day, $year);
+
+ }
+
+ function getExtremeOfMonth($month, $year, $extreme){
+ $daysInMonth = $this->daysinmonth($month, $year);
+ if ($this->bits[2] == "*"){
+ if ($this->bits[4] == "*"){//Is there a day range?
+ //$this->debug("There were $daysInMonth days in $month, $year");
+ if ($extreme == "END"){
+ $day = $daysInMonth;
+ } else {
+ $day=1;
+ }
+ } else {//There's a day range. Ignore the dateDay range and just get the list of possible weekday values.
+ $days = $this->getWeekDays($this->bits[4],$month, $year);
+ $this->debug($this->bits);
+ $this->debug("Days array for ".$this->bits[4].", $month, $year:");
+ $this->debug($days);
+ if ($extreme == "END"){
+ sort($days);
+ } else {
+ rsort($days);
+ }
+ $day = array_pop($days);
+ }
+ } else {
+ $days = $this->expand_ranges($this->bits[2]);
+ if ($extreme == "END"){
+ sort($days);
+ } else {
+ rsort($days);
+ }
+
+ do {
+ $day = array_pop($days);
+ } while($day > $daysInMonth);
+ }
+ //$this->debug("$extreme day is $day");
+ return $day;
+ }
+
+ function getDaysArray($month, $year=0){
+ if ($year == 0) {
+ $year = $this->now[5];
+ }
+ $this->debug("Getting days for $month");
+ $days = array();
+
+ if ($this->bits[4] != "*"){
+ $days = $this->getWeekDays($this->bits[4], $month, $year);
+ $this->debug("Weekdays:");
+ $this->debug($days);
+ }
+ if ($this->bits[2] != "*" && $this->bits[4] == "*") {
+ $days = $this->expand_ranges($this->bits[2]);
+ }
+ if ($this->bits[2] == "*" && $this->bits[4] == "*"){
+ //Just return every day of the month
+ $daysinmonth = $this->daysinmonth($month, $year);
+ $this->debug("Days in ".$month.", ".$year.": ".$daysinmonth);
+ for($i = 1;$i<=$daysinmonth;$i++){
+ $days[] = $i;
+ }
+ }
+ $this->debug($days);
+
+ return $days;
+ }
+
+ function getExtremeHour($extreme){
+ if ($this->bits[1] == "*"){
+ if ($extreme == "END"){
+ $hour = 23;
+ } else {
+ $hour = 0;
+ }
+ } else {
+ $hours = $this->expand_ranges($this->bits[1]);
+ if ($extreme == "END"){
+ sort($hours);
+ } else {
+ rsort($hours);
+ }
+ $hour = array_pop($hours);
+ }
+ //$this->debug("$extreme hour is $hour");
+ return $hour;
+ }
+
+ function getExtremeMinute($extreme){
+ if ($this->bits[0] == "*"){
+ if ($extreme == "END"){
+ $minute = 59;
+ } else {
+ $minute = 0;
+ }
+ } else {
+ $minutes = $this->expand_ranges($this->bits[0]);
+ if ($extreme == "END"){
+ sort($minutes);
+ } else {
+ rsort($minutes);
+ }
+ $minute = array_pop($minutes);
+ }
+ //$this->debug("$extreme minute is $minute");
+ return $minute;
+ }
+
+}
+
+?> \ No newline at end of file