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
Push — master ( aea5ce...acd138 )
by Kyle
22s
created

OpeningHours::previousClose()   B

Complexity

Conditions 10
Paths 24

Size

Total Lines 43

Duplication

Lines 26
Ratio 60.47 %

Importance

Changes 0
Metric Value
dl 26
loc 43
rs 7.6666
c 0
b 0
f 0
cc 10
nc 24
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Spatie\OpeningHours;
4
5
use DateTime;
6
use DateTimeZone;
7
use DateTimeInterface;
8
use Spatie\OpeningHours\Helpers\Arr;
9
use Spatie\OpeningHours\Helpers\DataTrait;
10
use Spatie\OpeningHours\Exceptions\Exception;
11
use Spatie\OpeningHours\Exceptions\InvalidDate;
12
use Spatie\OpeningHours\Helpers\DateTimeCopier;
13
use Spatie\OpeningHours\Exceptions\InvalidDayName;
14
use Spatie\OpeningHours\Exceptions\MaximumLimitExceeded;
15
16
class OpeningHours
17
{
18
    const DEFAULT_DAY_LIMIT = 8;
19
20
    use DataTrait, DateTimeCopier;
21
22
    /** @var \Spatie\OpeningHours\Day[] */
23
    protected $openingHours = [];
24
25
    /** @var \Spatie\OpeningHours\OpeningHoursForDay[] */
26
    protected $exceptions = [];
27
28
    /** @var callable[] */
29
    protected $filters = [];
30
31
    /** @var DateTimeZone|null */
32
    protected $timezone = null;
33
34
    /** @var bool Allow for overflowing time ranges which overflow into the next day */
35
    protected $overflow;
36
37
    /** @var int Number of days to try before abandoning the search of the next close/open time */
38
    protected $dayLimit = null;
39
40
    public function __construct($timezone = null)
41
    {
42
        if ($timezone instanceof DateTimeZone) {
43
            $this->timezone = $timezone;
44
        } elseif (is_string($timezone)) {
45
            $this->timezone = new DateTimeZone($timezone);
46
        } elseif ($timezone) {
47
            throw new \InvalidArgumentException('Invalid Timezone');
48
        }
49
50
        $this->openingHours = Day::mapDays(function () {
51
            return new OpeningHoursForDay();
52
        });
53
    }
54
55
    /**
56
     * @param string[][]               $data
57
     * @param string|DateTimeZone|null $timezone
58
     *
59
     * @return static
60
     */
61
    public static function create(array $data, $timezone = null): self
62
    {
63
        return (new static($timezone))->fill($data);
64
    }
65
66
    /**
67
     * @param array $data
68
     *
69
     * @return array
70
     */
71
    public static function mergeOverlappingRanges(array $data)
72
    {
73
        $result = [];
74
        $ranges = [];
75
        foreach ($data as $key => $value) {
76
            $value = is_array($value)
77
                ? static::mergeOverlappingRanges($value)
78
                : (is_string($value) ? TimeRange::fromString($value) : $value);
79
80
            if ($value instanceof TimeRange) {
81
                $newRanges = [];
82
                foreach ($ranges as $range) {
83
                    if ($value->format() === $range->format()) {
84
                        continue 2;
85
                    }
86
87
                    if ($value->overlaps($range) || $range->overlaps($value)) {
88
                        $value = TimeRange::fromList([$value, $range]);
89
90
                        continue;
91
                    }
92
93
                    $newRanges[] = $range;
94
                }
95
96
                $newRanges[] = $value;
97
                $ranges = $newRanges;
98
99
                continue;
100
            }
101
102
            $result[$key] = $value;
103
        }
104
105
        foreach ($ranges as $range) {
106
            $result[] = $range;
107
        }
108
109
        return $result;
110
    }
111
112
    /**
113
     * @param string[][]               $data
114
     * @param string|DateTimeZone|null $timezone
115
     *
116
     * @return static
117
     */
118
    public static function createAndMergeOverlappingRanges(array $data, $timezone = null)
119
    {
120
        return static::create(static::mergeOverlappingRanges($data), $timezone);
121
    }
122
123
    /**
124
     * @param array $data
125
     *
126
     * @return bool
127
     */
128
    public static function isValid(array $data): bool
129
    {
130
        try {
131
            static::create($data);
132
133
            return true;
134
        } catch (Exception $exception) {
135
            return false;
136
        }
137
    }
138
139
    /**
140
     * Set the number of days to try before abandoning the search of the next close/open time.
141
     *
142
     * @param int $dayLimit number of days
143
     */
144
    public function setDayLimit(int $dayLimit)
145
    {
146
        $this->dayLimit = $dayLimit;
147
    }
148
149
    /**
150
     * Get the number of days to try before abandoning the search of the next close/open time.
151
     *
152
     * @return int
153
     */
154
    public function getDayLimit(): int
155
    {
156
        return $this->dayLimit ?: static::DEFAULT_DAY_LIMIT;
157
    }
158
159
    public function setFilters(array $filters)
160
    {
161
        $this->filters = $filters;
162
163
        return $this;
164
    }
165
166
    public function getFilters(): array
167
    {
168
        return $this->filters;
169
    }
170
171
    public function fill(array $data)
172
    {
173
        list($openingHours, $exceptions, $metaData, $filters, $overflow) = $this->parseOpeningHoursAndExceptions($data);
174
175
        $this->overflow = $overflow;
176
177
        foreach ($openingHours as $day => $openingHoursForThisDay) {
178
            $this->setOpeningHoursFromStrings($day, $openingHoursForThisDay);
179
        }
180
181
        $this->setExceptionsFromStrings($exceptions);
182
183
        return $this->setFilters($filters)->setData($metaData);
184
    }
185
186
    public function forWeek(): array
187
    {
188
        return $this->openingHours;
189
    }
190
191
    public function forWeekCombined(): array
192
    {
193
        $equalDays = [];
194
        $allOpeningHours = $this->openingHours;
195
        $uniqueOpeningHours = array_unique($allOpeningHours);
196
        $nonUniqueOpeningHours = $allOpeningHours;
197
198
        foreach ($uniqueOpeningHours as $day => $value) {
199
            $equalDays[$day] = ['days' => [$day], 'opening_hours' => $value];
200
            unset($nonUniqueOpeningHours[$day]);
201
        }
202
203
        foreach ($uniqueOpeningHours as $uniqueDay => $uniqueValue) {
204
            foreach ($nonUniqueOpeningHours as $nonUniqueDay => $nonUniqueValue) {
205
                if ((string) $uniqueValue === (string) $nonUniqueValue) {
206
                    $equalDays[$uniqueDay]['days'][] = $nonUniqueDay;
207
                }
208
            }
209
        }
210
211
        return $equalDays;
212
    }
213
214
    public function forDay(string $day): OpeningHoursForDay
215
    {
216
        $day = $this->normalizeDayName($day);
217
218
        return $this->openingHours[$day];
219
    }
220
221
    public function forDate(DateTimeInterface $date): OpeningHoursForDay
222
    {
223
        $date = $this->applyTimezone($date);
224
225
        foreach ($this->filters as $filter) {
226
            $result = $filter($date);
227
228
            if (is_array($result)) {
229
                return OpeningHoursForDay::fromStrings($result);
230
            }
231
        }
232
233
        return $this->exceptions[$date->format('Y-m-d')] ?? ($this->exceptions[$date->format('m-d')] ?? $this->forDay(Day::onDateTime($date)));
234
    }
235
236
    /**
237
     * @param DateTimeInterface $date
238
     *
239
     * @return TimeRange[]
240
     */
241
    public function forDateTime(DateTimeInterface $date): array
242
    {
243
        return array_merge(
244
            iterator_to_array($this->forDate(
245
                $this->yesterday($date)
246
            )->forNightTime(Time::fromDateTime($date))),
247
            iterator_to_array($this->forDate($date)->forTime(Time::fromDateTime($date)))
248
        );
249
    }
250
251
    public function exceptions(): array
252
    {
253
        return $this->exceptions;
254
    }
255
256
    public function isOpenOn(string $day): bool
257
    {
258
        return count($this->forDay($day)) > 0;
259
    }
260
261
    public function isClosedOn(string $day): bool
262
    {
263
        return ! $this->isOpenOn($day);
264
    }
265
266
    public function isOpenAt(DateTimeInterface $dateTime): bool
267
    {
268
        $dateTime = $this->applyTimezone($dateTime);
269
270
        if ($this->overflow) {
271
            $dateTimeMinus1Day = $this->yesterday($dateTime);
272
            $openingHoursForDayBefore = $this->forDate($dateTimeMinus1Day);
273
            if ($openingHoursForDayBefore->isOpenAtNight(Time::fromDateTime($dateTimeMinus1Day))) {
274
                return true;
275
            }
276
        }
277
278
        $openingHoursForDay = $this->forDate($dateTime);
279
280
        return $openingHoursForDay->isOpenAt(Time::fromDateTime($dateTime));
281
    }
282
283
    public function isClosedAt(DateTimeInterface $dateTime): bool
284
    {
285
        return ! $this->isOpenAt($dateTime);
286
    }
287
288
    public function isOpen(): bool
289
    {
290
        return $this->isOpenAt(new DateTime());
291
    }
292
293
    public function isClosed(): bool
294
    {
295
        return $this->isClosedAt(new DateTime());
296
    }
297
298
    public function currentOpenRange(DateTimeInterface $dateTime)
299
    {
300
        $list = $this->forDateTime($dateTime);
301
302
        return end($list) ?: false;
303
    }
304
305 View Code Duplication
    public function currentOpenRangeStart(DateTimeInterface $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...
306
    {
307
        /** @var TimeRange $range */
308
        $range = $this->currentOpenRange($dateTime);
309
310
        if (! $range) {
311
            return false;
312
        }
313
314
        $dateTime = $this->copyDateTime($dateTime);
315
316
        $nextDateTime = $range->start()->toDateTime();
317
318
        if ($range->overflowsNextDay() && $nextDateTime->format('Hi') > $dateTime->format('Hi')) {
319
            $dateTime = $dateTime->modify('-1 day');
320
        }
321
322
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
323
    }
324
325 View Code Duplication
    public function currentOpenRangeEnd(DateTimeInterface $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...
326
    {
327
        /** @var TimeRange $range */
328
        $range = $this->currentOpenRange($dateTime);
329
330
        if (! $range) {
331
            return false;
332
        }
333
334
        $dateTime = $this->copyDateTime($dateTime);
335
336
        $nextDateTime = $range->end()->toDateTime();
337
338
        if ($range->overflowsNextDay() && $nextDateTime->format('Hi') < $dateTime->format('Hi')) {
339
            $dateTime = $dateTime->modify('+1 day');
340
        }
341
342
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
343
    }
344
345
    public function nextOpen(DateTimeInterface $dateTime): DateTimeInterface
346
    {
347
        $dateTime = $this->copyDateTime($dateTime);
348
        $openingHoursForDay = $this->forDate($dateTime);
349
        $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime));
350
        $tries = $this->getDayLimit();
351
352 View Code Duplication
        while ($nextOpen === false || $nextOpen->hours() >= 24) {
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...
353
            if (--$tries < 0) {
354
                throw MaximumLimitExceeded::forString(
355
                    'No open date/time found in the next '.$this->getDayLimit().' days,'.
356
                    ' use $openingHours->setDayLimit() to increase the limit.'
357
                );
358
            }
359
360
            $dateTime = $dateTime
361
                ->modify('+1 day')
362
                ->setTime(0, 0, 0);
363
364
            if ($this->isOpenAt($dateTime) && ! $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
365
                return $dateTime;
366
            }
367
368
            $openingHoursForDay = $this->forDate($dateTime);
369
370
            $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime));
371
        }
