Completed
Push — master ( 62427c...e99507 )
by Ron
09:16
created

IntervalParser::getNext()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 8.9777
c 0
b 0
f 0
cc 6
nc 9
nop 2
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);
0 ignored issues
show
Bug introduced by
The variable $hours does not exist. Did you forget to declare it?

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.

Loading history...
Bug introduced by
The variable $minutes does not exist. Did you forget to declare it?

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.

Loading history...
Bug introduced by
The variable $seconds does not exist. Did you forget to declare it?

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.

Loading history...
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