GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#81)
by
unknown
01:32
created

OpeningHours::createAndMergeOverlappingRanges()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Spatie\OpeningHours;
4
5
use DateTime;
6
use DateTimeZone;
7
use DateTimeImmutable;
8
use DateTimeInterface;
9
use Spatie\OpeningHours\Helpers\Arr;
10
use Spatie\OpeningHours\Exceptions\Exception;
11
use Spatie\OpeningHours\Exceptions\InvalidDate;
12
use Spatie\OpeningHours\Exceptions\InvalidDayName;
13
14
class OpeningHours
15
{
16
    /** @var \Spatie\OpeningHours\Day[] */
17
    protected $openingHours = [];
18
19
    /** @var array */
20
    protected $exceptions = [];
21
22
    /** @var DateTimeZone|null */
23
    protected $timezone = null;
24
25
    public function __construct($timezone = null)
26
    {
27
        $this->timezone = $timezone ? new DateTimeZone($timezone) : null;
28
29
        $this->openingHours = Day::mapDays(function () {
30
            return new OpeningHoursForDay();
31
        });
32
    }
33
34
    /**
35
     * @param array $data
36
     *
37
     * @return static
38
     */
39
    public static function create(array $data)
40
    {
41
        return (new static())->fill($data);
42
    }
43
44
    /**
45
     * @param array $data
46
     *
47
     * @return array
48
     */
49
    public static function mergeOverlappingRanges(array $data)
50
    {
51
        $result = [];
52
        $ranges = [];
53
        foreach ($data as $key => $value) {
54
            $value = is_array($value)
55
                ? static::mergeOverlappingRanges($value)
56
                : (is_string($value) ? TimeRange::fromString($value) : $value);
57
58
            if ($value instanceof TimeRange) {
59
                $newRanges = [];
60
                foreach ($ranges as $range) {
61
                    if ($value->format() === $range->format()) {
62
                        continue 2;
63
                    }
64
65
                    if ($value->overlaps($range) || $range->overlaps($value)) {
66
                        $value = TimeRange::fromList([$value, $range]);
67
68
                        continue;
69
                    }
70
71
                    $newRanges[] = $range;
72
                }
73
74
                $newRanges[] = $value;
75
                $ranges = $newRanges;
76
77
                continue;
78
            }
79
80
            $result[$key] = $value;
81
        }
82
83
        foreach ($ranges as $range) {
84
            $result[] = $range;
85
        }
86
87
        return $result;
88
    }
89
90
    /**
91
     * @param array $data
92
     *
93
     * @return static
94
     */
95
    public static function createAndMergeOverlappingRanges(array $data)
96
    {
97
        return static::create(static::mergeOverlappingRanges($data));
98
    }
99
100
    /**
101
     * @param array $data
102
     *
103
     * @return bool
104
     */
105
    public static function isValid(array $data): bool
106
    {
107
        try {
108
            static::create($data);
109
110
            return true;
111
        } catch (Exception $exception) {
112
            return false;
113
        }
114
    }
115
116
    public function fill(array $data)
117
    {
118
        list($openingHours, $exceptions) = $this->parseOpeningHoursAndExceptions($data);
119
120
        foreach ($openingHours as $day => $openingHoursForThisDay) {
121
            $this->setOpeningHoursFromStrings($day, $openingHoursForThisDay);
122
        }
123
124
        $this->setExceptionsFromStrings($exceptions);
125
126
        return $this;
127
    }
128
129
    public function forWeek(): array
130
    {
131
        return $this->openingHours;
132
    }
133
134
    public function forWeekCombined(): array
135
    {
136
        $equalDays = [];
137
        $allOpeningHours = $this->openingHours;
138
        $uniqueOpeningHours = array_unique($allOpeningHours);
139
        $nonUniqueOpeningHours = $allOpeningHours;
140
141
        foreach ($uniqueOpeningHours as $day => $value) {
142
            $equalDays[$day] = ['days' => [$day], 'opening_hours' => $value];
143
            unset($nonUniqueOpeningHours[$day]);
144
        }
145
146
        foreach ($uniqueOpeningHours as $uniqueDay => $uniqueValue) {
147
            foreach ($nonUniqueOpeningHours as $nonUniqueDay => $nonUniqueValue) {
148
                if ((string) $uniqueValue === (string) $nonUniqueValue) {
149
                    $equalDays[$uniqueDay]['days'][] = $nonUniqueDay;
150
                }
151
            }
152
        }
153
154
        return $equalDays;
155
    }
156
157
    public function forDay(string $day): OpeningHoursForDay
158
    {
159
        $day = $this->normalizeDayName($day);
160
161
        return $this->openingHours[$day];
162
    }
163
164
    public function forDate(DateTimeInterface $date): OpeningHoursForDay
165
    {
166
        $date = $this->applyTimezone($date);
167
168
        return $this->exceptions[$date->format('Y-m-d')] ?? ($this->exceptions[$date->format('m-d')] ?? $this->forDay(Day::onDateTime($date)));
169
    }
170
171
    public function exceptions(): array
172
    {
173
        return $this->exceptions;
174
    }
175
176
    public function isOpenOn(string $day): bool
177
    {
178
        return count($this->forDay($day)) > 0;
179
    }
180
181
    public function isClosedOn(string $day): bool
182
    {
183
        return ! $this->isOpenOn($day);
184
    }
185
186
    public function isOpenAt(DateTimeInterface $dateTime): bool
187
    {
188
        $dateTime = $this->applyTimezone($dateTime);
189
190
        $openingHoursForDay = $this->forDate($dateTime);
191
192
        return $openingHoursForDay->isOpenAt(Time::fromDateTime($dateTime));
193
    }
194
195
    public function isClosedAt(DateTimeInterface $dateTime): bool
196
    {
197
        return ! $this->isOpenAt($dateTime);
198
    }
199
200
    public function isOpen(): bool
201
    {
202
        return $this->isOpenAt(new DateTime());
203
    }
204
205
    public function isClosed(): bool
206
    {
207
        return $this->isClosedAt(new DateTime());
208
    }
209
210
    /**
211
     * @todo This should return DateTimeInterface on next major release
212
     */
213 View Code Duplication
    public function nextOpen(DateTimeInterface $dateTime): DateTime
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
214
    {
215
        if (! ($dateTime instanceof DateTimeImmutable)) {
216
            $dateTime = clone $dateTime;
217
        }
218
219
        $openingHoursForDay = $this->forDate($dateTime);
220
        $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime));