372
373
        if ($dateTime->format('H:i') === '00:00' && $this->isOpenAt((clone $dateTime)->modify('-1 second'))) {
374
            return $this->nextOpen($dateTime->modify('+1 minute'));
375
        }
376
377
        $nextDateTime = $nextOpen->toDateTime();
378
379
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
380
    }
381
382
    public function nextClose(DateTimeInterface $dateTime): DateTimeInterface
383
    {
384
        $dateTime = $this->copyDateTime($dateTime);
385
        $nextClose = null;
386 View Code Duplication
        if ($this->overflow) {
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...
387
            $dateTimeMinus1Day = $this->copyDateTime($dateTime)->modify('-1 day');
388
            $openingHoursForDayBefore = $this->forDate($dateTimeMinus1Day);
389
            if ($openingHoursForDayBefore->isOpenAtNight(Time::fromDateTime($dateTimeMinus1Day))) {
390
                $nextClose = $openingHoursForDayBefore->nextClose(Time::fromDateTime($dateTime));
391
            }
392
        }
393
394
        $openingHoursForDay = $this->forDate($dateTime);
395
        if (! $nextClose) {
396
            $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
397
398
            if ($nextClose && $nextClose->hours() < 24 && $nextClose->format('Gi') < $dateTime->format('Gi')) {
399
                $dateTime = $dateTime->modify('+1 day');
400
            }
401
        }
402
403
        $tries = $this->getDayLimit();
404
405 View Code Duplication
        while ($nextClose === false || $nextClose->hours() >= 24) {
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...
406
            if (--$tries < 0) {
407
                throw MaximumLimitExceeded::forString(
408
                    'No close date/time found in the next '.$this->getDayLimit().' days,'.
409
                    ' use $openingHours->setDayLimit() to increase the limit.'
410
                );
411
            }
412
413
            $dateTime = $dateTime
414
                ->modify('+1 day')
415
                ->setTime(0, 0, 0);
416
417
            if ($this->isClosedAt($dateTime) && $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
418
                return $dateTime;
419
            }
420
421
            $openingHoursForDay = $this->forDate($dateTime);
422
423
            $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
424
        }
425
426
        $nextDateTime = $nextClose->toDateTime();
427
428
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
429
    }
