TYPO3  7.6
CronCommand.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Scheduler\CronCommand;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
21 {
37 
44  protected $timestamp;
45 
54  public function __construct($cronCommand, $timestamp = false)
55  {
56  $cronCommand = NormalizeCommand::normalize($cronCommand);
57  // Explode cron command to sections
58  $this->cronCommandSections = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(' ', $cronCommand);
59  // Initialize the values with the starting time
60  // This takes care that the calculated time is always in the future
61  if ($timestamp === false) {
62  $timestamp = strtotime('+1 minute');
63  } else {
64  $timestamp += 60;
65  }
66  $this->timestamp = $this->roundTimestamp($timestamp);
67  }
68 
76  public function calculateNextValue()
77  {
78  $newTimestamp = $this->getTimestamp();
79  // Calculate next minute and hour field
80  $loopCount = 0;
81  while (true) {
82  $loopCount++;
83  // If there was no match within two days, cron command is invalid.
84  // The second day is needed to catch the summertime leap in some countries.
85  if ($loopCount > 2880) {
86  throw new \RuntimeException('Unable to determine next execution timestamp: Hour and minute combination is invalid.', 1291494126);
87  }
88  if ($this->minuteAndHourMatchesCronCommand($newTimestamp)) {
89  break;
90  }
91  $newTimestamp += 60;
92  }
93  $loopCount = 0;
94  while (true) {
95  $loopCount++;
96  // A date must match within the next 4 years, this high number makes
97  // sure leap year cron command configuration are caught.
98  // If the loop runs longer than that, the cron command is invalid.
99  if ($loopCount > 1464) {
100  throw new \RuntimeException('Unable to determine next execution timestamp: Day of month, month and day of week combination is invalid.', 1291501280);
101  }
102  if ($this->dayMatchesCronCommand($newTimestamp)) {
103  break;
104  }
105  $newTimestamp += $this->numberOfSecondsInDay($newTimestamp);
106  }
107  $this->timestamp = $newTimestamp;
108  }
109 
116  public function getTimestamp()
117  {
118  return $this->timestamp;
119  }
120 
127  public function getCronCommandSections()
128  {
130  }
131 
139  {
140  $minute = (int)date('i', $timestamp);
141  $hour = (int)date('G', $timestamp);
142  $commandMatch = false;
143  if ($this->isInCommandList($this->cronCommandSections[0], $minute) && $this->isInCommandList($this->cronCommandSections[1], $hour)) {
144  $commandMatch = true;
145  }
146  return $commandMatch;
147  }
148 
156  protected function dayMatchesCronCommand($timestamp)
157  {
158  $dayOfMonth = date('j', $timestamp);
159  $month = date('n', $timestamp);
160  $dayOfWeek = date('N', $timestamp);
161  $isInDayOfMonth = $this->isInCommandList($this->cronCommandSections[2], $dayOfMonth);
162  $isInMonth = $this->isInCommandList($this->cronCommandSections[3], $month);
163  $isInDayOfWeek = $this->isInCommandList($this->cronCommandSections[4], $dayOfWeek);
164  // Quote from vixiecron:
165  // Note: The day of a command's execution can be specified by two fields — day of month, and day of week.
166  // If both fields are restricted (i.e., aren't *), the command will be run when either field
167  // matches the current time. For example, `30 4 1,15 * 5' would cause
168  // a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday.
169  $isDayOfMonthRestricted = (string)$this->cronCommandSections[2] !== '*';
170  $isDayOfWeekRestricted = (string)$this->cronCommandSections[4] !== '*';
171  $commandMatch = false;
172  if ($isInMonth) {
173  if ($isInDayOfMonth && $isDayOfMonthRestricted || $isInDayOfWeek && $isDayOfWeekRestricted || $isInDayOfMonth && !$isDayOfMonthRestricted && $isInDayOfWeek && !$isDayOfWeekRestricted) {
174  $commandMatch = true;
175  }
176  }
177  return $commandMatch;
178  }
179 
188  protected function isInCommandList($commandExpression, $numberToMatch)
189  {
190  $inList = false;
191  if ((string)$commandExpression === '*') {
192  $inList = true;
193  } else {
194  $inList = \TYPO3\CMS\Core\Utility\GeneralUtility::inList($commandExpression, $numberToMatch);
195  }
196  return $inList;
197  }
198 
211  protected function numberOfSecondsInDay($timestamp)
212  {
213  $now = mktime(0, 0, 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp));
214  // Make sure to be in next day, even if day has 25 hours
215  $nextDay = $now + 60 * 60 * 25;
216  $nextDay = mktime(0, 0, 0, date('n', $nextDay), date('j', $nextDay), date('Y', $nextDay));
217  return $nextDay - $now;
218  }
219 
226  protected function roundTimestamp($timestamp)
227  {
228  return mktime(date('H', $timestamp), date('i', $timestamp), 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp));
229  }
230 }