Skip to content

Commit 3ee70f0

Browse files
authored
Merge pull request #4 from adhocore/develop
Refactor: Extract class and methods
2 parents cdbf3a8 + 9e3fd69 commit 3ee70f0

File tree

2 files changed

+193
-140
lines changed

2 files changed

+193
-140
lines changed

src/Expression.php

Lines changed: 12 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -60,29 +60,12 @@ public static function isDue($expr, $time = null)
6060
{
6161
list($expr, $time) = static::process($expr, $time);
6262

63-
foreach ($expr as $pos => $value) {
64-
if ($value === '*' || $value === '?') {
63+
foreach ($expr as $pos => $segment) {
64+
if ($segment === '*' || $segment === '?') {
6565
continue;
6666
}
6767

68-
$isDue = true;
69-
$value = explode(',', trim($value));
70-
71-
foreach ($value as $offset) {
72-
$isDue = static::isOffsetDue($offset, $pos, $time);
73-
74-
if (null === $isDue) {
75-
throw new \UnexpectedValueException(
76-
sprintf('Invalid offset value %s for segment #%d', $offset, $pos)
77-
);
78-
}
79-
80-
if ($isDue) {
81-
break;
82-
}
83-
}
84-
85-
if (!$isDue) {
68+
if (!SegmentChecker::isDue($segment, $pos, $time)) {
8669
return false;
8770
}
8871
}
@@ -113,135 +96,24 @@ protected static function process($expr, $time)
11396
);
11497
}
11598

116-
if (empty($time)) {
117-
$time = time();
118-
} elseif (is_string($time)) {
119-
$time = strtotime($time);
120-
} elseif ($time instanceof \DateTime) {
121-
$time = $time->getTimestamp();
122-
}
99+
$time = static::normalizeTime($time);
123100

124101
$time = array_map('intval', explode(' ', date('i G j n w Y t d m N', $time)));
125102

126103
return [$expr, $time];
127104
}
128105

129-
/**
130-
* Check if a given offset at a position is due with respect to given time.
131-
*
132-
* @param string $offset
133-
* @param int $pos
134-
* @param array $time
135-
*
136-
* @return bool|null
137-
*/
138-
protected static function isOffsetDue($offset, $pos, $time)
106+
protected static function normalizeTime($time)
139107
{
140-
if (strpos($offset, '*/') !== false || strpos($offset, '0/') !== false) {
141-
$parts = explode('/', $offset, 2);
142-
143-
return $time[$pos] % $parts[1] === 0;
144-
}
145-
146-
if (strpos($offset, '/') !== false) {
147-
$parts = explode('/', $offset, 2);
148-
$subparts = explode('-', $parts[0], 2) + [1 => $time[$pos]];
149-
150-
return ($subparts[0] <= $time[$pos] && $time[$pos] <= $subparts[1] && $parts[1])
151-
? in_array($time[$pos], range($subparts[0], $subparts[1], $parts[1]))
152-
: false;
153-
}
154-
155-
if (strpos($offset, '-') !== false) {
156-
$parts = explode('-', $offset);
157-
158-
return $parts[0] <= $time[$pos] && $time[$pos] <= $parts[1];
159-
}
160-
161-
if (is_numeric($offset)) {
162-
return $time[$pos] == $offset;
163-
}
164-
165-
if (($pos === 2 || $pos === 4) && strpbrk($offset, 'LCW#')) {
166-
return $pos === 4
167-
? static::checkWeekDay($offset, $time)
168-
: static::checkMonthDay($offset, $time);
169-
}
170-
}
171-
172-
/**
173-
* Check if modifiers [L C W #] are satisfied.
174-
*
175-
* @internal
176-
*
177-
* @param string $value
178-
* @param int $time
179-
*
180-
* @return bool|null
181-
*/
182-
protected static function checkMonthDay($value, $time)
183-
{
184-
$month = $time[8] < 10 ? '0' . $time[8] : $time[8];
185-
186-
if ($value == 'L') {
187-
return $time[2] == $time[6];
188-
}
189-
190-
if ($pos = strpos($value, 'W')) {
191-
$value = substr($value, 0, $pos);
192-
193-
foreach ([0, -1, 1, -2, 2] as $i) {
194-
$incr = $value + $i;
195-
if ($incr > 0 && $incr <= $time[6]) {
196-
if ($incr < 10) {
197-
$incr = '0' . $incr;
198-
}
199-
200-
$parts = explode(' ', date('N m j', strtotime("$time[5]-$month-$incr")));
201-
if ($parts[0] < 6 && $parts[1] == $month) {
202-
return $time[2] == $parts[2];
203-
}
204-
}
205-
}
206-
}
207-
}
208-
209-
/**
210-
* Check if modifiers [L C W #] are satisfied.
211-
*
212-
* @internal
213-
*
214-
* @param string $value
215-
* @param int $time
216-
*
217-
* @return bool|null
218-
*/
219-
protected static function checkWeekDay($value, $time)
220-
{
221-
$month = $time[8] < 10 ? '0' . $time[8] : $time[8];
222-
223-
if ($pos = strpos($value, 'L')) {
224-
$value = explode('L', str_replace('7L', '0L', $value));
225-
$decr = $time[6];
226-
for ($i = 0; $i < 7; $i++) {
227-
$decr -= $i;
228-
if (date('w', strtotime("$time[5]-$month-$decr")) == $value[0]) {
229-
return $time[2] == $decr;
230-
}
231-
}
232-
233-
return false;
108+
if (empty($time)) {
109+
$time = time();
110+
} elseif (is_string($time)) {
111+
$time = strtotime($time);
112+
} elseif ($time instanceof \DateTime) {
113+
$time = $time->getTimestamp();
234114
}
235115

236-
if (strpos($value, '#')) {
237-
$value = explode('#', str_replace('0#', '7#', $value));
238-
239-
if ($value[0] < 0 || $value[0] > 7 || $value[1] < 1 || $value[1] > 5 || $time[9] != $value[0]) {
240-
return false;
241-
}
242-
243-
return intval($time[7] / 7) == $value[1] - 1;
244-
}
116+
return $time;
245117
}
246118

247119
/**

src/SegmentChecker.php

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
<?php
2+
3+
namespace Ahc\Cron;
4+
5+
/**
6+
* Cron Expression Parser.
7+
*
8+
* This class checks if a cron segment satisfies given time.
9+
*
10+
* @author Jitendra Adhikari <[email protected]>
11+
*/
12+
class SegmentChecker
13+
{
14+
public static function isDue($segment, $pos, $time)
15+
{
16+
$isDue = true;
17+
$offsets = explode(',', trim($segment));
18+
19+
foreach ($offsets as $offset) {
20+
if (null === $isDue = static::isOffsetDue($offset, $pos, $time)) {
21+
throw new \UnexpectedValueException(
22+
sprintf('Invalid offset value %s for segment #%d', $offset, $pos)
23+
);
24+
}
25+
26+
if ($isDue) {
27+
return true;
28+
}
29+
}
30+
31+
return false;
32+
}
33+
34+
/**
35+
* Check if a given offset at a position is due with respect to given time.
36+
*
37+
* @param string $offset
38+
* @param int $pos
39+
* @param array $time
40+
*
41+
* @return bool|null
42+
*/
43+
protected static function isOffsetDue($offset, $pos, $time)
44+
{
45+
if (strpos($offset, '/') !== false) {
46+
return static::inStep($time[$pos], $offset);
47+
}
48+
49+
if (strpos($offset, '-') !== false) {
50+
return static::inRange($time[$pos], $offset);
51+
}
52+
53+
if (is_numeric($offset)) {
54+
return $time[$pos] == $offset;
55+
}
56+
57+
return static::inModifier($offset, $pos, $time);
58+
}
59+
60+
protected static function inModifier($offset, $pos, $time)
61+
{
62+
$isModifier = strpbrk($offset, 'LCW#');
63+
64+
if ($pos === 2 && $isModifier) {
65+
return static::checkMonthDay($offset, $time);
66+
}
67+
68+
if ($pos === 4 && $isModifier) {
69+
return static::checkWeekDay($offset, $time);
70+
}
71+
}
72+
73+
protected static function inRange($value, $offset)
74+
{
75+
$parts = explode('-', $offset);
76+
77+
return $parts[0] <= $value && $value <= $parts[1];
78+
}
79+
80+
protected static function inStep($value, $offset)
81+
{
82+
if (strpos($offset, '*/') !== false || strpos($offset, '0/') !== false) {
83+
$parts = explode('/', $offset, 2);
84+
85+
return $value % $parts[1] === 0;
86+
}
87+
88+
$parts = explode('/', $offset, 2);
89+
$subparts = explode('-', $parts[0], 2) + [1 => $value];
90+
91+
return ($subparts[0] <= $value && $value <= $subparts[1] && $parts[1])
92+
? in_array($value, range($subparts[0], $subparts[1], $parts[1]))
93+
: false;
94+
}
95+
96+
/**
97+
* Check if modifiers [L C W #] are satisfied.
98+
*
99+
* @internal
100+
*
101+
* @param string $value
102+
* @param int $time
103+
*
104+
* @return bool|null
105+
*/
106+
protected static function checkMonthDay($value, $time)
107+
{
108+
$month = $time[8] < 10 ? '0' . $time[8] : $time[8];
109+
110+
if ($value == 'L') {
111+
return $time[2] == $time[6];
112+
}
113+
114+
if ($pos = strpos($value, 'W')) {
115+
$value = substr($value, 0, $pos);
116+
117+
return static::isClosestWeekDay($value, $month, $time);
118+
}
119+
}
120+
121+
protected static function isClosestWeekDay($value, $month, $time)
122+
{
123+
foreach ([0, -1, 1, -2, 2] as $i) {
124+
$incr = $value + $i;
125+
if ($incr > 0 && $incr <= $time[6]) {
126+
if ($incr < 10) {
127+
$incr = '0' . $incr;
128+
}
129+
130+
$parts = explode(' ', date('N m j', strtotime("$time[5]-$month-$incr")));
131+
if ($parts[0] < 6 && $parts[1] == $month) {
132+
return $time[2] == $parts[2];
133+
}
134+
}
135+
}
136+
}
137+
138+
/**
139+
* Check if modifiers [L C W #] are satisfied.
140+
*
141+
* @internal
142+
*
143+
* @param string $value
144+
* @param int $time
145+
*
146+
* @return bool|null
147+
*/
148+
protected static function checkWeekDay($value, $time)
149+
{
150+
$month = $time[8] < 10 ? '0' . $time[8] : $time[8];
151+
152+
if (strpos($value, 'L')) {
153+
return static::lastWeekDay($value, $month, $time);
154+
}
155+
156+
if (strpos($value, '#')) {
157+
$value = explode('#', str_replace('0#', '7#', $value));
158+
159+
if ($value[0] < 0 || $value[0] > 7 || $value[1] < 1 || $value[1] > 5 || $time[9] != $value[0]) {
160+
return false;
161+
}
162+
163+
return intval($time[7] / 7) == $value[1] - 1;
164+
}
165+
}
166+
167+
protected static function lastWeekDay($value, $month, $time)
168+
{
169+
$value = explode('L', str_replace('7L', '0L', $value));
170+
$decr = $time[6];
171+
172+
for ($i = 0; $i < 7; $i++) {
173+
$decr -= $i;
174+
if (date('w', strtotime("$time[5]-$month-$decr")) == $value[0]) {
175+
return $time[2] == $decr;
176+
}
177+
}
178+
179+
return false;
180+
}
181+
}

0 commit comments

Comments
 (0)