430
431
    public function previousOpen(DateTimeInterface $dateTime): DateTimeInterface
432
    {
433
        $dateTime = $this->copyDateTime($dateTime);
434
        $openingHoursForDay = $this->forDate($dateTime);
435
        $previousOpen = $openingHoursForDay->previousOpen(Time::fromDateTime($dateTime));
436
        $tries = $this->getDayLimit();
437
438 View Code Duplication
        while ($previousOpen === false || ($previousOpen->hours() === 0 && $previousOpen->minutes() === 0)) {
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...
439
            if (--$tries < 0) {
440
                throw MaximumLimitExceeded::forString(
441
                    'No open date/time found in the previous '.$this->getDayLimit().' days,'.
442
                    ' use $openingHours->setDayLimit() to increase the limit.'
443
                );
444
            }
445
446
            $midnight = $dateTime->setTime(0, 0, 0);
447
            $dateTime = clone $midnight;
448
            $dateTime = $dateTime->modify('-1 minute');
449
450
            $openingHoursForDay = $this->forDate($dateTime);
451
452
            if ($this->isOpenAt($midnight) && ! $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
453
                return $midnight;
454
            }
455
456
            $previousOpen = $openingHoursForDay->previousOpen(Time::fromDateTime($dateTime));
457
        }
458
459
        $nextDateTime = $previousOpen->toDateTime();
460
461
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
462
    }
