[TASK] Change "false" into "FALSE"
[Packages/TYPO3.CMS.git] / typo3 / sysext / scheduler / class.tx_scheduler_croncmd.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2008-2011 Markus Friedrich (markus.friedrich@dkd.de)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
24
25 /**
26 * This class provides calulations for the cron command format.
27 *
28 * @author Markus Friedrich <markus.friedrich@dkd.de>
29 * @author Christian Kuhn <lolli@schwarzbu.ch>
30 * @package TYPO3
31 * @subpackage tx_scheduler
32 */
33 class tx_scheduler_CronCmd {
34
35 /**
36 * Normalized sections of the cron command.
37 * Allowed are comma separated lists of integers and the character '*'
38 *
39 * field lower and upper bound
40 * ----- --------------
41 * minute 0-59
42 * hour 0-23
43 * day of month 1-31
44 * month 1-12
45 * day of week 1-7
46 *
47 * @var array $cronCommandSections
48 */
49 protected $cronCommandSections;
50
51 /**
52 * Timestamp of next execution date.
53 * This value starts with 'now + 1 minute' if not set externally
54 * by unit tests. After a call to calculateNextValue() it holds the timestamp of
55 * the next execution date which matches the cron command restrictions.
56 */
57 protected $timestamp;
58
59 /**
60 * Constructor
61 *
62 * @api
63 * @param string $cronCommand: The cron command can hold any combination documented as valid
64 * expression in usual unix like crons like vixiecron. Special commands like @weekly,
65 * ranges, steps and three letter month and weekday abbreviations are allowed.
66 * @param integer $timestamp: optional start time, used in unit tests
67 * @return void
68 */
69 public function __construct($cronCommand, $timestamp = FALSE) {
70 $cronCommand = tx_scheduler_CronCmd_Normalize::normalize($cronCommand);
71
72 // Explode cron command to sections
73 $this->cronCommandSections = t3lib_div::trimExplode(' ', $cronCommand);
74
75 // Initialize the values with the starting time
76 // This takes care that the calculated time is always in the future
77 if ($timestamp === FALSE) {
78 $timestamp = strtotime('+1 minute');
79 } else {
80 $timestamp += 60;
81 }
82 $this->timestamp = $this->roundTimestamp($timestamp);
83 }
84
85 /**
86 * Calulates the date of the next execution.
87 *
88 * @api
89 * @param integer $level (Deprecated) Number of the current level, e.g. 2 is the day level
90 * @return void
91 */
92 public function calculateNextValue($level = NULL) {
93 if (!is_null($level)) {
94 t3lib_div::deprecationLog('The parameter $level is deprecated since TYPO3 version 4.5.');
95 }
96
97 $newTimestamp = $this->getTimestamp();
98
99 // Calculate next minute and hour field
100 $loopCount = 0;
101 while (TRUE) {
102 $loopCount ++;
103 // If there was no match within two days, cron command is invalid.
104 // The second day is needed to catch the summertime leap in some countries.
105 if ($loopCount > 2880) {
106 throw new RuntimeException(
107 'Unable to determine next execution timestamp: Hour and minute combination is invalid.',
108 1291494126
109 );
110 }
111 if ($this->minuteAndHourMatchesCronCommand($newTimestamp)) {
112 break;
113 }
114 $newTimestamp += 60;
115 }
116
117 $loopCount = 0;
118 while (TRUE) {
119 $loopCount ++;
120 // A date must match within the next 4 years, this high number makes
121 // sure leap year cron command configuration are caught.
122 // If the loop runs longer than that, the cron command is invalid.
123 if ($loopCount > 1464) {
124 throw new RuntimeException(
125 'Unable to determine next execution timestamp: Day of month, month and day of week combination is invalid.',
126 1291501280
127 );
128 }
129 if ($this->dayMatchesCronCommand($newTimestamp)) {
130 break;
131 }
132 $newTimestamp += $this->numberOfSecondsInDay($newTimestamp);
133 }
134
135 $this->timestamp = $newTimestamp;
136 }
137
138 /*
139 * Get next timestamp
140 *
141 * @api
142 * @return integer Unix timestamp
143 */
144 public function getTimestamp() {
145 return $this->timestamp;
146 }
147
148 /**
149 * Get cron command sections. Array of strings, each containing either
150 * a list of comma seperated integers or *
151 *
152 * @return array command sections:
153 * 0 => minute
154 * 1 => hour
155 * 2 => day of month
156 * 3 => month
157 * 4 => day of week
158 */
159 public function getCronCommandSections() {
160 return $this->cronCommandSections;
161 }
162
163 /**
164 * Determine if current timestamp matches minute and hour cron command restriction.
165 *
166 * @param integer $timestamp to test
167 * @return boolean TRUE if cron command conditions are met
168 */
169 protected function minuteAndHourMatchesCronCommand($timestamp) {
170 $minute = intval(date('i', $timestamp));
171 $hour = intval(date('G', $timestamp));
172
173 $commandMatch = FALSE;
174 if (
175 $this->isInCommandList($this->cronCommandSections[0], $minute)
176 && $this->isInCommandList($this->cronCommandSections[1], $hour)
177 ) {
178 $commandMatch = TRUE;
179 }
180
181 return $commandMatch;
182 }
183
184 /**
185 * Determine if current timestamp matches day of month, month and day of week
186 * cron command restriction
187 *
188 * @param integer $timestamp to test
189 * @return boolean TRUE if cron command conditions are met
190 */
191 protected function dayMatchesCronCommand($timestamp) {
192 $dayOfMonth = date('j', $timestamp);
193 $month = date('n', $timestamp);
194 $dayOfWeek = date('N', $timestamp);
195
196 $isInDayOfMonth = $this->isInCommandList($this->cronCommandSections[2], $dayOfMonth);
197 $isInMonth = $this->isInCommandList($this->cronCommandSections[3], $month);
198 $isInDayOfWeek = $this->isInCommandList($this->cronCommandSections[4], $dayOfWeek);
199
200 // Quote from vixiecron:
201 // Note: The day of a command's execution can be specified by two fields — day of month, and day of week.
202 // If both fields are restricted (i.e., aren't *), the command will be run when either field
203 // matches the current time. For example, `30 4 1,15 * 5' would cause
204 // a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday.
205
206 $isDayOfMonthRestricted = (string)$this->cronCommandSections[2] === '*' ? FALSE : TRUE;
207 $isDayOfWeekRestricted = (string)$this->cronCommandSections[4] === '*' ? FALSE : TRUE;
208
209 $commandMatch = FALSE;
210 if ($isInMonth) {
211 if (
212 ($isInDayOfMonth && $isDayOfMonthRestricted)
213 || ($isInDayOfWeek && $isDayOfWeekRestricted)
214 || ($isInDayOfMonth && !$isDayOfMonthRestricted && $isInDayOfWeek && !$isDayOfWeekRestricted)
215 ) {
216 $commandMatch = TRUE;
217 }
218 }
219
220 return $commandMatch;
221 }
222
223 /**
224 * Determine if a given number validates a cron command section. The given cron
225 * command must be a 'normalized' list with only comma separated integers or '*'
226 *
227 * @param string $commandExpression: cron command
228 * @param integer $numberToMatch: number to look up
229 * @return boolean TRUE if number is in list
230 */
231 protected function isInCommandList($commandExpression, $numberToMatch) {
232 $inList = FALSE;
233 if ((string)$commandExpression === '*') {
234 $inList = TRUE;
235 } else {
236 $inList = t3lib_div::inList($commandExpression, $numberToMatch);
237 }
238
239 return $inList;
240 }
241
242 /**
243 * Helper method to calculate number of seconds in a day.
244 *
245 * This is not always 86400 (60*60*24) and depends on the timezone:
246 * Some countries like Germany have a summertime / wintertime switch,
247 * on every last sunday in march clocks are forwarded by one hour (set from 2:00 to 3:00),
248 * and on last sunday of october they are set back one hour (from 3:00 to 2:00).
249 * This shortens and lengthens the length of a day by one hour.
250 *
251 * @param integer timestamp
252 * @return integer Number of seconds of day
253 */
254 protected function numberOfSecondsInDay($timestamp) {
255 $now = mktime(0, 0, 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp));
256 // Make sure to be in next day, even if day has 25 hours
257 $nextDay = $now + 60*60*25;
258 $nextDay = mktime(0, 0, 0, date('n', $nextDay), date('j', $nextDay), date('Y', $nextDay));
259
260 return ($nextDay - $now);
261 }
262
263 /**
264 * Round a timestamp down to full minute.
265 *
266 * @param integer timestamp
267 * @return integer Rounded timestamp
268 */
269 protected function roundTimestamp($timestamp) {
270 return mktime(date('H', $timestamp), date('i', $timestamp), 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp));
271 }
272
273 /**
274 * Returns the first value that is higher than the current value
275 * from a list of possible values.
276 *
277 * @deprecated since 4.5
278 * @param mixed $currentValue: the value to be searched in the list
279 * @param array $listArray: the list of values
280 * @return mixed The value from the list right after the current value
281 */
282 public function getNextValue($currentValue, array $listArray) {
283 t3lib_div::deprecationLog('The method is deprecated since TYPO3 version 4.5.');
284
285 $next_value = FALSE;
286
287 $numValues = count($listArray);
288 for ($i = 0; $i < $numValues; $i++) {
289 if ($listArray[$i] > $currentValue) {
290 $next_value = $listArray[$i];
291 break;
292 }
293 }
294
295 return $next_value;
296 }
297
298 /**
299 * Returns the timestamp for the value parts in $this->values.
300 *
301 * @deprecated since 4.5
302 * @return integer unix timestamp
303 */
304 public function getTstamp() {
305 t3lib_div::deprecationLog('The method is deprecated since TYPO3 version 4.5.');
306 return $this->getTimestamp();
307 }
308 }
309
310 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_croncmd.php'])) {
311 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/scheduler/class.tx_scheduler_croncmd.php']);
312 }
313 ?>