Cron::getExpression()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 4
nop 0
1
<?php
2
3
/*
4
 * janitor (http://juliangut.com/janitor).
5
 * Effortless maintenance management.
6
 *
7
 * @license BSD-3-Clause
8
 * @link https://github.com/juliangut/janitor
9
 * @author Julián Gutiérrez <[email protected]>
10
 */
11
12
namespace Janitor\Watcher;
13
14
use Cron\CronExpression;
15
16
/**
17
 * Cron syntax scheduled maintenance status watcher.
18
 *
19
 * Maintenance mode is considered to be On if current date is in the interval
20
 * initiated by a cron expression
21
 *
22
 * Cron expression syntax
23
 *   *    *    *    *    *    *
24
 *   |    |    |    |    |    |
25
 *   |    |    |    |    |    +--- Year [optional]
26
 *   |    |    |    |    +-------- Day of week (0-7) (Sunday=0|7)
27
 *   |    |    |    +------------- Month (1-12)
28
 *   |    |    +------------------ Day of month (1-31)
29
 *   |    +----------------------- Hour (0-23)
30
 *   +---------------------------- Minute (0-59)
31
 */
32
class Cron extends AbstractScheduled
33
{
34
    /**
35
     * Special cron expression shorthands.
36
     */
37
    const PERIOD_YEARLY   = '@yearly';
38
    const PERIOD_ANNUALLY = '@annually';
39
    const PERIOD_MONTHLY  = '@monthly';
40
    const PERIOD_WEEKLY   = '@weekly';
41
    const PERIOD_DAILY    = '@daily';
42
    const PERIOD_HOURLY   = '@hourly';
43
44
    /**
45
     * Mapper for especial cron expression shorthands.
46
     *
47
     * @var array
48
     */
49
    protected $expressionMapper = [
50
        self::PERIOD_YEARLY   => '0 0 1 1 *',
51
        self::PERIOD_ANNUALLY => '0 0 1 1 *',
52
        self::PERIOD_MONTHLY  => '0 0 1 * *',
53
        self::PERIOD_WEEKLY   => '0 0 * * 0',
54
        self::PERIOD_DAILY    => '0 0 * * *',
55
        self::PERIOD_HOURLY   => '0 * * * *',
56
    ];
57
58
    /**
59
     * Cron expression handler.
60
     *
61
     * @var \Cron\CronExpression
62
     */
63
    protected $expression;
64
65
    /**
66
     * Maintenance mode interval time.
67
     *
68
     * @var \DateInterval
69
     */
70
    protected $interval;
71
72
    /**
73
     * Cron constructor.
74
     *
75
     * @param \Cron\CronExpression|string $expression
76
     * @param \DateInterval|string        $interval
77
     * @param \DateTimeZone|string|int    $timeZone
0 ignored issues
show
Documentation introduced by
Should the type for parameter $timeZone not be \DateTimeZone|string|integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
78
     *
79
     * @throws \InvalidArgumentException
80
     */
81
    public function __construct($expression, $interval, $timeZone = null)
82
    {
83
        $this->setExpression($expression);
84
        $this->setInterval($interval);
85
86
        if ($timeZone !== null) {
87
            $this->setTimeZone($timeZone);
88
        }
89
    }
90
91
    /**
92
     * Set cron expression.
93
     *
94
     * @param \Cron\CronExpression|string $expression
95
     *
96
     * @throws \InvalidArgumentException
97
     *
98
     * @return $this
99
     */
100
    public function setExpression($expression)
101
    {
102
        if (!$expression instanceof CronExpression) {
103
            try {
104
                $this->expression = CronExpression::factory($expression);
105
            } catch (\Exception $exception) {
106
                throw new \InvalidArgumentException(
107
                    sprintf('"%s" is not a valid cron expression', $expression)
108
                );
109
            }
110
        }
111
112
        return $this;
113
    }
114
115
    /**
116
     * Get cron expression.
117
     *
118
     * @return string
119
     */
120
    public function getExpression()
121
    {
122
        $expression = $this->expression ? $this->expression->getExpression() : '';
123
124
        return !array_key_exists($expression, $this->expressionMapper)
125
            ? $expression
126
            : $this->expressionMapper[$expression];
127
    }
128
129
    /**
130
     * Sets a valid \DateInterval.
131
     *
132
     * @param \DateInterval|string $interval
133
     *
134
     * @throws \InvalidArgumentException
135
     *
136
     * @return $this
137
     */
138
    public function setInterval($interval)
139
    {
140
        if (!$interval instanceof \DateInterval) {
141
            try {
142
                $interval = new \DateInterval($interval);
143
            } catch (\Exception $exception) {
144
                throw new \InvalidArgumentException(
145
                    sprintf('"%s" is not a valid DateInterval string definition', $interval)
146
                );
147
            }
148
        }
149
150
        $this->interval = $interval;
151
152
        return $this;
153
    }
154
155
    /**
156
     * Get maintenance interval.
157
     *
158
     * @return \DateInterval
159
     */
160
    public function getInterval()
161
    {
162
        return $this->interval;
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168
    public function getStart()
169
    {
170
        if ($this->isActive()) {
171
            $now = new \DateTime('now', $this->getTimeZone());
172
173
            return $this->expression->getPreviousRunDate($now, 0, true);
174
        }
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    public function getEnd()
181
    {
182
        if ($this->isActive()) {
183
            $now = new \DateTime('now', $this->getTimeZone());
184
185
            $end = $this->expression->getPreviousRunDate($now, 0, true);
186
            $end->add($this->interval);
187
188
            return $end;
189
        }
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195
    public function getScheduledTimes($count = 5)
196
    {
197
        try {
198
            $now = new \DateTime('now', $this->getTimeZone());
199
200
            $runDates = $this->expression->getMultipleRunDates($count, $now);
201
        // @codeCoverageIgnoreStart
202
        } catch (\RuntimeException $exception) {
203
            return [];
204
        }
205
        // @codeCoverageIgnoreEnd
206
207
        $interval = $this->interval;
208
209
        return array_map(
210
            function (\DateTime $start) use ($interval) {
211
                $end = clone $start;
212
                $end->add($interval);
213
214
                return [
215
                    'start' => $start,
216
                    'end'   => $end,
217
                ];
218
            },
219
            $runDates
220
        );
221
    }
222
223
    /**
224
     * {@inheritdoc}
225
     */
226
    public function isScheduled()
227
    {
228
        try {
229
            $now = new \DateTime('now', $this->getTimeZone());
230
231
            return $this->expression->getNextRunDate($now) instanceof \DateTime;
232
        // @codeCoverageIgnoreStart
233
        } catch (\Exception $exception) {
234
            return false;
235
        }
236
        // @codeCoverageIgnoreEnd
237
    }
238
239
    /**
240
     * {@inheritdoc}
241
     */
242
    public function isActive()
243
    {
244
        $now = new \DateTime('now', $this->getTimeZone());
245
246
        try {
247
            $limitDate = $this->expression->getPreviousRunDate($now, 0, true);
248
            $limitDate->add($this->interval);
249
        // @codeCoverageIgnoreStart
250
        } catch (\RuntimeException $exception) {
251
            return false;
252
        }
253
        // @codeCoverageIgnoreEnd
254
255
        return $now <= $limitDate;
256
    }
257
}
258