463
464
    public function previousClose(DateTimeInterface $dateTime): DateTimeInterface
465
    {
466
        $dateTime = $this->copyDateTime($dateTime);
467
        $previousClose = null;
468 View Code Duplication
        if ($this->overflow) {
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...
469
            $dateTimeMinus1Day = $this->copyDateTime($dateTime)->modify('-1 day');
470
            $openingHoursForDayBefore = $this->forDate($dateTimeMinus1Day);
471
            if ($openingHoursForDayBefore->isOpenAtNight(Time::fromDateTime($dateTimeMinus1Day))) {
472
                $previousClose = $openingHoursForDayBefore->previousClose(Time::fromDateTime($dateTime));
473
            }
474
        }
475
476
        $openingHoursForDay = $this->forDate($dateTime);
477
        if (! $previousClose) {
478
            $previousClose = $openingHoursForDay->previousClose(Time::fromDateTime($dateTime));
479
        }
480
481
        $tries = $this->getDayLimit();
482
483 View Code Duplication
        while ($previousClose === false || ($previousClose->hours() === 0 && $previousClose->minutes() === 0)) {
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...
484
            if (--$tries < 0) {
485
                throw MaximumLimitExceeded::forString(
486
                    'No close date/time found in the previous '.$this->getDayLimit().' days,'.
487
                    ' use $openingHours->setDayLimit() to increase the limit.'
488
                );
489
            }
490
491
            $midnight = $dateTime->setTime(0, 0, 0);
492
            $dateTime = clone $midnight;
493
            $dateTime = $dateTime->modify('-1 minute');
494
            $openingHoursForDay = $this->forDate($dateTime);
495
496
            if ($this->isClosedAt($midnight) && $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
497
                return $midnight;
498
            }
499
500
            $previousClose = $openingHoursForDay->previousClose(Time::fromDateTime($dateTime));
501
        }
502
503
        $previousDateTime = $previousClose->toDateTime();
504
505
        return $dateTime->setTime($previousDateTime->format('G'), $previousDateTime->format('i'), 0);
506
    }
