TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
NormalizeCommand.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 
18 
27 {
40  public static function normalize($cronCommand)
41  {
42  $cronCommand = trim($cronCommand);
43  $cronCommand = self::convertKeywordsToCronCommand($cronCommand);
44  $cronCommand = self::normalizeFields($cronCommand);
45  return $cronCommand;
46  }
47 
55  protected static function convertKeywordsToCronCommand($cronCommand)
56  {
57  switch ($cronCommand) {
58  case '@yearly':
59  case '@annually':
60  $cronCommand = '0 0 1 1 *';
61  break;
62  case '@monthly':
63  $cronCommand = '0 0 1 * *';
64  break;
65  case '@weekly':
66  $cronCommand = '0 0 * * 0';
67  break;
68  case '@daily':
69 
70  case '@midnight':
71  $cronCommand = '0 0 * * *';
72  break;
73  case '@hourly':
74  $cronCommand = '0 * * * *';
75  break;
76  }
77  return $cronCommand;
78  }
79 
86  protected static function normalizeFields($cronCommand)
87  {
88  $fieldArray = self::splitFields($cronCommand);
89  $fieldArray[0] = self::normalizeIntegerField($fieldArray[0], 0, 59);
90  $fieldArray[1] = self::normalizeIntegerField($fieldArray[1], 0, 23);
91  $fieldArray[2] = self::normalizeIntegerField($fieldArray[2], 1, 31);
92  $fieldArray[3] = self::normalizeMonthAndWeekdayField($fieldArray[3], true);
93  $fieldArray[4] = self::normalizeMonthAndWeekdayField($fieldArray[4], false);
94  $normalizedCronCommand = implode(' ', $fieldArray);
95  return $normalizedCronCommand;
96  }
97 
105  protected static function splitFields($cronCommand)
106  {
107  $fields = explode(' ', $cronCommand);
108  if (count($fields) !== 5) {
109  throw new \InvalidArgumentException('Unable to split given cron command to five fields.', 1291227373);
110  }
111  return $fields;
112  }
113 
121  protected static function normalizeMonthAndWeekdayField($expression, $isMonthField = true)
122  {
123  if ((string)$expression === '*') {
124  $fieldValues = '*';
125  } else {
126  // Fragment expression by , / and - and substitute three letter code of month and weekday to numbers
127  $listOfCommaValues = explode(',', $expression);
128  $fieldArray = [];
129  foreach ($listOfCommaValues as $listElement) {
130  if (strpos($listElement, '/') !== false) {
131  list($left, $right) = explode('/', $listElement);
132  if (strpos($left, '-') !== false) {
133  list($leftBound, $rightBound) = explode('-', $left);
134  $leftBound = self::normalizeMonthAndWeekday($leftBound, $isMonthField);
135  $rightBound = self::normalizeMonthAndWeekday($rightBound, $isMonthField);
136  $left = $leftBound . '-' . $rightBound;
137  } else {
138  if ((string)$left !== '*') {
139  $left = self::normalizeMonthAndWeekday($left, $isMonthField);
140  }
141  }
142  $fieldArray[] = $left . '/' . $right;
143  } elseif (strpos($listElement, '-') !== false) {
144  list($left, $right) = explode('-', $listElement);
145  $left = self::normalizeMonthAndWeekday($left, $isMonthField);
146  $right = self::normalizeMonthAndWeekday($right, $isMonthField);
147  $fieldArray[] = $left . '-' . $right;
148  } else {
149  $fieldArray[] = self::normalizeMonthAndWeekday($listElement, $isMonthField);
150  }
151  }
152  $fieldValues = implode(',', $fieldArray);
153  }
154  return $isMonthField ? self::normalizeIntegerField($fieldValues, 1, 12) : self::normalizeIntegerField($fieldValues, 1, 7);
155  }
156 
166  protected static function normalizeIntegerField($expression, $lowerBound = 0, $upperBound = 59)
167  {
168  if ((string)$expression === '*') {
169  $fieldValues = '*';
170  } else {
171  $listOfCommaValues = explode(',', $expression);
172  $fieldArray = [];
173  foreach ($listOfCommaValues as $listElement) {
174  if (strpos($listElement, '/') !== false) {
175  list($left, $right) = explode('/', $listElement);
176  if ((string)$left === '*') {
177  $leftList = self::convertRangeToListOfValues($lowerBound . '-' . $upperBound);
178  } else {
179  $leftList = self::convertRangeToListOfValues($left);
180  }
181  $fieldArray[] = self::reduceListOfValuesByStepValue($leftList . '/' . $right);
182  } elseif (strpos($listElement, '-') !== false) {
183  $fieldArray[] = self::convertRangeToListOfValues($listElement);
184  } elseif (MathUtility::canBeInterpretedAsInteger($listElement)) {
185  $fieldArray[] = $listElement;
186  } else {
187  throw new \InvalidArgumentException('Unable to normalize integer field.', 1291429389);
188  }
189  }
190  $fieldValues = implode(',', $fieldArray);
191  }
192  if ((string)$fieldValues === '') {
193  throw new \InvalidArgumentException('Unable to convert integer field to list of values: Result list empty.', 1291422012);
194  }
195  if ((string)$fieldValues !== '*') {
196  $fieldList = explode(',', $fieldValues);
197  sort($fieldList);
198  $fieldList = array_unique($fieldList);
199  if (current($fieldList) < $lowerBound) {
200  throw new \InvalidArgumentException('Lowest element in list is smaller than allowed.', 1291470084);
201  }
202  if (end($fieldList) > $upperBound) {
203  throw new \InvalidArgumentException('An element in the list is higher than allowed.', 1291470170);
204  }
205  $fieldValues = implode(',', $fieldList);
206  }
207  return (string)$fieldValues;
208  }
209 
217  protected static function convertRangeToListOfValues($range)
218  {
219  if ((string)$range === '') {
220  throw new \InvalidArgumentException('Unable to convert range to list of values with empty string.', 1291234985);
221  }
222  $rangeArray = explode('-', $range);
223  // Sanitize fields and cast to integer
224  foreach ($rangeArray as $fieldNumber => $fieldValue) {
225  if (!MathUtility::canBeInterpretedAsInteger($fieldValue)) {
226  throw new \InvalidArgumentException('Unable to convert value to integer.', 1291237668);
227  }
228  $rangeArray[$fieldNumber] = (int)$fieldValue;
229  }
230 
231  $rangeArrayCount = count($rangeArray);
232  if ($rangeArrayCount === 1) {
233  $resultList = $rangeArray[0];
234  } elseif ($rangeArrayCount === 2) {
235  $left = $rangeArray[0];
236  $right = $rangeArray[1];
237  if ($left > $right) {
238  throw new \InvalidArgumentException('Unable to convert range to list: Left integer must not be greater than right integer.', 1291237145);
239  }
240  $resultListArray = [];
241  for ($i = $left; $i <= $right; $i++) {
242  $resultListArray[] = $i;
243  }
244  $resultList = implode(',', $resultListArray);
245  } else {
246  throw new \InvalidArgumentException('Unable to convert range to list of values.', 1291234986);
247  }
248  return (string)$resultList;
249  }
250 
261  protected static function reduceListOfValuesByStepValue($stepExpression)
262  {
263  if ($stepExpression === '') {
264  throw new \InvalidArgumentException('Unable to convert step values.', 1291234987);
265  }
266  $stepValuesAndStepArray = explode('/', $stepExpression);
267  $stepValuesAndStepArrayCount = count($stepValuesAndStepArray);
268  if ($stepValuesAndStepArrayCount < 1 || $stepValuesAndStepArrayCount > 2) {
269  throw new \InvalidArgumentException('Unable to convert step values: Multiple slashes found.', 1291242168);
270  }
271  $left = $stepValuesAndStepArray[0];
272  $right = $stepValuesAndStepArray[1];
273  if ((string)$stepValuesAndStepArray[0] === '') {
274  throw new \InvalidArgumentException('Unable to convert step values: Left part of / is empty.', 1291414955);
275  }
276  if ((string)$stepValuesAndStepArray[1] === '') {
277  throw new \InvalidArgumentException('Unable to convert step values: Right part of / is empty.', 1291414956);
278  }
280  throw new \InvalidArgumentException('Unable to convert step values: Right part must be a single integer.', 1291414957);
281  }
282  $right = (int)$right;
283  $leftArray = explode(',', $left);
284  $validValues = [];
285  $currentStep = $right;
286  foreach ($leftArray as $leftValue) {
287  if (!MathUtility::canBeInterpretedAsInteger($leftValue)) {
288  throw new \InvalidArgumentException('Unable to convert step values: Left part must be a single integer or comma separated list of integers.', 1291414958);
289  }
290  if ($currentStep === 0) {
291  $currentStep = $right;
292  }
293  if ($currentStep === $right) {
294  $validValues[] = (int)$leftValue;
295  }
296  $currentStep--;
297  }
298  if (empty($validValues)) {
299  throw new \InvalidArgumentException('Unable to convert step values: Result value list is empty.', 1291414959);
300  }
301  return implode(',', $validValues);
302  }
303 
311  protected static function normalizeMonthAndWeekday($expression, $isMonth = true)
312  {
313  $expression = $isMonth ? self::normalizeMonth($expression) : self::normalizeWeekday($expression);
314  return (string)$expression;
315  }
316 
325  protected static function normalizeMonth($month)
326  {
327  $timestamp = strtotime('2010-' . $month . '-01');
328  // timestamp must be >= 2010-01-01 and <= 2010-12-01
329  if (!$timestamp || $timestamp < strtotime('2010-01-01') || $timestamp > strtotime('2010-12-01')) {
330  throw new \InvalidArgumentException('Unable to convert given month name.', 1291083486);
331  }
332  return (int)date('n', $timestamp);
333  }
334 
343  protected static function normalizeWeekday($weekday)
344  {
345  $normalizedWeekday = false;
346  // 0 (sunday) -> 7
347  if ((string)$weekday === '0') {
348  $weekday = 7;
349  }
350  if ($weekday >= 1 && $weekday <= 7) {
351  $normalizedWeekday = (int)$weekday;
352  }
353  if (!$normalizedWeekday) {
354  // Convert string representation like 'sun' to integer
355  $timestamp = strtotime('next ' . $weekday, mktime(0, 0, 0, 1, 1, 2010));
356  if (!$timestamp || $timestamp < strtotime('2010-01-01') || $timestamp > strtotime('2010-01-08')) {
357  throw new \InvalidArgumentException('Unable to convert given weekday name.', 1291163589);
358  }
359  $normalizedWeekday = (int)date('N', $timestamp);
360  }
361  return $normalizedWeekday;
362  }
363 }
static normalizeIntegerField($expression, $lowerBound=0, $upperBound=59)
static normalizeMonthAndWeekday($expression, $isMonth=true)
static normalizeMonthAndWeekdayField($expression, $isMonthField=true)