1
|
|
|
<?php |
2
|
|
|
namespace Kir\Services\Cmd\Dispatcher\Common; |
3
|
|
|
|
4
|
|
|
use DateTimeImmutable; |
5
|
|
|
use DateTimeInterface; |
6
|
|
|
use Generator; |
7
|
|
|
use RuntimeException; |
8
|
|
|
use Throwable; |
9
|
|
|
|
10
|
|
|
class IntervalParser { |
11
|
|
|
/** |
12
|
|
|
* @param string|int|array $interval |
13
|
|
|
* @param DateTimeInterface|null $now |
14
|
|
|
* @return DateTimeImmutable |
15
|
|
|
*/ |
16
|
|
|
public static function getNext($interval, DateTimeInterface $now = null): DateTimeImmutable { |
17
|
|
|
if($now === null) { |
18
|
|
|
try { |
19
|
|
|
$now = new DateTimeImmutable(); |
20
|
|
|
} catch (Throwable $e) { |
21
|
|
|
throw new RuntimeException($e->getMessage(), $e->getCode(), $e); |
22
|
|
|
} |
23
|
|
|
} else { |
24
|
|
|
$now = DateTimeHelper::createImmutable($now); |
25
|
|
|
} |
26
|
|
|
$result = null; |
27
|
|
|
foreach(self::parse($interval, $now) as $date) { |
28
|
|
|
if($result === null) { |
29
|
|
|
$result = $date; |
30
|
|
|
} elseif($date < $result) { |
31
|
|
|
$result = $date; |
32
|
|
|
} |
33
|
|
|
} |
34
|
|
|
return $result; |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @param string|array $interval |
39
|
|
|
* @param DateTimeImmutable $now |
40
|
|
|
* @return Generator|DateTimeImmutable[] |
41
|
|
|
*/ |
42
|
|
|
private static function parse($interval, DateTimeImmutable $now) { |
43
|
|
|
if(is_array($interval)) { |
44
|
|
|
foreach($interval as $inner) { |
45
|
|
|
yield from self::parse($inner, $now); |
46
|
|
|
} |
47
|
|
|
} elseif(ctype_digit($interval)) { |
48
|
|
|
yield self::parseInt($interval, $now); |
49
|
|
|
} else { |
50
|
|
|
yield self::parseString($interval, $now); |
51
|
|
|
} |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @param int $interval |
56
|
|
|
* @param DateTimeImmutable $now |
57
|
|
|
* @return DateTimeImmutable |
58
|
|
|
*/ |
59
|
|
|
private static function parseInt(int $interval, DateTimeImmutable $now): DateTimeImmutable { |
60
|
|
|
return $now->modify("+{$interval} second"); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @param string $interval |
65
|
|
|
* @param DateTimeImmutable $now |
66
|
|
|
* @return DateTimeImmutable |
67
|
|
|
*/ |
68
|
|
|
private static function parseString(string $interval, DateTimeImmutable $now): DateTimeImmutable { |
69
|
|
|
if(preg_match('/^(\\d{1,2}|\\*):(\\d{1,2}|\\*)(?::(\\d{1,2}|\\*))?$/', $interval, $matches)) { |
70
|
|
|
$matches[] = 0; |
71
|
|
|
[$hours, $minutes, $seconds] = array_slice($matches, 1); |
|
|
|
|
72
|
|
|
$today = DateTimeHelper::createImmutable($now)->setTime((int) $hours, (int) $minutes, (int) $seconds); |
73
|
|
|
$possibleDates = [ |
74
|
|
|
$today, |
75
|
|
|
$today->modify('+24 hour') |
76
|
|
|
]; |
77
|
|
|
return self::nearst($possibleDates, $now); |
78
|
|
|
} |
79
|
|
|
// Expect input to be a cron-expression |
80
|
|
|
return DateTimeHelper::getNextRunDateFromCronExpression($interval, $now); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param array $possibleDates |
85
|
|
|
* @param DateTimeImmutable $now |
86
|
|
|
* @return DateTimeImmutable |
87
|
|
|
*/ |
88
|
|
|
private static function nearst(array $possibleDates, DateTimeImmutable $now) { |
89
|
|
|
$current = null; |
90
|
|
|
foreach($possibleDates as $possibleDate) { |
91
|
|
|
if($now > $possibleDate) { // The current date is in the past |
92
|
|
|
continue; |
93
|
|
|
} |
94
|
|
|
if($current === null || $possibleDate < $current) { |
95
|
|
|
$current = $possibleDate; |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
if($current !== null) { |
99
|
|
|
return $current; |
100
|
|
|
} |
101
|
|
|
throw new RuntimeException('No alternative lays in the future'); |
102
|
|
|
} |
103
|
|
|
} |
104
|
|
|
|
This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.