221
222
        while ($nextOpen === false) {
223
            $dateTime = $dateTime
224
                ->modify('+1 day')
225
                ->setTime(0, 0, 0);
226
227
            $openingHoursForDay = $this->forDate($dateTime);
228
229
            $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime));
230
        }
231
232
        $nextDateTime = $nextOpen->toDateTime();
233
        $dateTime = $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
234
235
        return $dateTime;
236
    }
237
238
    /**
239
     * @todo This should return DateTimeInterface on next major release
240
     */
241 View Code Duplication
    public function nextClose(DateTimeInterface $dateTime): DateTime
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
242
    {
243
        if (! ($dateTime instanceof DateTimeImmutable)) {
244
            $dateTime = clone $dateTime;
245
        }
246
247
        $openingHoursForDay = $this->forDate($dateTime);
248
        $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
249
250
        while ($nextClose === false) {
251
            $dateTime = $dateTime
252
                ->modify('+1 day')
253
                ->setTime(0, 0, 0);
254
255
            $openingHoursForDay = $this->forDate($dateTime);
256
257
            $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
258
        }
259
260
        $nextDateTime = $nextClose->toDateTime();
261
        $dateTime = $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
262
263
        return $dateTime;
264
    }
265
266
    public function regularClosingDays(): array
267
    {
268
        return array_keys($this->filter(function (OpeningHoursForDay $openingHoursForDay) {
269
            return $openingHoursForDay->isEmpty();
270
        }));
271
    }
272
273
    public function regularClosingDaysISO(): array
274
    {
275
        return Arr::map($this->regularClosingDays(), [Day::class, 'toISO']);
276
    }
277
278
    public function exceptionalClosingDates(): array
279
    {
280
        $dates = array_keys($this->filterExceptions(function (OpeningHoursForDay $openingHoursForDay, $date) {
0 ignored issues
show
Unused Code introduced by
The parameter $date is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
281
            return $openingHoursForDay->isEmpty();
282
        }));
283
284
        return Arr::map($dates, function ($date) {
285
            return DateTime::createFromFormat('Y-m-d', $date);
286
        });
287
    }
