Skip to content

Commit 5c2799c

Browse files
authored
Merge pull request #18 from adhocore/develop
Develop
2 parents 139c578 + 96b78d1 commit 5c2799c

File tree

9 files changed

+179
-92
lines changed

9 files changed

+179
-92
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
/.idea/
55
/vendor/
66
composer.lock
7+
coverage.xml

README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,22 @@ $jobs = [
5656
];
5757

5858
// The second param $time can be used same as above: null/time()/date string/DateTime
59-
$dueJobs = Expression::getDues($jobs, '2015-08-10 21:50:00');
59+
$dues = Expression::getDues($jobs, '2015-08-10 21:50:00');
6060
// ['job1', 'job4', 'job5']
6161

6262
// Dont like static calls? Below is possible too!
6363
$expr = new Expression;
64-
$dueJobs = $expr->filter($jobs, time());
64+
$dues = $expr->filter($jobs, time());
6565
```
6666

67+
### Cron Expression
68+
69+
Cron expression normally consists of 5 segments viz:
70+
```
71+
<minute> <hour> <day> <month> <weekday>
72+
```
73+
and sometimes there can be 6th segment for `<year>` at the end.
74+
6775
### Real Abbreviations
6876

6977
You can use real abbreviations for month and week days. eg: `JAN`, `dec`, `fri`, `SUN`
@@ -88,11 +96,11 @@ Following tags are available and they are converted to real cron expressions bef
8896
Following modifiers supported
8997

9098
- *Day of Month / 3rd segment:*
91-
- `L` stands for last day of month (eg: `L` could mean 29th for February in leap year)
92-
- `W` stands for closest week day (eg: `10W` is closest week days (MON-FRI) to 10th date)
99+
- `L` stands for last day of month (eg: `L` could mean 29th for February in leap year)
100+
- `W` stands for closest week day (eg: `10W` is closest week days (MON-FRI) to 10th date)
93101
- *Day of Week / 5th segment:*
94-
- `L` stands for last weekday of month (eg: `2L` is last monday)
95-
- `#` stands for nth day of week in the month (eg: `1#2` is second sunday)
102+
- `L` stands for last weekday of month (eg: `2L` is last monday)
103+
- `#` stands for nth day of week in the month (eg: `1#2` is second sunday)
96104

97105
## LICENSE
98106

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@
2323
}
2424
},
2525
"require": {
26-
"php": ">=5.4"
26+
"php": ">=7.0"
2727
},
2828
"require-dev": {
29-
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5"
29+
"phpunit/phpunit": "^6.5 || ^7.5"
3030
},
3131
"scripts": {
3232
"test": "vendor/bin/phpunit",
33-
"test:cov": "vendor/bin/phpunit --coverage-text --coverage-clover coverage.xml"
33+
"test:cov": "vendor/bin/phpunit --coverage-text --coverage-clover coverage.xml --coverage-html vendor/cov"
3434
}
3535
}

phpunit.xml.dist

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
convertWarningsToExceptions="true"
99
processIsolation="false"
1010
stopOnFailure="false"
11-
syntaxCheck="false"
1211
bootstrap="vendor/autoload.php"
1312
>
1413
<testsuites>

src/Expression.php

