Skip to content

Commit 9e14d3f

Browse files
authored
Merge pull request #3 from adhocore/develop
Refactor: Extract methods
2 parents 66984d6 + b528a78 commit 9e14d3f

File tree

3 files changed

+157
-79
lines changed

3 files changed

+157
-79
lines changed

readme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Expression::isDue('@always');
1515
Expression::isDue('@hourly', '2015-01-01 00:00:00');
1616
Expression::isDue('*/20 * * * *', new DateTime);
1717
Expression::isDue('5-34/4 * * * *', time());
18+
19+
// Dont like static calls? Below is possible too!
20+
$expr = new Expression;
21+
$expr->isCronDue('*/1 * * * *', time());
1822
```
1923

2024
### Real Abbreviations

src/Expression.php

Lines changed: 146 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -58,28 +58,7 @@ class Expression
5858
*/
5959
public static function isDue($expr, $time = null)
6060
{
61-
if (isset(static::$expressions[$expr])) {
62-
$expr = static::$expressions[$expr];
63-
}
64-
65-
if ('' === trim($expr, '?* ')) {
66-
return true;
67-
}
68-
69-
if (empty($time)) {
70-
$time = time();
71-
} elseif (is_string($time)) {
72-
$time = strtotime($time);
73-
} elseif ($time instanceof \DateTime) {
74-
$time = $time->getTimestamp();
75-
}
76-
77-
$expr = explode(' ', str_ireplace(array_keys(static::$literals), array_values(static::$literals), $expr));
78-
if (count($expr) < 5 || count($expr) > 6) {
79-
throw new \UnexpectedValueException('Cron $expr should have 5 or 6 segments delimited by space');
80-
}
81-
82-
$time = array_map('intval', explode(' ', date('i G j n w Y t d m N', $time)));
61+
list($expr, $time) = static::process($expr, $time);
8362

8463
foreach ($expr as $pos => $value) {
8564
if ($value === '*' || $value === '?') {
@@ -90,22 +69,12 @@ public static function isDue($expr, $time = null)
9069
$value = explode(',', trim($value));
9170

9271
foreach ($value as $offset) {
93-
if (strpos($offset, '*/') !== false || strpos($offset, '0/') !== false) {
94-
$parts = explode('/', $offset, 2);
95-
$isDue = $time[$pos] % $parts[1] === 0;
96-
} elseif (strpos($offset, '/') !== false) {
97-
$parts = explode('/', $offset, 2);
98-
$subparts = explode('-', $parts[0], 2) + [1 => $time[$pos]];
99-
$isDue = $subparts[0] <= $time[$pos] && $time[$pos] <= $subparts[1] && $parts[1]
100-
? in_array($time[$pos], range($subparts[0], $subparts[1], $parts[1]))
101-
: false;
102-
} elseif (strpos($offset, '-') !== false) {
103-
$parts = explode('-', $offset);
104-
$isDue = $parts[0] <= $time[$pos] && $time[$pos] <= $parts[1];
105-
} elseif (is_numeric($offset)) {
106-
$isDue = $time[$pos] == $offset;
107-
} elseif (strpbrk($offset, 'LCW#')) {
108-
$isDue = static::checkModifier($offset, $pos, $time);
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+
);
10978
}
11079

11180
if ($isDue) {
@@ -121,72 +90,172 @@ public static function isDue($expr, $time = null)
12190
return true;
12291
}
12392

93+
/**
94+
* Process and prepare input.
95+
*
96+
* @param string $expr
97+
* @param mixed $time
98+
*
99+
* @return array
100+
*/
101+
protected static function process($expr, $time)
102+
{
103+
if (isset(static::$expressions[$expr])) {
104+
$expr = static::$expressions[$expr];
105+
}
106+
107+
$expr = str_ireplace(array_keys(static::$literals), array_values(static::$literals), $expr);
108+
$expr = explode(' ', $expr);
109+
110+
if (count($expr) < 5 || count($expr) > 6) {
111+
throw new \UnexpectedValueException(
112+
'Cron $expr should have 5 or 6 segments delimited by space'
113+
);
114+
}
115+
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+
}
123+
124+
$time = array_map('intval', explode(' ', date('i G j n w Y t d m N', $time)));
125+
126+
return [$expr, $time];
127+
}
128+
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)
139+
{
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+
124172
/**
125173
* Check if modifiers [L C W #] are satisfied.
126174
*
127175
* @internal
128176
*
129177
* @param string $value
130-
* @param int $pos
131178
* @param int $time
132179
*
133-
* @return bool
180+
* @return bool|null
134181
*/
135-
protected static function checkModifier($value, $pos, $time)
182+
protected static function checkMonthDay($value, $time)
136183
{
137184
$month = $time[8] < 10 ? '0' . $time[8] : $time[8];
138185

139-
// Day of month.
140-
if ($pos === 2) {
141-
if ($value == 'L') {
142-
return $time[2] == $time[6];
143-
}
186+
if ($value == 'L') {
187+
return $time[2] == $time[6];
188+
}
144189

145-
if ($pos = strpos($value, 'W')) {
146-
$value = substr($value, 0, $pos);
190+
if ($pos = strpos($value, 'W')) {
191+
$value = substr($value, 0, $pos);
147192

148-
foreach ([0, -1, 1, -2, 2] as $i) {
149-
$incr = $value + $i;
150-
if ($incr > 0 && $incr <= $time[6]) {
151-
if ($incr < 10) {
152-
$incr = '0' . $incr;
153-
}
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+
}
154199

155-
$parts = explode(' ', date('N m j', strtotime("$time[5]-$month-$incr")));
156-
if ($parts[0] < 6 && $parts[1] == $month) {
157-
return $time[2] == $parts[2];
158-
}
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];
159203
}
160204
}
161205
}
162206
}
207+
}
163208

164-
// Day of week.
165-
if ($pos === 4) {
166-
if ($pos = strpos($value, 'L')) {
167-
$value = explode('L', str_replace('7L', '0L', $value));
168-
$decr = $time[6];
169-
for ($i = 0; $i < 7; $i++) {
170-
$decr -= $i;
171-
if (date('w', strtotime("$time[5]-$month-$decr")) == $value[0]) {
172-
return $time[2] == $decr;
173-
}
174-
}
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];
175222

176-
return false;
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+
}
177231
}
178232

179-
if (strpos($value, '#')) {
180-
$value = explode('#', str_replace('0#', '7#', $value));
233+
return false;
234+
}
181235

182-
if ($value[0] < 0 || $value[0] > 7 || $value[1] < 1 || $value[1] > 5 || $time[9] != $value[0]) {
183-
return false;
184-
}
236+
if (strpos($value, '#')) {
237+
$value = explode('#', str_replace('0#', '7#', $value));
185238

186-
return intval($time[7] / 7) == $value[1] - 1;
239+
if ($value[0] < 0 || $value[0] > 7 || $value[1] < 1 || $value[1] > 5 || $time[9] != $value[0]) {
240+
return false;
187241
}
242+
243+
return intval($time[7] / 7) == $value[1] - 1;
188244
}
245+
}
189246

190-
throw new \UnexpectedValueException(sprintf('Invalid modifier value %s for segment #%d', $value, $pos));
247+
/**
248+
* Instance call.
249+
*
250+
* Parse cron expression to decide if it can be run on given time (or default now).
251+
*
252+
* @param string $expr The cron expression.
253+
* @param int $time The timestamp to validate the cron expr against. Defaults to now.
254+
*
255+
* @return bool
256+
*/
257+
public function isCronDue($expr, $time = null)
258+
{
259+
return static::isDue($expr, $time);
191260
}
192261
}

tests/ExpressionTest.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ class ExpressionTest extends \PHPUnit_Framework_TestCase
99
{
1010
/**
1111
* @dataProvider scheduleProvider
12-
* @covers \Ahc\Cron\Expression::isDue
13-
* @covers \Ahc\Cron\Expression::checkModifier
1412
*/
1513
public function test_isDue($expr, $time, $foo, $expected)
1614
{
@@ -19,6 +17,13 @@ public function test_isDue($expr, $time, $foo, $expected)
1917
$this->assertSame($expected, $actual, 'The expression ' . $expr . ' has failed');
2018
}
2119

20+
public function test_isCronDue()
21+
{
22+
$expr = new Expression;
23+
24+
$this->assertTrue(is_bool($expr->isCronDue('*/1 * * * *', time())));
25+
}
26+
2227
public function test_isDue_throws_if_expr_invalid()
2328
{
2429
$this->setExpectedException(\UnexpectedValueException::class);

0 commit comments

Comments
 (0)