Repository navigation
Expand file tree
/
Copy pathParser.php
More file actions
162 lines (150 loc) · 6.23 KB
/
Copy pathParser.php
File metadata and controls
162 lines (150 loc) · 6.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact [email protected]
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Crontab;
use Carbon\Carbon;
use DateTime;
use DateTimeZone;
use InvalidArgumentException;
class Parser
{
/**
* 解析crontab的定时格式,linux只支持到分钟/,这个类支持到秒.
*
* @param string $crontabString :
* 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|Carbon|int $startTime
* @return Carbon[]
* @throws InvalidArgumentException
*/
public function parse(string $crontabString, $startTime = null, null|DateTimeZone|string $timezone = null): array
{
if (! $this->isValid($crontabString)) {
throw new InvalidArgumentException('Invalid cron string: ' . $crontabString);
}
$startTime = $this->parseStartTime($startTime);
$date = $this->parseDate($crontabString);
$result = [];
$currentDateTime = new DateTime();
$currentDateTime->setTimestamp($startTime);
if (isset($timezone)) {
$timezone = is_string($timezone) ? new DateTimeZone($timezone) : $timezone;
$currentDateTime->setTimezone($timezone);
}
if (in_array((int) $currentDateTime->format('i'), $date['minutes'])
&& in_array((int) $currentDateTime->format('G'), $date['hours'])
&& in_array((int) $currentDateTime->format('j'), $date['day'])
&& in_array((int) $currentDateTime->format('w'), $date['week'])
&& in_array((int) $currentDateTime->format('n'), $date['month'])
) {
foreach ($date['second'] as $second) {
$result[] = Carbon::createFromTimestamp($startTime + $second, $timezone);
}
}
return $result;
}
public function isValid(string $crontabString): 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($crontabString))) {
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($crontabString))) {
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 (str_contains($string, ',')) {
$exploded = explode(',', $string);
foreach ($exploded as $value) {
if (str_contains($value, '/') || str_contains($string, '-')) {
$result = array_merge($result, $this->parseSegment($value, $min, $max, $start));
continue;
}
if (trim($value) === '' || ! $this->between((int) $value, max($min, $start), $max)) {
continue;
}
$result[] = (int) $value;
}
} elseif (str_contains($string, '/')) {
$exploded = explode('/', $string);
if (str_contains($exploded[0], '-')) {
[$nMin, $nMax] = explode('-', $exploded[0]);
$nMin > $min && $min = (int) $nMin;
$nMax < $max && $max = (int) $nMax;
}
// If the value of start is larger than the value of min, the value of start should equal with the value of min.
$start < $min && $start = $min;
for ($i = $start; $i <= $max;) {
$result[] = $i;
$i += (int) $exploded[1];
}
} elseif (str_contains($string, '-')) {
$result = array_merge($result, $this->parseSegment($string . '/1', $min, $max, $start));
} elseif ($this->between((int) $string, max($min, $start), $max)) {
$result[] = (int) $string;
}
return $result;
}
/**
* Determine if the $value is between in $min and $max ?
*/
private function between(int $value, int $min, int $max): bool
{
return $value >= $min && $value <= $max;
}
/**
* @param null|Carbon|int $startTime
*/
private function parseStartTime($startTime): int
{
if ($startTime instanceof Carbon) {
$startTime = $startTime->getTimestamp();
} elseif ($startTime === null) {
$startTime = time();
}
if (! is_numeric($startTime)) {
throw new InvalidArgumentException("\$startTime have to be a valid unix timestamp ({$startTime} given)");
}
return (int) $startTime;
}
private function parseDate(string $crontabString): array
{
$cron = preg_split('/\s+/i', trim($crontabString));
return [
'week' => $this->parseSegment(array_pop($cron), 0, 6),
'month' => $this->parseSegment(array_pop($cron), 1, 12),
'day' => $this->parseSegment(array_pop($cron), 1, 31),
'hours' => $this->parseSegment(array_pop($cron), 0, 23),
'minutes' => $this->parseSegment(array_pop($cron), 0, 59),
'second' => $this->parseSegment(array_pop($cron) ?? '0', 0, 59),
];
}
}