Completed
Push — master ( 84470e...c291b9 )
by Ron
03:30
created

IntervalParser::parse()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 4
nc 4
nop 2
1
<?php
2
namespace Kir\Services\Cmd\Dispatcher\Common;
3
4
use Cron\CronExpression;
5
use DateTimeImmutable;
6
use DateTimeInterface;
7
use Generator;
8
use RuntimeException;
9
use Throwable;
10
11
class IntervalParser {
12
	/**
13
	 * @param string|int|array $interval
14
	 * @param DateTimeInterface|null $now
15
	 * @return DateTimeImmutable
16
	 */
17
	public static function getNext($interval, DateTimeInterface $now = null): DateTimeImmutable {
18
		if($now === null) {
19
			try {
20
				$now = new DateTimeImmutable();
21
			} catch (Throwable $e) {
22
				throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
23
			}
24
		}
25
		$result = null;
26
		foreach(self::parse($interval, $now) as $date) {
27
			if($result === null) {
28
				$result = $date;
29
			} elseif($date < $result) {
30
				$result = $date;
31
			}
32
		}
33
		return $result;
34
	}
35
	
36
	/**
37
	 * @param $interval
38
	 * @param DateTimeInterface|null $now
39
	 * @return Generator|DateTimeImmutable
40
	 */
41
	private static function parse($interval, DateTimeInterface $now) {
42
		if(is_array($interval)) {
43
			foreach($interval as $inner) {
44
				yield from self::parse($inner, $now);
45
			}
46
		} elseif(ctype_digit($interval)) {
47
			yield self::parseInt($interval, $now);
48
		} else {
49
			yield self::parseString($interval, $now);
50
		}
51
	}
52
	
53
	/**
54
	 * @param int $interval
55
	 * @param DateTimeInterface $now
56
	 * @return DateTimeImmutable
57
	 */
58
	private static function parseInt(int $interval, DateTimeInterface $now): DateTimeImmutable {
59
		return $now->modify("+{$interval} second");
60
	}
61
	
62
	/**
63
	 * @param string $interval
64
	 * @param DateTimeInterface $now
65
	 * @return DateTimeInterface
66
	 */
67
	private static function parseString(string $interval, DateTimeInterface $now): DateTimeInterface {
68
		if(preg_match('/^(\\d{1,2}|\\*):(\\d{1,2}|\\*)(?::(\\d{1,2}|\\*))?$/', $interval, $matches)) {
69
			$matches[] = 0;
70
			[$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...
71
			$today = date_create_immutable($now->format('c'))->setTime((int) $hours, (int) $minutes, (int) $seconds);
72
			$possibleDates = [
73
				$today,
74
				$today->modify('+24 hour')
75
			];
76
			return self::nearst($possibleDates, $now);
77
		}
78
		// Expect input to be a cron-expression
79
		$expr = new CronExpression($interval);
80
		$dt = $expr->getNextRunDate($now);
81
		return new DateTimeImmutable($dt->format('c'));
82
	}
83
	
84
	/**
85
	 * @param array $possibleDates
86
	 * @param DateTimeInterface $now
87
	 * @return DateTimeInterface
88
	 */
89
	private static function nearst(array $possibleDates, DateTimeInterface $now) {
90
		$current = null;
91
		foreach($possibleDates as $possibleDate) {
92
			if($now > $possibleDate) { // The current date is in the past
93
				continue;
94
			}
95
			if($current === null || $possibleDate < $current) {
96
				$current = $possibleDate;
97
			}
98
		}
99
		if($current !== null) {
100
			return $current;
101
		}
102
		throw new RuntimeException('No alternative lays in the future');
103
	}
104
}
105