‪TYPO3CMS  ‪main
CronCommand.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
19 
24 {
39  protected ‪$cronCommandSections;
40 
48  protected ‪$timestamp;
49 
56  public function ‪__construct($cronCommand, ‪$timestamp = false)
57  {
58  $cronCommand = ‪NormalizeCommand::normalize($cronCommand);
59  // Explode cron command to sections
60  $this->cronCommandSections = ‪GeneralUtility::trimExplode(' ', $cronCommand);
61  // Initialize the values with the starting time
62  // This takes care that the calculated time is always in the future
63  if (‪$timestamp === false) {
64  ‪$timestamp = strtotime('+1 minute');
65  } else {
66  ‪$timestamp += 60;
67  }
68  $this->timestamp = $this->‪roundTimestamp(‪$timestamp);
69  }
70 
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 
115  public function ‪getTimestamp()
116  {
117  return ‪$this->timestamp;
118  }
119 
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): bool
157  {
158  $dayOfMonth = (int)date('j', ‪$timestamp);
159  $month = (int)date('n', ‪$timestamp);
160  $dayOfWeek = (int)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  if ((string)$commandExpression === '*') {
191  $inList = true;
192  } else {
193  $inList = ‪GeneralUtility::inList($commandExpression, (string)$numberToMatch);
194  }
195  return $inList;
196  }
197 
210  protected function ‪numberOfSecondsInDay(‪$timestamp)
211  {
212  $now = mktime(0, 0, 0, (int)date('n', ‪$timestamp), (int)date('j', ‪$timestamp), (int)date('Y', ‪$timestamp));
213  // Make sure to be in next day, even if day has 25 hours
214  $nextDay = $now + 60 * 60 * 25;
215  $nextDay = mktime(0, 0, 0, (int)date('n', $nextDay), (int)date('j', $nextDay), (int)date('Y', $nextDay));
216  return $nextDay - $now;
217  }
218 
225  protected function ‪roundTimestamp(int ‪$timestamp): int
226  {
227  return (int)(floor(‪$timestamp / 60) * 60);
228  }
229 }
‪TYPO3\CMS\Scheduler\CronCommand\NormalizeCommand\normalize
‪static string normalize($cronCommand)
Definition: NormalizeCommand.php:40
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\$cronCommandSections
‪array $cronCommandSections
Definition: CronCommand.php:38
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\isInCommandList
‪bool isInCommandList($commandExpression, $numberToMatch)
Definition: CronCommand.php:186
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\minuteAndHourMatchesCronCommand
‪bool minuteAndHourMatchesCronCommand($timestamp)
Definition: CronCommand.php:136
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\calculateNextValue
‪calculateNextValue()
Definition: CronCommand.php:74
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\$timestamp
‪int $timestamp
Definition: CronCommand.php:46
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\getTimestamp
‪int getTimestamp()
Definition: CronCommand.php:113
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\dayMatchesCronCommand
‪bool dayMatchesCronCommand($timestamp)
Definition: CronCommand.php:154
‪TYPO3\CMS\Scheduler\CronCommand
Definition: CronCommand.php:16
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\numberOfSecondsInDay
‪int numberOfSecondsInDay($timestamp)
Definition: CronCommand.php:208
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand
Definition: CronCommand.php:24
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\__construct
‪__construct($cronCommand, $timestamp=false)
Definition: CronCommand.php:54
‪TYPO3\CMS\Core\Utility\GeneralUtility\inList
‪static bool inList($list, $item)
Definition: GeneralUtility.php:422
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\getCronCommandSections
‪array getCronCommandSections()
Definition: CronCommand.php:125
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822
‪TYPO3\CMS\Scheduler\CronCommand\CronCommand\roundTimestamp
‪int roundTimestamp(int $timestamp)
Definition: CronCommand.php:223