Lines changed: 13 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ public static function getDues(array $jobs, $time = null): array
121121
*/
122122
public function isCronDue(string $expr, $time = null): bool
123123
{
124-
list($expr, $times) = $this->process($expr, $time);
124+
$this->checker->setReference(new ReferenceTime($time));
125125

126-
foreach ($expr as $pos => $segment) {
126+
foreach (\explode(' ', $this->normalizeExpr($expr)) as $pos => $segment) {
127127
if ($segment === '*' || $segment === '?') {
128128
continue;
129129
}
130130

131-
if (!$this->checker->checkDue($segment, $pos, $times)) {
131+
if (!$this->checker->checkDue($segment, $pos)) {
132132
return false;
133133
}
134134
}
@@ -147,7 +147,6 @@ public function isCronDue(string $expr, $time = null): bool
147147
public function filter(array $jobs, $time = null): array
148148
{
149149
$dues = $cache = [];
150-
$time = $this->normalizeTime($time);
151150

152151
foreach ($jobs as $name => $expr) {
153152
$expr = $this->normalizeExpr($expr);
@@ -164,44 +163,6 @@ public function filter(array $jobs, $time = null): array
164163
return $dues;
165164
}
166165

167-
/**
168-
* Process and prepare input.
169-
*
170-
* @param string $expr
171-
* @param mixed $time
172-
*
173-
* @return array [expr, time]
174-
*/
175-
protected function process(string $expr, $time): array
176-
{
177-
$expr = $this->normalizeExpr($expr);
178-
$expr = \preg_split('~\s+~', $expr); // 14
179-
180-
if (\count($expr) < 5 || \count($expr) > 6) {
181-
throw new \UnexpectedValueException(
182-
'Cron $expr should have 5 or 6 segments delimited by space'
183-
);
184-
}
185-
186-
$time = static::normalizeTime($time);
187-
$times = \array_map('intval', \explode(' ', \date('i G j n w Y t d m N', $time)));
188-
189-
return [$expr, $times];
190-
}
191-
192-
protected function normalizeTime($time): int
193-
{
194-
if (empty($time)) {
195-
$time = \time();
196-
} elseif (\is_string($time)) {
197-
$time = \strtotime($time);
198-
} elseif ($time instanceof \DateTime) {
199-
$time = $time->getTimestamp();
200-
}
201-
202-
return $time;
203-
}
204-
205166
protected function normalizeExpr(string $expr): string
206167
{
207168
$expr = \trim($expr);
@@ -210,10 +171,15 @@ protected function normalizeExpr(string $expr): string
210171
return static::$expressions[$expr];
211172
}
212173

213-
return \str_ireplace(
214-
\array_keys(static::$literals),
215-
\array_values(static::$literals),
216-
$expr
217-
);
174+
$expr = \preg_replace('~\s+~', ' ', $expr);
175+
$count = \substr_count($expr, ' ');
176+
177+
if ($count < 4 || $count > 5) {
178+
throw new \UnexpectedValueException(
179+
'Cron $expr should have 5 or 6 segments delimited by space'
180+
);
181+
}
182+
183+
return \str_ireplace(\array_keys(static::$literals), \array_values(static::$literals), $expr);
218184
}
219185
}

src/ReferenceTime.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the PHP-CRON-EXPR package.
7+
*
8+
* (c) Jitendra Adhikari <[email protected]>
9+
* <https://github.com/adhocore>
10+
*
11+
* Licensed under MIT license.
12+
*/
13+
14+
namespace Ahc\Cron;
15+
16+
/**
17+
* @method int minute()
18+
* @method int hour()
19+
* @method int monthDay()
20+
* @method int month()
21+
* @method int weekDay() 0 based day of week.
22+
* @method int year()
23+
* @method int day()
24+
* @method int weekDay1() 1 based day of week.
25+
* @method int numDays() Number of days in the month.
26+
*/
27+
class ReferenceTime
28+
{
29+
// The cron parts. (Donot change it)
30+
const MINUTE = 0;
31+
const HOUR = 1;
32+
const MONTHDAY = 2;
33+
const MONTH = 3;
34+
const WEEKDAY = 4;
35+
const YEAR = 5;
36+
37+
// Meta data parts.
38+
const DAY = 6;
39+
const WEEKDAY1 = 7;
40+
const NUMDAYS = 8;
41+
42+
/** @var array The data */
43+
protected $values = [];
44+
45+
/** @var array The Magic methods */
46+
protected $methods = [];
47+
48+
public function __construct($time)
49+
{
50+
$timestamp = $this->normalizeTime($time);
51+
52+
$this->values = $this->parse($timestamp);
53+
$this->methods = (new \ReflectionClass($this))->getConstants();
54+
}
55+
56+
public function __call(string $method, array $args): int
57+
{
58+
$method = \preg_replace('/^GET/', '', \strtoupper($method));
59+
if (isset($this->methods[$method])) {
60+
return $this->values[$this->methods[$method]];
61+
}
62+
63+
// @codeCoverageIgnoreStart
64+
throw new \BadMethodCallException("Method '$method' doesnot exist in ReferenceTime.");
65+
// @codeCoverageIgnoreEnd
66+
}
67+
68+
public function get(int $segment): int
69+
{
70+
return $this->values[$segment];
71+
}
72+
73+
public function isAt($value, $segment): bool
74+
{
75+
return $this->values[$segment] == $value;
76+
}
77+
78+
protected function normalizeTime($time): int
79+
{
80+
if (empty($time)) {
81+
$time = \time();
82+
} elseif (\is_string($time)) {
83+
$time = \strtotime($time);
84+
} elseif ($time instanceof \DateTime) {
85+
$time = $time->getTimestamp();
86+
}
87+
88+
return $time;
89+
}
90+
91+
protected function parse(int $timestamp): array
92+
{
93+
$parts = \date('i G j n w Y d N t', $timestamp);
94+
$parts = \explode(' ', $parts);
95+
$parts = \array_map('intval', $parts);
96+
97+
return $parts;
98+
}
99+
}

src/SegmentChecker.php

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
*/
2323
class SegmentChecker
2424
{
25+
/** @var ReferenceTime */
26+
protected $reference;
27+
2528
/** @var Validator */
2629
protected $validator;
2730

@@ -30,21 +33,25 @@ public function __construct(Validator $validator = null)
3033
$this->validator = $validator ?: new Validator;
3134
}
3235

36+
public function setReference(ReferenceTime $reference)
37+
{
38+
$this->reference = $reference;
39+
}
40+
3341
/**
3442
* Checks if a cron segment satisfies given time.
3543
*
3644
* @param string $segment
3745
* @param int $pos
38-
* @param array $times
3946
*
4047
* @return bool
4148
*/
42-
public function checkDue(string $segment, int $pos, array $times): bool
49+
public function checkDue(string $segment, int $pos): bool
4350
{
4451
$offsets = \explode(',', \trim($segment));
4552

4653
foreach ($offsets as $offset) {
47-
if ($this->isOffsetDue($offset, $pos, $times)) {
54+
if ($this->isOffsetDue($offset, $pos)) {
4855
return true;
4956
}
5057
}
@@ -57,39 +64,41 @@ public function checkDue(string $segment, int $pos, array $times): bool
5764
*
5865
* @param string $offset
5966
* @param int $pos
60-
* @param array $times
6167
*
6268
* @return bool
6369
*/
64-
protected function isOffsetDue(string $offset, int $pos, array $times): bool
70+
protected function isOffsetDue(string $offset, int $pos): bool
6571
{
6672
if (\strpos($offset, '/') !== false) {
67-
return $this->validator->inStep($times[$pos], $offset);
73+
return $this->validator->inStep($this->reference->get($pos), $offset);
6874
}
6975

7076
if (\strpos($offset, '-') !== false) {
71-
return $this->validator->inRange($times[$pos], $offset);
77+
return $this->validator->inRange($this->reference->get($pos), $offset);
7278
}
7379

7480
if (\is_numeric($offset)) {
75-
return $times[$pos] == $offset;
81+
return $this->reference->isAt($offset, $pos);
7682
}
7783

78-
return $this->checkModifier($offset, $pos, $times);
84+
return $this->checkModifier($offset, $pos);
7985
}
8086

81-
protected function checkModifier(string $offset, int $pos, array $times): bool
87+
protected function checkModifier(string $offset, int $pos): bool
8288
{
8389
$isModifier = \strpbrk($offset, 'LCW#');
8490

85-
if ($pos === 2 && $isModifier) {
86-
return $this->validator->isValidMonthDay($offset, $times);
91+
if ($pos === ReferenceTime::MONTHDAY && $isModifier) {
92+
return $this->validator->isValidMonthDay($offset, $this->reference);
8793
}
8894

89-
if ($pos === 4 && $isModifier) {
90-
return $this->validator->isValidWeekDay($offset, $times);
95+
if ($pos === ReferenceTime::WEEKDAY && $isModifier) {
96+
return $this->validator->isValidWeekDay($offset, $this->reference);
9197
}
9298

9399
$this->validator->unexpectedValue($pos, $offset);
100+
// @codeCoverageIgnoreStart
94101
}
102+
103+
// @codeCoverageIgnoreEnd
95104
}

0 commit comments

Comments
 (0)