TYPO3 CMS  TYPO3_8-7
CronCommand.php
Go to the documentation of this file.
1 <?php
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 
53  public function __construct($cronCommand, $timestamp = false)
54  {
55  $cronCommand = NormalizeCommand::normalize($cronCommand);
56  // Explode cron command to sections
57  $this->cronCommandSections = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(' ', $cronCommand);
58  // Initialize the values with the starting time
59  // This takes care that the calculated time is always in the future
60  if ($timestamp === false) {
61  $timestamp = strtotime('+1 minute');
62  } else {
63  $timestamp += 60;
64  }
65  $this->timestamp = $this->roundTimestamp($timestamp);
66  }
67 
74  public function calculateNextValue()
75  {
76  $newTimestamp = $this->getTimestamp();
77  // Calculate next minute and hour field
78  $loopCount = 0;
79  while (true) {
80  $loopCount++;
81  // If there was no match within two days, cron command is invalid.
82  // The second day is needed to catch the summertime leap in some countries.
83  if ($loopCount > 2880) {
84  throw new \RuntimeException('Unable to determine next execution timestamp: Hour and minute combination is invalid.', 1291494126);
85  }
86  if ($this->minuteAndHourMatchesCronCommand($newTimestamp)) {
87  break;
88  }
89  $newTimestamp += 60;
90  }
91  $loopCount = 0;
92  while (true) {
93  $loopCount++;
94  // A date must match within the next 4 years, this high number makes
95  // sure leap year cron command configuration are caught.
96  // If the loop runs longer than that, the cron command is invalid.
97  if ($loopCount > 1464) {
98  throw new \RuntimeException('Unable to determine next execution timestamp: Day of month, month and day of week combination is invalid.', 1291501280);
99  }
100  if ($this->dayMatchesCronCommand($newTimestamp)) {
101  break;
102  }
103  $newTimestamp += $this->numberOfSecondsInDay($newTimestamp);
104  }
105  $this->timestamp = $newTimestamp;
106  }
107 
114  public function getTimestamp()
115  {
116  return $this->timestamp;
117  }
118 
125  public function getCronCommandSections()
126  {
128  }
129 
137  {
138  $minute = (int)date('i', $timestamp);
139  $hour = (int)date('G', $timestamp);
140  $commandMatch = false;
141  if ($this->isInCommandList($this->cronCommandSections[0], $minute) && $this->isInCommandList($this->cronCommandSections[1], $hour)) {
142  $commandMatch = true;
143  }
144  return $commandMatch;
145  }
146 
154  protected function dayMatchesCronCommand($timestamp)
155  {
156  $dayOfMonth = date('j', $timestamp);
157  $month = date('n', $timestamp);
158  $dayOfWeek = date('N', $timestamp);
159  $isInDayOfMonth = $this->isInCommandList($this->cronCommandSections[2], $dayOfMonth);
160  $isInMonth = $this->isInCommandList($this->cronCommandSections[3], $month);
161  $isInDayOfWeek = $this->isInCommandList($this->cronCommandSections[4], $dayOfWeek);
162  // Quote from vixiecron:
163  // Note: The day of a command's execution can be specified by two fields — day of month, and day of week.
164  // If both fields are restricted (i.e., aren't *), the command will be run when either field
165  // matches the current time. For example, `30 4 1,15 * 5' would cause
166  // a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday.
167  $isDayOfMonthRestricted = (string)$this->cronCommandSections[2] !== '*';
168  $isDayOfWeekRestricted = (string)$this->cronCommandSections[4] !== '*';
169  $commandMatch = false;
170  if ($isInMonth) {
171  if ($isInDayOfMonth && $isDayOfMonthRestricted || $isInDayOfWeek && $isDayOfWeekRestricted || $isInDayOfMonth && !$isDayOfMonthRestricted && $isInDayOfWeek && !$isDayOfWeekRestricted) {
172  $commandMatch = true;
173  }
174  }
175  return $commandMatch;
176  }
177 
186  protected function isInCommandList($commandExpression, $numberToMatch)
187  {
188  if ((string)$commandExpression === '*') {
189  $inList = true;
190  } else {
191  $inList = \TYPO3\CMS\Core\Utility\GeneralUtility::inList($commandExpression, $numberToMatch);
192  }
193  return $inList;
194  }
195 
208  protected function numberOfSecondsInDay($timestamp)
209  {
210  $now = mktime(0, 0, 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp));
211  // Make sure to be in next day, even if day has 25 hours
212  $nextDay = $now + 60 * 60 * 25;
213  $nextDay = mktime(0, 0, 0, date('n', $nextDay), date('j', $nextDay), date('Y', $nextDay));
214  return $nextDay - $now;
215  }
216 
223  protected function roundTimestamp($timestamp)
224  {
225  return mktime(date('H', $timestamp), date('i', $timestamp), 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp));
226  }
227 }
isInCommandList($commandExpression, $numberToMatch)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
__construct($cronCommand, $timestamp=false)
Definition: CronCommand.php:53