|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2010-2011 Christian Kuhn <lolli@schwarzbu.ch> 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * 00017 * This script is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU General Public License for more details. 00021 * 00022 * This copyright notice MUST APPEAR in all copies of the script! 00023 ***************************************************************/ 00024 00025 /** 00026 * Validate and normalize a cron command. 00027 * 00028 * Special fields like three letter weekdays, ranges and steps are substituted 00029 * to a comma separated list of integers. Example: 00030 * '2-4 10-40/10 * mar * fri' will be nolmalized to '2,4 10,20,30,40 * * 3 1,2' 00031 * 00032 * @author Christian Kuhn <lolli@schwarzbu.ch> 00033 * 00034 * @package TYPO3 00035 * @subpackage scheduler 00036 */ 00037 final class tx_scheduler_CronCmd_Normalize { 00038 00039 /** 00040 * Main API method: Get the cron command and normalize it. 00041 * 00042 * If no exception is thrown, the resulting cron command is validated 00043 * and consists of five whitespace separated fields, which are either 00044 * the letter '*' or a sorted, unique comma separated list of integers. 00045 * 00046 * @api 00047 * @throws InvalidArgumentException cron command is invalid or out of bounds 00048 * @param string $cronCommand The cron command to normalize 00049 * @return string Normalized cron command 00050 */ 00051 public static function normalize($cronCommand) { 00052 $cronCommand = trim($cronCommand); 00053 $cronCommand = self::convertKeywordsToCronCommand($cronCommand); 00054 $cronCommand = self::normalizeFields($cronCommand); 00055 return $cronCommand; 00056 } 00057 00058 /** 00059 * Accept special cron command keywords and convert to standard cron syntax. 00060 * Allowed keywords: @yearly, @annually, @monthly, @weekly, @daily, @midnight, @hourly 00061 * 00062 * @params string $cronCommand cron command 00063 * @return string Normalized cron command if keyword was found, else unchanged cron command 00064 */ 00065 public static function convertKeywordsToCronCommand($cronCommand) { 00066 switch ($cronCommand) { 00067 case '@yearly': 00068 case '@annually': 00069 $cronCommand = '0 0 1 1 *'; 00070 break; 00071 case '@monthly': 00072 $cronCommand = '0 0 1 * *'; 00073 break; 00074 case '@weekly': 00075 $cronCommand = '0 0 * * 0'; 00076 break; 00077 case '@daily': 00078 case '@midnight': 00079 $cronCommand = '0 0 * * *'; 00080 break; 00081 case '@hourly': 00082 $cronCommand = '0 * * * *'; 00083 break; 00084 } 00085 00086 return $cronCommand; 00087 } 00088 00089 /** 00090 * Normalize cron command field to list of integers or * 00091 * 00092 * @param string $cronCommand cron command 00093 * @return string Normalized cron command 00094 */ 00095 public static function normalizeFields($cronCommand) { 00096 $fieldArray = self::splitFields($cronCommand); 00097 00098 $fieldArray[0] = self::normalizeIntegerField($fieldArray[0], 0, 59); 00099 $fieldArray[1] = self::normalizeIntegerField($fieldArray[1], 0, 23); 00100 $fieldArray[2] = self::normalizeIntegerField($fieldArray[2], 1, 31); 00101 $fieldArray[3] = self::normalizeMonthAndWeekdayField($fieldArray[3], TRUE); 00102 $fieldArray[4] = self::normalizeMonthAndWeekdayField($fieldArray[4], FALSE); 00103 00104 $normalizedCronCommand = implode(' ', $fieldArray); 00105 return $normalizedCronCommand; 00106 } 00107 00108 /** 00109 * Split a given cron command like '23 * * * *' to an array with five fields. 00110 * 00111 * @throws InvalidArgumentException If splitted array does not contain five entries 00112 * @param string $cronCommand cron command 00113 * @return array 00114 * 0 => minute field 00115 * 1 => hour field 00116 * 2 => day of month field 00117 * 3 => month field 00118 * 4 => day of week field 00119 */ 00120 public static function splitFields($cronCommand) { 00121 $fields = explode(' ', $cronCommand); 00122 00123 if (count($fields) !== 5) { 00124 throw new InvalidArgumentException( 00125 'Unable to split given cron command to five fields.', 00126 1291227373 00127 ); 00128 } 00129 00130 return $fields; 00131 } 00132 00133 /** 00134 * Normalize month field. 00135 * 00136 * @param string $expression Month field expression 00137 * @param boolean $isMonthField True if month field is handled, false for weekday field 00138 * @return string Normalized expression 00139 */ 00140 public static function normalizeMonthAndWeekdayField($expression, $isMonthField = TRUE) { 00141 if ((string)$expression === '*') { 00142 $fieldValues = '*'; 00143 } else { 00144 // Fragment espression by , / and - and substitute three letter code of month and weekday to numbers 00145 $listOfCommaValues = explode(',', $expression); 00146 $fieldArray = array(); 00147 foreach ($listOfCommaValues as $listElement) { 00148 if (strpos($listElement, '/') !== FALSE) { 00149 list($left, $right) = explode('/', $listElement); 00150 if (strpos($left, '-') !== FALSE) { 00151 list($leftBound, $rightBound) = explode('-', $left); 00152 $leftBound = self::normalizeMonthAndWeekday($leftBound, $isMonthField); 00153 $rightBound = self::normalizeMonthAndWeekday($rightBound, $isMonthField); 00154 $left = $leftBound . '-' . $rightBound; 00155 } else { 00156 if ((string)$left !== '*') { 00157 $left = self::normalizeMonthAndWeekday($left, $isMonthField); 00158 } 00159 } 00160 $fieldArray[] = $left . '/' . $right; 00161 } elseif (strpos($listElement, '-') !== FALSE) { 00162 list($left, $right) = explode('-', $listElement); 00163 $left = self::normalizeMonthAndWeekday($left, $isMonthField); 00164 $right = self::normalizeMonthAndWeekday($right, $isMonthField); 00165 $fieldArray[] = $left . '-' . $right; 00166 } else { 00167 $fieldArray[] = self::normalizeMonthAndWeekday($listElement, $isMonthField); 00168 } 00169 } 00170 $fieldValues = implode(',', $fieldArray); 00171 } 00172 00173 return $isMonthField ? self::normalizeIntegerField($fieldValues, 1, 12) : self::normalizeIntegerField($fieldValues, 1, 7); 00174 } 00175 00176 /** 00177 * Normalize integer field. 00178 * 00179 * @throws InvalidArgumentException If field is invalid or out of bounds 00180 * @param string $expression Expression 00181 * @param integer $lowerBound Lower limit of result list 00182 * @param integer $upperBound Upper limit of result list 00183 * @return string Normalized expression 00184 */ 00185 public static function normalizeIntegerField($expression, $lowerBound = 0, $upperBound = 59) { 00186 if ((string)$expression === '*') { 00187 $fieldValues = '*'; 00188 } else { 00189 $listOfCommaValues = explode(',', $expression); 00190 $fieldArray = array(); 00191 foreach ($listOfCommaValues as $listElement) { 00192 if (strpos($listElement, '/') !== FALSE) { 00193 list($left, $right) = explode('/', $listElement); 00194 if ((string)$left === '*') { 00195 $leftList = self::convertRangeToListOfValues($lowerBound . '-' . $upperBound); 00196 } else { 00197 $leftList = self::convertRangeToListOfValues($left); 00198 } 00199 $fieldArray[] = self::reduceListOfValuesByStepValue($leftList . '/' . $right); 00200 } elseif (strpos($listElement, '-') !== FALSE) { 00201 $fieldArray[] = self::convertRangeToListOfValues($listElement); 00202 } elseif (strcmp(intval($listElement), $listElement) === 0) { 00203 $fieldArray[] = $listElement; 00204 } else { 00205 throw new InvalidArgumentException( 00206 'Unable to normalize integer field.', 00207 1291429389 00208 ); 00209 } 00210 } 00211 $fieldValues = implode(',', $fieldArray); 00212 } 00213 00214 if (strlen($fieldValues) === 0) { 00215 throw new InvalidArgumentException( 00216 'Unable to convert integer field to list of values: Result list empty.', 00217 1291422012 00218 ); 00219 } 00220 00221 if ((string)$fieldValues !== '*') { 00222 $fieldList = explode(',', $fieldValues); 00223 00224 sort($fieldList); 00225 $fieldList = array_unique($fieldList); 00226 00227 if (current($fieldList) < $lowerBound) { 00228 throw new InvalidArgumentException( 00229 'Lowest element in list is smaller than allowed.', 00230 1291470084 00231 ); 00232 } 00233 00234 if (end($fieldList) > $upperBound) { 00235 throw new InvalidArgumentException( 00236 'An element in the list is higher than allowed.', 00237 1291470170 00238 ); 00239 } 00240 00241 $fieldValues = implode(',', $fieldList); 00242 } 00243 00244 return (string)$fieldValues; 00245 } 00246 00247 00248 /** 00249 * Convert a range of integers to a list: 4-6 results in a string '4,5,6' 00250 * 00251 * @throws InvalidArgumentException If range can not be coverted to list 00252 * @params string $range integer-integer 00253 * @return array 00254 */ 00255 public static function convertRangeToListOfValues($range) { 00256 if (strlen($range) === 0) { 00257 throw new InvalidArgumentException( 00258 'Unable to convert range to list of values with empty string.', 00259 1291234985 00260 ); 00261 } 00262 00263 $rangeArray = explode('-', $range); 00264 00265 // Sanitize fields and cast to integer 00266 foreach ($rangeArray as $fieldNumber => $fieldValue) { 00267 if (strcmp(intval($fieldValue), $fieldValue) !== 0) { 00268 throw new InvalidArgumentException( 00269 'Unable to convert value to integer.', 00270 1291237668 00271 ); 00272 } 00273 $rangeArray[$fieldNumber] = (int)$fieldValue; 00274 } 00275 00276 $resultList = ''; 00277 if (count($rangeArray) === 1) { 00278 $resultList = $rangeArray[0]; 00279 } elseif (count($rangeArray) === 2) { 00280 $left = $rangeArray[0]; 00281 $right = $rangeArray[1]; 00282 00283 if ($left > $right) { 00284 throw new InvalidArgumentException( 00285 'Unable to convert range to list: Left integer must not be greather than right integer.', 00286 1291237145 00287 ); 00288 } 00289 00290 $resultListArray = array(); 00291 for ($i = $left; $i <= $right; $i++) { 00292 $resultListArray[] = $i; 00293 } 00294 00295 $resultList = implode(',', $resultListArray); 00296 } else { 00297 throw new InvalidArgumentException( 00298 'Unable to convert range to list of values.', 00299 1291234985 00300 ); 00301 } 00302 00303 return (string)$resultList; 00304 } 00305 00306 /** 00307 * Reduce a given list of values by step value. 00308 * Following a range with ``/<number>'' specifies skips of the number's value through the range. 00309 * 1-5/2 -> 1,3,5 00310 * 2-10/3 -> 2,5,8 00311 * 00312 * @throws Exception if step value is invalid or if resulting list is empty 00313 * @param string #stepExpression stepvalue expression 00314 * @return string Comma separated list of valid values 00315 */ 00316 public static function reduceListOfValuesByStepValue($stepExpression) { 00317 if (strlen($stepExpression) === 0) { 00318 throw new InvalidArgumentException( 00319 'Unable to convert step values.', 00320 1291234985 00321 ); 00322 } 00323 00324 $stepValuesAndStepArray = explode('/', $stepExpression); 00325 00326 if (count($stepValuesAndStepArray) < 1 || count($stepValuesAndStepArray) > 2) { 00327 throw new InvalidArgumentException( 00328 'Unable to convert step values: Multiple slashes found.', 00329 1291242168 00330 ); 00331 } 00332 00333 $left = $stepValuesAndStepArray[0]; 00334 $right = $stepValuesAndStepArray[1]; 00335 00336 if (strlen($stepValuesAndStepArray[0]) === 0) { 00337 throw new InvalidArgumentException( 00338 'Unable to convert step values: Left part of / is empty.', 00339 1291414955 00340 ); 00341 } 00342 00343 if (strlen($stepValuesAndStepArray[1]) === 0) { 00344 throw new InvalidArgumentException( 00345 'Unable to convert step values: Right part of / is empty.', 00346 1291414956 00347 ); 00348 } 00349 00350 if (strcmp(intval($right), $right) !== 0) { 00351 throw new InvalidArgumentException( 00352 'Unable to convert step values: Right part must be a single integer.', 00353 1291414957 00354 ); 00355 } 00356 00357 $right = (int)$right; 00358 $leftArray = explode(',', $left); 00359 00360 $validValues = array(); 00361 $currentStep = $right; 00362 foreach ($leftArray as $leftValue) { 00363 if (strcmp(intval($leftValue), $leftValue) !== 0) { 00364 throw new InvalidArgumentException( 00365 'Unable to convert step values: Left part must be a single integer or comma separated list of integers.', 00366 1291414958 00367 ); 00368 } 00369 00370 if ($currentStep === 0) { 00371 $currentStep = $right; 00372 } 00373 00374 if ($currentStep === $right) { 00375 $validValues[] = (int)$leftValue; 00376 } 00377 00378 $currentStep --; 00379 } 00380 00381 if (count($validValues) === 0) { 00382 throw new InvalidArgumentException( 00383 'Unable to convert step values: Result value list is empty.', 00384 1291414958 00385 ); 00386 } 00387 00388 return implode(',', $validValues); 00389 } 00390 00391 /** 00392 * Dispatcher method for normalizeMonth and normalizeWeekday 00393 * 00394 * @param string $expression Month or weekday to be normalized 00395 * @param boolean $isMonth TRUE if a month is handled, FALSE for weekday 00396 * @return string normalized month or weekday 00397 */ 00398 public static function normalizeMonthAndWeekday($expression, $isMonth = TRUE) { 00399 $expression = $isMonth ? self::normalizeMonth($expression) : self::normalizeWeekday($expression); 00400 00401 return (string)$expression; 00402 } 00403 00404 /** 00405 * Accept a string representation or integer number of a month like 00406 * 'jan', 'February', 01, ... and convert to normalized integer value 1 .. 12 00407 * 00408 * @throws InvalidArgumentException If month string can not be converted to integer 00409 * @param string $month Month representation 00410 * @return integer month integer representation between 1 and 12 00411 */ 00412 public static function normalizeMonth($month) { 00413 $timestamp = strtotime('2010-' . $month . '-01'); 00414 00415 // timestamp must be >= 2010-01-01 and <= 2010-12-01 00416 if (!$timestamp || $timestamp < strtotime('2010-01-01') || $timestamp > strtotime('2010-12-01')) { 00417 throw new InvalidArgumentException( 00418 'Unable to convert given month name.', 00419 1291083486 00420 ); 00421 } 00422 00423 return (int)date('n', $timestamp); 00424 } 00425 00426 /** 00427 * Accept a string representation or integer number of a weekday like 00428 * 'mon', 'Friday', 3, ... and convert to normalized integer value 1 .. 7 00429 * 00430 * @throws InvalidArgumentException If weekday string can not be converted 00431 * @param string $weekday Weekday representation 00432 * @return integer weekday integer representation between 1 and 7 00433 */ 00434 public static function normalizeWeekday($weekday) { 00435 $normalizedWeekday = FALSE; 00436 00437 // 0 (sunday) -> 7 00438 if ((string)$weekday === '0') { 00439 $weekday = 7; 00440 } 00441 00442 if ($weekday >= 1 && $weekday <= 7) { 00443 $normalizedWeekday = (int)$weekday; 00444 } 00445 00446 if (!$normalizedWeekday) { 00447 // Convert string representation like 'sun' to integer 00448 $timestamp = strtotime('next ' . $weekday, mktime(0, 0, 0, 1, 1, 2010)); 00449 if (!$timestamp || $timestamp < strtotime('2010-01-01') || $timestamp > strtotime('2010-01-08')) { 00450 throw new InvalidArgumentException( 00451 'Unable to convert given weekday name.', 00452 1291163589 00453 ); 00454 } 00455 $normalizedWeekday = (int)date('N', $timestamp); 00456 } 00457 00458 return $normalizedWeekday; 00459 } 00460 } 00461 ?>
1.8.0