288
289
    public function setTimezone($timezone)
290
    {
291
        $this->timezone = new DateTimeZone($timezone);
292
    }
293
294
    protected function parseOpeningHoursAndExceptions(array $data): array
295
    {
296
        $exceptions = Arr::pull($data, 'exceptions', []);
297
        $openingHours = [];
298
299
        foreach ($data as $day => $openingHoursData) {
300
            $openingHours[$this->normalizeDayName($day)] = $openingHoursData;
301
        }
302
303
        return [$openingHours, $exceptions];
304
    }
305
306
    protected function setOpeningHoursFromStrings(string $day, array $openingHours)
307
    {
308
        $day = $this->normalizeDayName($day);
309
310
        $this->openingHours[$day] = OpeningHoursForDay::fromStrings($openingHours);
311
    }
312
313
    protected function setExceptionsFromStrings(array $exceptions)
314
    {
315
        $this->exceptions = Arr::map($exceptions, function (array $openingHours, string $date) {
316
            $recurring = DateTime::createFromFormat('m-d', $date);
317
318
            if ($recurring === false || $recurring->format('m-d') !== $date) {
319
                $dateTime = DateTime::createFromFormat('Y-m-d', $date);
320
321
                if ($dateTime === false || $dateTime->format('Y-m-d') !== $date) {
322
                    throw InvalidDate::invalidDate($date);
323
                }
324
            }
325
326
            return OpeningHoursForDay::fromStrings($openingHours);
327
        });
328
    }
329
330
    protected function normalizeDayName(string $day)
331
    {
332
        $day = strtolower($day);
333
334
        if (! Day::isValid($day)) {
335
            throw new InvalidDayName();
336
        }
337
338
        return $day;
339
    }
340
341
    protected function applyTimezone(DateTimeInterface $date)
342
    {
343
        if ($this->timezone) {
344
            $date = $date->setTimezone($this->timezone);
345
        }
346
347
        return $date;
348
    }
349
350
    public function filter(callable $callback): array
351
    {
352
        return Arr::filter($this->openingHours, $callback);
353
    }
354
355
    public function map(callable $callback): array
356
    {
357
        return Arr::map($this->openingHours, $callback);
358
    }
359
360
    public function flatMap(callable $callback): array
361
    {
362
        return Arr::flatMap($this->openingHours, $callback);
363
    }
364
365
    public function filterExceptions(callable $callback): array
366
    {
367
        return Arr::filter($this->exceptions, $callback);
368
    }
369
370
    public function mapExceptions(callable $callback): array
371
    {
372
        return Arr::map($this->exceptions, $callback);
373
    }
374
375
    public function flatMapExceptions(callable $callback): array
376
    {
377
        return Arr::flatMap($this->exceptions, $callback);
378
    }
379
380
    public function asStructuredData(): array
381
    {
382 View Code Duplication
        $regularHours = $this->flatMap(function (OpeningHoursForDay $openingHoursForDay, string $day) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
383
            return $openingHoursForDay->map(function (TimeRange $timeRange) use ($day) {
384
                return [
385
                    '@type' => 'OpeningHoursSpecification',
386
                    'dayOfWeek' => ucfirst($day),
387
                    'opens' => (string) $timeRange->start(),
388
                    'closes' => (string) $timeRange->end(),
389
                ];
390
            });
391
        });
392
393
        $exceptions = $this->flatMapExceptions(function (OpeningHoursForDay $openingHoursForDay, string $date) {
394
            if ($openingHoursForDay->isEmpty()) {
395
                return [[
396
                    '@type' => 'OpeningHoursSpecification',
397
                    'opens' => '00:00',
398
                    'closes' => '00:00',
399
                    'validFrom' => $date,
400
                    'validThrough' => $date,
401
                ]];
402
            }
403
404 View Code Duplication
            return $openingHoursForDay->map(function (TimeRange $timeRange) use ($date) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
405
                return [
406
                    '@type' => 'OpeningHoursSpecification',
407
                    'opens' => (string) $timeRange->start(),
408
                    'closes' => (string) $timeRange->end(),
409
                    'validFrom' => $date,
410
                    'validThrough' => $date,
411
                ];
412
            });
413
        });
414
415
        return array_merge($regularHours, $exceptions);
416
    }
417
}
418