507
508
    public function regularClosingDays(): array
509
    {
510
        return array_keys($this->filter(function (OpeningHoursForDay $openingHoursForDay) {
511
            return $openingHoursForDay->isEmpty();
512
        }));
513
    }
514
515
    public function regularClosingDaysISO(): array
516
    {
517
        return Arr::map($this->regularClosingDays(), [Day::class, 'toISO']);
518
    }
519
520
    public function exceptionalClosingDates(): array
521
    {
522
        $dates = array_keys($this->filterExceptions(function (OpeningHoursForDay $openingHoursForDay) {
523
            return $openingHoursForDay->isEmpty();
524
        }));
525
526
        return Arr::map($dates, function ($date) {
527
            return DateTime::createFromFormat('Y-m-d', $date);
528
        });
529
    }
530
531
    public function setTimezone($timezone)
532
    {
533
        $this->timezone = new DateTimeZone($timezone);
534
    }
535
536
    protected function parseOpeningHoursAndExceptions(array $data): array
537
    {
538
        $metaData = Arr::pull($data, 'data', null);
539
        $exceptions = [];
540
        $filters = Arr::pull($data, 'filters', []);
541
        $overflow = (bool) Arr::pull($data, 'overflow', false);
542
543
        foreach (Arr::pull($data, 'exceptions', []) as $key => $exception) {
544
            if (is_callable($exception)) {
545
                $filters[] = $exception;
546
547
                continue;
548
            }
549
550
            $exceptions[$key] = $exception;
551
        }
552
553
        $openingHours = [];
554
555
        foreach ($data as $day => $openingHoursData) {
556
            $openingHours[$this->normalizeDayName($day)] = $openingHoursData;
557
        }
558
559
        return [$openingHours, $exceptions, $metaData, $filters, $overflow];
560
    }
561
562
    protected function setOpeningHoursFromStrings(string $day, array $openingHours)
563
    {
564
        $day = $this->normalizeDayName($day);
565
566
        $data = null;
567
568
        if (isset($openingHours['data'])) {
569
            $data = $openingHours['data'];
570
            unset($openingHours['data']);
571
        }
572
573
        $this->openingHours[$day] = OpeningHoursForDay::fromStrings($openingHours)->setData($data);
574
    }
