1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Speicher210\BusinessHours\Day; |
4
|
|
|
|
5
|
|
|
use Speicher210\BusinessHours\Day\Time\Time; |
6
|
|
|
use Speicher210\BusinessHours\Day\Time\TimeInterval; |
7
|
|
|
use Speicher210\BusinessHours\Day\Time\TimeIntervalInterface; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Abstract day class. |
11
|
|
|
*/ |
12
|
|
|
abstract class AbstractDay implements DayInterface |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* The days of the week. |
16
|
|
|
* |
17
|
|
|
* @var array |
18
|
|
|
*/ |
19
|
|
|
private $daysOfWeek = array( |
20
|
|
|
DayInterface::WEEK_DAY_MONDAY => 'Monday', |
21
|
|
|
DayInterface::WEEK_DAY_TUESDAY => 'Tuesday', |
22
|
|
|
DayInterface::WEEK_DAY_WEDNESDAY => 'Wednesday', |
23
|
|
|
DayInterface::WEEK_DAY_THURSDAY => 'Thursday', |
24
|
|
|
DayInterface::WEEK_DAY_FRIDAY => 'Friday', |
25
|
|
|
DayInterface::WEEK_DAY_SATURDAY => 'Saturday', |
26
|
|
|
DayInterface::WEEK_DAY_SUNDAY => 'Sunday', |
27
|
|
|
); |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* The day of week. |
31
|
|
|
* |
32
|
|
|
* @var integer |
33
|
|
|
*/ |
34
|
|
|
protected $dayOfWeek; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* The time intervals. |
38
|
|
|
* |
39
|
|
|
* @var TimeIntervalInterface[] |
40
|
|
|
*/ |
41
|
|
|
protected $openingHoursIntervals; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Constructor. |
45
|
|
|
* |
46
|
|
|
* @param integer $dayOfWeek The day of week. |
47
|
|
|
* @param TimeIntervalInterface[] $openingHoursIntervals The opening hours intervals. |
48
|
|
|
*/ |
49
|
|
|
public function __construct($dayOfWeek, array $openingHoursIntervals) |
50
|
|
|
{ |
51
|
|
|
$this->setDayOfWeek($dayOfWeek); |
52
|
|
|
$this->setOpeningHoursIntervals($openingHoursIntervals); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* {@inheritdoc} |
57
|
|
|
*/ |
58
|
|
|
public function getDayOfWeek() |
59
|
|
|
{ |
60
|
|
|
return $this->dayOfWeek; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* {@inheritdoc} |
65
|
|
|
*/ |
66
|
|
|
public function getDayOfWeekName() |
67
|
|
|
{ |
68
|
|
|
return $this->daysOfWeek[$this->dayOfWeek]; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* {@inheritdoc} |
73
|
|
|
*/ |
74
|
|
|
public function getOpeningHoursIntervals() |
75
|
|
|
{ |
76
|
|
|
return $this->openingHoursIntervals; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* {@inheritdoc} |
81
|
|
|
*/ |
82
|
|
|
public function getClosestPreviousOpeningHoursInterval(Time $time) |
83
|
|
|
{ |
84
|
|
|
foreach ($this->openingHoursIntervals as $openingHoursInterval) { |
85
|
|
|
if ($openingHoursInterval->contains($time)) { |
86
|
|
|
return $openingHoursInterval; |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
return $this->getPreviousOpeningHoursInterval($time); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* {@inheritdoc} |
95
|
|
|
*/ |
96
|
|
|
public function getClosestNextOpeningHoursInterval(Time $time) |
97
|
|
|
{ |
98
|
|
|
foreach ($this->openingHoursIntervals as $openingHoursInterval) { |
99
|
|
|
if ($openingHoursInterval->contains($time)) { |
100
|
|
|
return $openingHoursInterval; |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
return $this->getNextOpeningHoursInterval($time); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* {@inheritdoc} |
109
|
|
|
*/ |
110
|
|
View Code Duplication |
public function getPreviousOpeningHoursInterval(Time $time) |
|
|
|
|
111
|
|
|
{ |
112
|
|
|
$closestTime = null; |
113
|
|
|
$closestInterval = null; |
114
|
|
|
|
115
|
|
|
/** @var TimeIntervalInterface $interval */ |
116
|
|
|
foreach (array_reverse($this->openingHoursIntervals) as $interval) { |
117
|
|
|
$distance = $time->toSeconds() - $interval->getEnd()->toSeconds(); |
118
|
|
|
|
119
|
|
|
if ($distance < 0) { |
120
|
|
|
continue; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
if (null === $closestTime) { |
124
|
|
|
$closestTime = $interval->getEnd(); |
125
|
|
|
$closestInterval = $interval; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
if ($distance < ($time->toSeconds() - $closestTime->toSeconds())) { |
129
|
|
|
$closestTime = $interval->getEnd(); |
130
|
|
|
$closestInterval = $interval; |
131
|
|
|
} |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
return $closestInterval; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* {@inheritdoc} |
139
|
|
|
*/ |
140
|
|
View Code Duplication |
public function getNextOpeningHoursInterval(Time $time) |
|
|
|
|
141
|
|
|
{ |
142
|
|
|
$closestTime = null; |
143
|
|
|
$closestInterval = null; |
144
|
|
|
|
145
|
|
|
foreach ($this->openingHoursIntervals as $interval) { |
146
|
|
|
$distance = $interval->getStart()->toSeconds() - $time->toSeconds(); |
147
|
|
|
|
148
|
|
|
if ($distance < 0) { |
149
|
|
|
continue; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
if (null === $closestTime) { |
153
|
|
|
$closestTime = $interval->getStart(); |
154
|
|
|
$closestInterval = $interval; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
if ($distance < ($closestTime->toSeconds() - $time->toSeconds())) { |
158
|
|
|
$closestTime = $interval->getStart(); |
159
|
|
|
$closestInterval = $interval; |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
return $closestInterval; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* {@inheritdoc} |
168
|
|
|
*/ |
169
|
|
|
public function getOpeningTime() |
170
|
|
|
{ |
171
|
|
|
return $this->openingHoursIntervals[0]->getStart(); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* {@inheritdoc} |
176
|
|
|
*/ |
177
|
|
|
public function getClosingTime() |
178
|
|
|
{ |
179
|
|
|
/** @var TimeIntervalInterface $interval */ |
180
|
|
|
$interval = end($this->openingHoursIntervals); |
181
|
|
|
|
182
|
|
|
return $interval->getEnd(); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* {@inheritdoc} |
187
|
|
|
*/ |
188
|
|
|
public function isWithinOpeningHours(Time $time) |
189
|
|
|
{ |
190
|
|
|
foreach ($this->openingHoursIntervals as $interval) { |
191
|
|
|
if ($interval->contains($time)) { |
192
|
|
|
return true; |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
return false; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Set the day of week. |
201
|
|
|
* |
202
|
|
|
* @param integer $dayOfWeek |
203
|
|
|
* @throws \OutOfBoundsException If the given day is invalid. |
204
|
|
|
*/ |
205
|
|
|
protected function setDayOfWeek($dayOfWeek) |
206
|
|
|
{ |
207
|
|
|
if (!isset($this->daysOfWeek[$dayOfWeek])) { |
208
|
|
|
throw new \OutOfBoundsException(sprintf('Invalid day of week "%s".', $dayOfWeek)); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
$this->dayOfWeek = $dayOfWeek; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
/** |
215
|
|
|
* Set the opening hours intervals. |
216
|
|
|
* |
217
|
|
|
* @param TimeIntervalInterface[] $openingHoursIntervals The opening hours intervals. |
218
|
|
|
* @throws \InvalidArgumentException If no days are passed or invalid interval is passed. |
219
|
|
|
*/ |
220
|
|
|
protected function setOpeningHoursIntervals(array $openingHoursIntervals) |
221
|
|
|
{ |
222
|
|
|
if (empty($openingHoursIntervals)) { |
223
|
|
|
throw new \InvalidArgumentException('The day must have at least one opening interval.'); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
$intervals = array(); |
227
|
|
|
|
228
|
|
|
foreach ($openingHoursIntervals as $interval) { |
229
|
|
|
if (!$interval instanceof TimeIntervalInterface) { |
230
|
|
|
throw new \InvalidArgumentException(sprintf('Interval must be a %s', TimeIntervalInterface::class)); |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
$intervals[] = $interval; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
$this->openingHoursIntervals = $this->flattenOpeningHoursIntervals($intervals); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Flatten the intervals that overlap. |
241
|
|
|
* |
242
|
|
|
* @param TimeIntervalInterface[] $openingHoursIntervals |
243
|
|
|
* @return TimeIntervalInterface[] |
244
|
|
|
*/ |
245
|
|
|
protected function flattenOpeningHoursIntervals(array $openingHoursIntervals) |
246
|
|
|
{ |
247
|
|
|
usort( |
248
|
|
|
$openingHoursIntervals, |
249
|
|
|
function (TimeIntervalInterface $a, TimeIntervalInterface $b) { |
250
|
|
|
return ($a->getStart() > $b->getStart()) ? 1 : -1; |
251
|
|
|
} |
252
|
|
|
); |
253
|
|
|
|
254
|
|
|
$intervals = array(); |
255
|
|
|
$tmpInterval = reset($openingHoursIntervals); |
256
|
|
|
foreach ($openingHoursIntervals as $interval) { |
257
|
|
|
/** @var TimeInterval $tmpInterval */ |
258
|
|
|
if ($interval->getStart() <= $tmpInterval->getEnd()) { |
259
|
|
|
$tmpInterval = new TimeInterval( |
260
|
|
|
$tmpInterval->getStart(), |
261
|
|
|
max($tmpInterval->getEnd(), $interval->getEnd()) |
262
|
|
|
); |
263
|
|
|
} else { |
264
|
|
|
$intervals[] = $tmpInterval; |
265
|
|
|
$tmpInterval = $interval; |
266
|
|
|
} |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
$intervals[] = $tmpInterval; |
270
|
|
|
|
271
|
|
|
return $intervals; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Handle cloning. |
276
|
|
|
*/ |
277
|
|
|
public function __clone() |
278
|
|
|
{ |
279
|
|
|
$openingHoursIntervals = array(); |
280
|
|
|
|
281
|
|
|
foreach ($this->openingHoursIntervals as $openingHoursInterval) { |
282
|
|
|
$openingHoursIntervals[] = clone $openingHoursInterval; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
$this->openingHoursIntervals = $openingHoursIntervals; |
286
|
|
|
} |
287
|
|
|
} |
288
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.