Completed
Push — master ( c95954...129d81 )
by Jitendra
18s queued 11s
created

Validator::isValidWeekDay()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
eloc 9
c 3
b 0
f 1
dl 0
loc 19
rs 9.6111
cc 5
nc 5
nop 2
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
 * Cron segment validator.
18
 *
19
 * This class checks if a cron segment is valid.
20
 *
21
 * @author Jitendra Adhikari <[email protected]>
22
 */
23
class Validator
24
{
25
    /**
26
     * Check if the value is in range of given offset.
27
     *
28
     * @param int    $value
29
     * @param string $offset
30
     *
31
     * @return bool
32
     */
33
    public function inRange(int $value, string $offset): bool
34
    {
35
        $parts = \explode('-', $offset);
36
37
        return $parts[0] <= $value && $value <= $parts[1];
38
    }
39
40
    /**
41
     * Check if the value is in step of given offset.
42
     *
43
     * @param int    $value
44
     * @param string $offset
45
     *
46
     * @return bool
47
     */
48
    public function inStep(int $value, string $offset): bool
49
    {
50
        $parts = \explode('/', $offset, 2);
51
52
        if (empty($parts[1])) {
53
            return false;
54
        }
55
56
        if (\strpos($offset, '*/') !== false || \strpos($offset, '0/') !== false) {
57
            return $value % $parts[1] === 0;
58
        }
59
60
        $parts    = \explode('/', $offset, 2);
61
        $subparts = \explode('-', $parts[0], 2) + [1 => $value];
62
63
        return $this->inStepRange((int) $value, (int) $subparts[0], (int) $subparts[1], (int) $parts[1]);
64
    }
65
66
    /**
67
     * Check if the value falls between start and end when advanved by step.
68
     *
69
     * @param int $value
70
     * @param int $start
71
     * @param int $end
72
     * @param int $step
73
     *
74
     * @return bool
75
     */
76
    public function inStepRange(int $value, int $start, int $end, int $step): bool
77
    {
78
        if (($start + $step) > $end) {
79
            return false;
80
        }
81
82
        if ($start <= $value && $value <= $end) {
83
            return \in_array($value, \range($start, $end, $step));
84
        }
85
86
        return false;
87
    }
88
89
    /**
90
     * Check if month modifiers [L C W #] are satisfied.
91
     *
92
     * @internal
93
     *
94
     * @param string $value
95
     * @param array  $time
96
     *
97
     * @return bool
98
     */
99
    public function isValidMonthDay(string $value, array $time): bool
100
    {
101
        if ($value == 'L') {
102
            return $time[2] == $time[6];
103
        }
104
105
        if ($pos = \strpos($value, 'W')) {
106
            $value = \substr($value, 0, $pos);
107
            $month = $this->zeroPad($time[8]);
108
109
            return $this->isClosestWeekDay((int) $value, $month, $time);
110
        }
111
112
        $this->unexpectedValue(2, $value);
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return boolean. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
113
    }
114
115
    protected function isClosestWeekDay(int $value, string $month, array $time): bool
116
    {
117
        foreach ([0, -1, 1, -2, 2] as $i) {
118
            $incr = $value + $i;
119
            if ($incr < 1 || $incr > $time[6]) {
120
                continue;
121
            }
122
123
            $incr  = $this->zeroPad($incr);
124
            $parts = \explode(' ', \date('N m j', \strtotime("{$time[5]}-$month-$incr")));
125
            if ($parts[0] < 6 && $parts[1] == $month) {
126
                return $time[2] == $parts[2];
127
            }
128
        }
129
130
        // @codeCoverageIgnoreStart
131
        return false;
132
        // @codeCoverageIgnoreEnd
133
    }
134
135
    /**
136
     * Check if week modifiers [L C W #] are satisfied.
137
     *
138
     * @internal
139
     *
140
     * @param string $value
141
     * @param array  $time
142
     *
143
     * @return bool
144
     */
145
    public function isValidWeekDay(string $value, array $time): bool
146
    {
147
        $month = $this->zeroPad($time[8]);
148
149
        if (\strpos($value, 'L')) {
150
            return $this->isLastWeekDay($value, $month, $time);
151
        }
152
153
        if (!\strpos($value, '#')) {
154
            $this->unexpectedValue(4, $value);
155
        }
156
157
        list($day, $nth) = \explode('#', \str_replace('0#', '7#', $value));
158
159
        if (!$this->isNthWeekDay((int) $day, (int) $nth) || $time[9] != $day) {
160
            return false;
161
        }
162
163
        return \intval($time[7] / 7) == $nth - 1;
164
    }
165
166
    /**
167
     * @param int    $pos
168
     * @param string $value
169
     *
170
     * @throws \UnexpectedValueException
171
     */
172
    public function unexpectedValue(int $pos, string $value)
173
    {
174
        throw new \UnexpectedValueException(
175
            \sprintf('Invalid offset value at segment #%d: %s', $pos, $value)
176
        );
177
    }
178
179
    protected function isLastWeekDay(string $value, string $month, array $time): bool
180
    {
181
        $value = \explode('L', \str_replace('7L', '0L', $value));
182
        $decr  = $time[6];
183
184
        for ($i = 0; $i < 7; $i++) {
185
            $decr -= $i;
186
            if (\date('w', \strtotime("{$time[5]}-$month-$decr")) == $value[0]) {
187
                return $time[2] == $decr;
188
            }
189
        }
190
191
        return false;
192
    }
193
194
    protected function isNthWeekDay(int $day, int $nth): bool
195
    {
196
        return !($day < 0 || $day > 7 || $nth < 1 || $nth > 5);
197
    }
198
199
    protected function zeroPad($value): string
200
    {
201
        return \str_pad((string) $value, 2, '0', \STR_PAD_LEFT);
202
    }
203
}
204