, group@hyperf.io * @license: http://www.gnu.org/licenses/ * @license: https://github.com/hyperf/hyperf/blob/master/LICENSE * * This is a simple script to parse crontab syntax to get the execution time * * Eg.: $timestamp = Crontab::parse('12 * * * 1-5'); * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Provides basic cron syntax parsing functionality * * @author: Jan Konieczny , group@hyperf.io * @license: http://www.gnu.org/licenses/ * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ namespace Workerman\Crontab; /** * Class Parser * @package Workerman\Crontab */ class Parser { /** * Finds next execution time(stamp) parsin crontab syntax. * * @param string $crontab_string : * 0 1 2 3 4 5 * * * * * * * * - - - - - - * | | | | | | * | | | | | +----- day of week (0 - 6) (Sunday=0) * | | | | +----- month (1 - 12) * | | | +------- day of month (1 - 31) * | | +--------- hour (0 - 23) * | +----------- min (0 - 59) * +------------- sec (0-59) * * @param null|int $start_time * @throws \InvalidArgumentException * @return int[] */ public function parse($crontab_string, $start_time = null) { if (! $this->isValid($crontab_string)) { throw new \InvalidArgumentException('Invalid cron string: ' . $crontab_string); } $start_time = $start_time ? $start_time : time(); $date = $this->parseDate($crontab_string); if (in_array((int) date('i', $start_time), $date['minutes']) && in_array((int) date('G', $start_time), $date['hours']) && in_array((int) date('j', $start_time), $date['day']) && in_array((int) date('w', $start_time), $date['week']) && in_array((int) date('n', $start_time), $date['month']) ) { $result = []; foreach ($date['second'] as $second) { $result[] = $start_time + $second; } return $result; } return []; } public function isValid(string $crontab_string): bool { if (! preg_match('/^((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)$/i', trim($crontab_string))) { if (! preg_match('/^((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)\s+((\*(\/[0-9]+)?)|[0-9\-\,\/]+)$/i', trim($crontab_string))) { return false; } } return true; } /** * Parse each segment of crontab string. */ protected function parseSegment(string $string, int $min, int $max, int $start = null) { if ($start === null || $start < $min) { $start = $min; } $result = []; if ($string === '*') { for ($i = $start; $i <= $max; ++$i) { $result[] = $i; } } elseif (strpos($string, ',') !== false) { $exploded = explode(',', $string); foreach ($exploded as $value) { if (strpos($value, '/') !== false || strpos($string, '-') !== false) { $result = array_merge($result, $this->parseSegment($value, $min, $max, $start)); continue; } if (trim($value) === '' || ! $this->between((int) $value, (int) ($min > $start ? $min : $start), (int) $max)) { continue; } $result[] = (int) $value; } } elseif (strpos($string, '/') !== false) { $exploded = explode('/', $string); if (strpos($exploded[0], '-') !== false) { [$nMin, $nMax] = explode('-', $exploded[0]); $nMin > $min && $min = (int) $nMin; $nMax < $max && $max = (int) $nMax; } $start < $min && $start = $min; for ($i = $start; $i <= $max;) { $result[] = $i; $i += $exploded[1]; } } elseif (strpos($string, '-') !== false) { $result = array_merge($result, $this->parseSegment($string . '/1', $min, $max, $start)); } elseif ($this->between((int) $string, $min > $start ? $min : $start, $max)) { $result[] = (int) $string; } return $result; } /** * Determire if the $value is between in $min and $max ? */ private function between(int $value, int $min, int $max): bool { return $value >= $min && $value <= $max; } private function parseDate(string $crontab_string): array { $cron = preg_split('/[\\s]+/i', trim($crontab_string)); if (count($cron) == 6) { $date = [ 'second' => $this->parseSegment($cron[0], 0, 59), 'minutes' => $this->parseSegment($cron[1], 0, 59), 'hours' => $this->parseSegment($cron[2], 0, 23), 'day' => $this->parseSegment($cron[3], 1, 31), 'month' => $this->parseSegment($cron[4], 1, 12), 'week' => $this->parseSegment($cron[5], 0, 6), ]; } else { $date = [ 'second' => [1 => 0], 'minutes' => $this->parseSegment($cron[0], 0, 59), 'hours' => $this->parseSegment($cron[1], 0, 23), 'day' => $this->parseSegment($cron[2], 1, 31), 'month' => $this->parseSegment($cron[3], 1, 12), 'week' => $this->parseSegment($cron[4], 0, 6), ]; } return $date; } }