|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2008-2011 Markus Friedrich (markus.friedrich@dkd.de) 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * 00017 * This script is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU General Public License for more details. 00021 * 00022 * This copyright notice MUST APPEAR in all copies of the script! 00023 ***************************************************************/ 00024 00025 /** 00026 * This class provides calulations for the cron command format. 00027 * 00028 * @author Markus Friedrich <markus.friedrich@dkd.de> 00029 * @author Christian Kuhn <lolli@schwarzbu.ch> 00030 * @package TYPO3 00031 * @subpackage tx_scheduler 00032 */ 00033 class tx_scheduler_CronCmd { 00034 00035 /** 00036 * Normalized sections of the cron command. 00037 * Allowed are comma separated lists of integers and the character '*' 00038 * 00039 * field lower and upper bound 00040 * ----- -------------- 00041 * minute 0-59 00042 * hour 0-23 00043 * day of month 1-31 00044 * month 1-12 00045 * day of week 1-7 00046 * 00047 * @var array $cronCommandSections 00048 */ 00049 protected $cronCommandSections; 00050 00051 /** 00052 * Timestamp of next execution date. 00053 * This value starts with 'now + 1 minute' if not set externally 00054 * by unit tests. After a call to calculateNextValue() it holds the timestamp of 00055 * the next execution date which matches the cron command restrictions. 00056 */ 00057 protected $timestamp; 00058 00059 /** 00060 * Constructor 00061 * 00062 * @api 00063 * @param string $cronCommand: The cron command can hold any combination documented as valid 00064 * expression in usual unix like crons like vixiecron. Special commands like @weekly, 00065 * ranges, steps and three letter month and weekday abbreviations are allowed. 00066 * @param integer $timestamp: optional start time, used in unit tests 00067 * @return void 00068 */ 00069 public function __construct($cronCommand, $timestamp = FALSE) { 00070 $cronCommand = tx_scheduler_CronCmd_Normalize::normalize($cronCommand); 00071 00072 // Explode cron command to sections 00073 $this->cronCommandSections = t3lib_div::trimExplode(' ', $cronCommand); 00074 00075 // Initialize the values with the starting time 00076 // This takes care that the calculated time is always in the future 00077 if ($timestamp === FALSE) { 00078 $timestamp = strtotime('+1 minute'); 00079 } else { 00080 $timestamp += 60; 00081 } 00082 $this->timestamp = $this->roundTimestamp($timestamp); 00083 } 00084 00085 /** 00086 * Calulates the date of the next execution. 00087 * 00088 * @api 00089 * @param integer $level (Deprecated) Number of the current level, e.g. 2 is the day level 00090 * @return void 00091 */ 00092 public function calculateNextValue($level = NULL) { 00093 if (!is_null($level)) { 00094 t3lib_div::deprecationLog('The parameter $level is deprecated since TYPO3 version 4.5.'); 00095 } 00096 00097 $newTimestamp = $this->getTimestamp(); 00098 00099 // Calculate next minute and hour field 00100 $loopCount = 0; 00101 while (TRUE) { 00102 $loopCount ++; 00103 // If there was no match within two days, cron command is invalid. 00104 // The second day is needed to catch the summertime leap in some countries. 00105 if ($loopCount > 2880) { 00106 throw new RuntimeException( 00107 'Unable to determine next execution timestamp: Hour and minute combination is invalid.', 00108 1291494126 00109 ); 00110 } 00111 if ($this->minuteAndHourMatchesCronCommand($newTimestamp)) { 00112 break; 00113 } 00114 $newTimestamp += 60; 00115 } 00116 00117 $loopCount = 0; 00118 while (TRUE) { 00119 $loopCount ++; 00120 // A date must match within the next 4 years, this high number makes 00121 // sure leap year cron command configuration are caught. 00122 // If the loop runs longer than that, the cron command is invalid. 00123 if ($loopCount > 1464) { 00124 throw new RuntimeException( 00125 'Unable to determine next execution timestamp: Day of month, month and day of week combination is invalid.', 00126 1291501280 00127 ); 00128 } 00129 if ($this->dayMatchesCronCommand($newTimestamp)) { 00130 break; 00131 } 00132 $newTimestamp += $this->numberOfSecondsInDay($newTimestamp); 00133 } 00134 00135 $this->timestamp = $newTimestamp; 00136 } 00137 00138 /* 00139 * Get next timestamp 00140 * 00141 * @api 00142 * @return integer Unix timestamp 00143 */ 00144 public function getTimestamp() { 00145 return $this->timestamp; 00146 } 00147 00148 /** 00149 * Get cron command sections. Array of strings, each containing either 00150 * a list of comma seperated integers or * 00151 * 00152 * @return array command sections: 00153 * 0 => minute 00154 * 1 => hour 00155 * 2 => day of month 00156 * 3 => month 00157 * 4 => day of week 00158 */ 00159 public function getCronCommandSections() { 00160 return $this->cronCommandSections; 00161 } 00162 00163 /** 00164 * Determine if current timestamp matches minute and hour cron command restriction. 00165 * 00166 * @param integer $timestamp to test 00167 * @return boolean TRUE if cron command conditions are met 00168 */ 00169 protected function minuteAndHourMatchesCronCommand($timestamp) { 00170 $minute = intval(date('i', $timestamp)); 00171 $hour = intval(date('G', $timestamp)); 00172 00173 $commandMatch = FALSE; 00174 if ( 00175 $this->isInCommandList($this->cronCommandSections[0], $minute) 00176 && $this->isInCommandList($this->cronCommandSections[1], $hour) 00177 ) { 00178 $commandMatch = TRUE; 00179 } 00180 00181 return $commandMatch; 00182 } 00183 00184 /** 00185 * Determine if current timestamp matches day of month, month and day of week 00186 * cron command restriction 00187 * 00188 * @param integer $timestamp to test 00189 * @return boolean TRUE if cron command conditions are met 00190 */ 00191 protected function dayMatchesCronCommand($timestamp) { 00192 $dayOfMonth = date('j', $timestamp); 00193 $month = date('n', $timestamp); 00194 $dayOfWeek = date('N', $timestamp); 00195 00196 $isInDayOfMonth = $this->isInCommandList($this->cronCommandSections[2], $dayOfMonth); 00197 $isInMonth = $this->isInCommandList($this->cronCommandSections[3], $month); 00198 $isInDayOfWeek = $this->isInCommandList($this->cronCommandSections[4], $dayOfWeek); 00199 00200 // Quote from vixiecron: 00201 // Note: The day of a command's execution can be specified by two fields — day of month, and day of week. 00202 // If both fields are restricted (i.e., aren't *), the command will be run when either field 00203 // matches the current time. For example, `30 4 1,15 * 5' would cause 00204 // a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday. 00205 00206 $isDayOfMonthRestricted = (string)$this->cronCommandSections[2] === '*' ? FALSE : TRUE; 00207 $isDayOfWeekRestricted = (string)$this->cronCommandSections[4] === '*' ? FALSE : TRUE; 00208 00209 $commandMatch = FALSE; 00210 if ($isInMonth) { 00211 if ( 00212 ($isInDayOfMonth && $isDayOfMonthRestricted) 00213 || ($isInDayOfWeek && $isDayOfWeekRestricted) 00214 || ($isInDayOfMonth && !$isDayOfMonthRestricted && $isInDayOfWeek && !$isDayOfWeekRestricted) 00215 ) { 00216 $commandMatch = TRUE; 00217 } 00218 } 00219 00220 return $commandMatch; 00221 } 00222 00223 /** 00224 * Determine if a given number validates a cron command section. The given cron 00225 * command must be a 'normalized' list with only comma separated integers or '*' 00226 * 00227 * @param string $commandExpression: cron command 00228 * @param integer $numberToMatch: number to look up 00229 * @return boolean TRUE if number is in list 00230 */ 00231 protected function isInCommandList($commandExpression, $numberToMatch) { 00232 $inList = FALSE; 00233 if ((string)$commandExpression === '*') { 00234 $inList = TRUE; 00235 } else { 00236 $inList = t3lib_div::inList($commandExpression, $numberToMatch); 00237 } 00238 00239 return $inList; 00240 } 00241 00242 /** 00243 * Helper method to calculate number of seconds in a day. 00244 * 00245 * This is not always 86400 (60*60*24) and depends on the timezone: 00246 * Some countries like Germany have a summertime / wintertime switch, 00247 * on every last sunday in march clocks are forwarded by one hour (set from 2:00 to 3:00), 00248 * and on last sunday of october they are set back one hour (from 3:00 to 2:00). 00249 * This shortens and lengthens the length of a day by one hour. 00250 * 00251 * @param integer timestamp 00252 * @return integer Number of seconds of day 00253 */ 00254 protected function numberOfSecondsInDay($timestamp) { 00255 $now = mktime(0, 0, 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp)); 00256 // Make sure to be in next day, even if day has 25 hours 00257 $nextDay = $now + 60*60*25; 00258 $nextDay = mktime(0, 0, 0, date('n', $nextDay), date('j', $nextDay), date('Y', $nextDay)); 00259 00260 return ($nextDay - $now); 00261 } 00262 00263 /** 00264 * Round a timestamp down to full minute. 00265 * 00266 * @param integer timestamp 00267 * @return integer Rounded timestamp 00268 */ 00269 protected function roundTimestamp($timestamp) { 00270 return mktime(date('H', $timestamp), date('i', $timestamp), 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp)); 00271 } 00272 00273 /** 00274 * Returns the first value that is higher than the current value 00275 * from a list of possible values. 00276 * 00277 * @deprecated since 4.5 00278 * @param mixed $currentValue: the value to be searched in the list 00279 * @param array $listArray: the list of values 00280 * @return mixed The value from the list right after the current value 00281 */ 00282 public function getNextValue($currentValue, array $listArray) { 00283 t3lib_div::deprecationLog('The method is deprecated since TYPO3 version 4.5.'); 00284 00285 $next_value = false; 00286 00287 $numValues = count($listArray); 00288 for ($i = 0; $i < $numValues; $i++) { 00289 if ($listArray[$i] > $currentValue) { 00290 $next_value = $listArray[$i]; 00291 break; 00292 } 00293 } 00294 00295 return $next_value; 00296 } 00297 00298 /** 00299 * Returns the timestamp for the value parts in $this->values. 00300 * 00301 * @deprecated since 4.5 00302 * @return integer unix timestamp 00303 */ 00304 public function getTstamp() { 00305 t3lib_div::deprecationLog('The method is deprecated since TYPO3 version 4.5.'); 00306 return $this->getTimestamp(); 00307 } 00308 } 00309 00310 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_croncmd.php'])) { 00311 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_croncmd.php']); 00312 }
1.8.0