Passed
Pull Request — master (#3)
by Kevin
02:01
created

CronExpression   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 101
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 12
eloc 50
c 1
b 0
f 0
dl 0
loc 101
ccs 36
cts 36
cp 1
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A hashField() 0 5 1
A getNextRun() 0 3 1
A isDue() 0 3 1
A isHashed() 0 3 1
A __toString() 0 3 1
A getParsedValue() 0 8 2
A getRawValue() 0 3 1
A __construct() 0 13 2
A parsePart() 0 12 2
1
<?php
2
3
namespace Zenstruck\ScheduleBundle\Schedule;
4
5
use Cron\CronExpression as CronSchedule;
6
7
/**
8
 * @author Kevin Bond <[email protected]>
9
 */
10
final class CronExpression
11
{
12
    private const MINUTE = 0;
13
    private const HOUR = 1;
14
    private const DOM = 2;
15
    private const MONTH = 3;
16
    private const DOW = 4;
17
18
    private const ALIAS_MAP = [
19
        '@hourly' => 'H * * * *',
20
        '@daily' => 'H H * * *',
21
        '@weekly' => 'H H * * H',
22
        '@monthly' => 'H H H * *',
23
        '@annually' => 'H H H H *',
24
        '@yearly' => 'H H H H *',
25
        '@midnight' => 'H H(0-2) * * *',
26
    ];
27
28
    private const RANGES = [
29
        self::MINUTE => [0, 59],
30
        self::HOUR => [0, 23],
31
        self::DOM => [1, 28],
32
        self::MONTH => [1, 12],
33
        self::DOW => [0, 6],
34
    ];
35
36
    private $value;
37
    private $parts;
38
    private $context;
39
    private $parsedValue;
40
41 125
    public function __construct(string $value, string $context)
42
    {
43 125
        $this->value = $value;
44
45 125
        $value = self::ALIAS_MAP[$value] ?? $value;
46 125
        $parts = \explode(' ', $value);
47
48 125
        if (5 !== \count($parts)) {
49 7
            throw new \InvalidArgumentException("\"{$value}\" is an invalid cron expression.");
50
        }
51
52 118
        $this->parts = $parts;
53 118
        $this->context = $context;
54 118
    }
55
56 76
    public function __toString(): string
57
    {
58 76
        return $this->getParsedValue();
59
    }
60
61 20
    public function getRawValue(): string
62
    {
63 20
        return $this->value;
64
    }
65
66 107
    public function getParsedValue(): string
67
    {
68 107
        return $this->parsedValue ?: $this->parsedValue = \implode(' ', [
69 107
            $this->parsePart(self::MINUTE),
70 107
            $this->parsePart(self::HOUR),
71 107
            $this->parsePart(self::DOM),
72 107
            $this->parsePart(self::MONTH),
73 107
            $this->parsePart(self::DOW),
74
        ]);
75
    }
76
77 19
    public function isHashed(): bool
78
    {
79 19
        return $this->getRawValue() !== $this->getParsedValue();
80
    }
81
82 6
    public function getNextRun(?string $timezone): \DateTimeInterface
83
    {
84 6
        return CronSchedule::factory($this->getParsedValue())->getNextRunDate('now', 0, false, $timezone);
85
    }
86
87 32
    public function isDue(?string $timezone): bool
88
    {
89 32
        return CronSchedule::factory($this->getParsedValue())->isDue('now', $timezone);
90
    }
91
92 107
    private function parsePart(int $position): string
93
    {
94 107
        $value = $this->parts[$position];
95
96 107
        if (\preg_match('#^H(\((\d+)-(\d+)\))?$#', $value, $matches)) {
97 21
            $value = $this->hashField(
98 21
                $matches[2] ?? self::RANGES[$position][0],
99 21
                $matches[3] ?? self::RANGES[$position][1]
100
            );
101
        }
102
103 107
        return $value;
104
    }
105
106 21
    private function hashField(int $start, int $end): string
107
    {
108 21
        $possibleValues = \range($start, $end);
109
110 21
        return $possibleValues[(int) \fmod(\hexdec(\mb_substr(\md5($this->context), 0, 10)), \count($possibleValues))];
111
    }
112
}
113