575
576
    protected function setExceptionsFromStrings(array $exceptions)
577
    {
578
        if (empty($exceptions)) {
579
            return;
580
        }
581
582
        if (! $this->dayLimit) {
583
            $this->dayLimit = 366;
584
        }
585
586
        $this->exceptions = Arr::map($exceptions, function (array $openingHours, string $date) {
587
            $recurring = DateTime::createFromFormat('m-d', $date);
588
589
            if ($recurring === false || $recurring->format('m-d') !== $date) {
590
                $dateTime = DateTime::createFromFormat('Y-m-d', $date);
591
592
                if ($dateTime === false || $dateTime->format('Y-m-d') !== $date) {
593
                    throw InvalidDate::invalidDate($date);
594
                }
595
            }
596
597
            return OpeningHoursForDay::fromStrings($openingHours);
598
        });
599
    }
600
601
    protected function normalizeDayName(string $day)
602
    {
603
        $day = strtolower($day);
604
605
        if (! Day::isValid($day)) {
606
            throw InvalidDayName::invalidDayName($day);
607
        }
608
609
        return $day;
610
    }
611
612
    protected function applyTimezone(DateTimeInterface $date)
613
    {
614
        if ($this->timezone) {
615
            $date = $date->setTimezone($this->timezone);
616
        }
617
618
        return $date;
619
    }
620
621
    public function filter(callable $callback): array
622
    {
623
        return Arr::filter($this->openingHours, $callback);
624
    }
625
626
    public function map(callable $callback): array
627
    {
628
        return Arr::map($this->openingHours, $callback);
629
    }
630
631
    public function flatMap(callable $callback): array
632
    {
633
        return Arr::flatMap($this->openingHours, $callback);
634
    }
635
636
    public function filterExceptions(callable $callback): array
637
    {
638
        return Arr::filter($this->exceptions, $callback);
639
    }
640
641
    public function mapExceptions(callable $callback): array
642
    {
643
        return Arr::map($this->exceptions, $callback);
644
    }
645
646
    public function flatMapExceptions(callable $callback): array
647
    {
648
        return Arr::flatMap($this->exceptions, $callback);
649
    }
650
651
    public function asStructuredData(string $format = 'H:i', $timezone = null): array
652
    {
653 View Code Duplication
        $regularHours = $this->flatMap(function (OpeningHoursForDay $openingHoursForDay, string $day) use ($format, $timezone) {
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...
654
            return $openingHoursForDay->map(function (TimeRange $timeRange) use ($format, $timezone, $day) {
655
                return [
656
                    '@type' => 'OpeningHoursSpecification',
657
                    'dayOfWeek' => ucfirst($day),
658
                    'opens' => $timeRange->start()->format($format, $timezone),
659
                    'closes' => $timeRange->end()->format($format, $timezone),
660
                ];
661
            });
662
        });
663
664
        $exceptions = $this->flatMapExceptions(function (OpeningHoursForDay $openingHoursForDay, string $date) use ($format, $timezone) {
665
            if ($openingHoursForDay->isEmpty()) {
666
                $zero = Time::fromString('00:00')->format($format, $timezone);
667
668
                return [[
669
                    '@type' => 'OpeningHoursSpecification',
670
                    'opens' => $zero,
671
                    'closes' => $zero,
672
                    'validFrom' => $date,
673
                    'validThrough' => $date,
674
                ]];
675
            }
676
677 View Code Duplication
            return $openingHoursForDay->map(function (TimeRange $timeRange) use ($format, $date, $timezone) {
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...
678
                return [
679
                    '@type' => 'OpeningHoursSpecification',
680
                    'opens' => $timeRange->start()->format($format, $timezone),
681
                    'closes' => $timeRange->end()->format($format, $timezone),
682
                    'validFrom' => $date,
683
                    'validThrough' => $date,
684
                ];
685
            });
686
        });
687
688
        return array_merge($regularHours, $exceptions);
689
    }
690
}
691