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 (#132)
by Kyle
01:47
created

OpeningHours::previousClose()   B

Complexity

Conditions 10
Paths 24

Size

Total Lines 44

Duplication

Lines 27
Ratio 61.36 %

Importance

Changes 0
Metric Value
dl 27
loc 44
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
        $yesterday = $this->copyDateTime($date)->modify('-1 day');
244
245
        return array_merge(
246
            iterator_to_array($this->forDate($yesterday)->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->copyDateTime($dateTime)->modify('-1 day');
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
            $yesterday = $this->copyDateTime($dateTime);
388
            $dateTimeMinus1Day = $yesterday->sub(new \DateInterval('P1D'));
389
            $openingHoursForDayBefore = $this->forDate($dateTimeMinus1Day);
390
            if ($openingHoursForDayBefore->isOpenAtNight(Time::fromDateTime($dateTimeMinus1Day))) {
391
                $nextClose = $openingHoursForDayBefore->nextClose(Time::fromDateTime($dateTime));
392
            }
393
        }
394
395
        $openingHoursForDay = $this->forDate($dateTime);
396
        if (! $nextClose) {
397
            $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
398
399
            if ($nextClose && $nextClose->hours() < 24 && $nextClose->format('Gi') < $dateTime->format('Gi')) {
400
                $dateTime = $dateTime->modify('+1 day');
401
            }
402
        }
403
404
        $tries = $this->getDayLimit();
405
406 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...
407
            if (--$tries < 0) {
408
                throw MaximumLimitExceeded::forString(
409
                    'No close date/time found in the next '.$this->getDayLimit().' days,'.
410
                    ' use $openingHours->setDayLimit() to increase the limit.'
411
                );
412
            }
413
414
            $dateTime = $dateTime
415
                ->modify('+1 day')
416
                ->setTime(0, 0, 0);
417
418
            if ($this->isClosedAt($dateTime) && $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
419
                return $dateTime;
420
            }
421
422
            $openingHoursForDay = $this->forDate($dateTime);
423
424
            $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
425
        }
426
427
        $nextDateTime = $nextClose->toDateTime();
428
429
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
430
    }
431
432
    public function previousOpen(DateTimeInterface $dateTime): DateTimeInterface
433
    {
434
        $dateTime = $this->copyDateTime($dateTime);
435
        $openingHoursForDay = $this->forDate($dateTime);
436
        $previousOpen = $openingHoursForDay->previousOpen(Time::fromDateTime($dateTime));
437
        $tries = $this->getDayLimit();
438
439 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...
440
            if (--$tries < 0) {
441
                throw MaximumLimitExceeded::forString(
442
                    'No open date/time found in the previous '.$this->getDayLimit().' days,'.
443
                    ' use $openingHours->setDayLimit() to increase the limit.'
444
                );
445
            }
446
447
            $midnight = $dateTime->setTime(0, 0, 0);
448
            $dateTime = clone $midnight;
449
            $dateTime = $dateTime->modify('-1 minute');
450
451
            $openingHoursForDay = $this->forDate($dateTime);
452
453
            if ($this->isOpenAt($midnight) && ! $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
454
                return $midnight;
455
            }
456
457
            $previousOpen = $openingHoursForDay->previousOpen(Time::fromDateTime($dateTime));
458
        }
459
460
        $nextDateTime = $previousOpen->toDateTime();
461
462
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
463
    }
464
465
    public function previousClose(DateTimeInterface $dateTime): DateTimeInterface
466
    {
467
        $dateTime = $this->copyDateTime($dateTime);
468
        $previousClose = null;
469 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...
470
            $yesterday = $this->copyDateTime($dateTime);
471
            $dateTimeMinus1Day = $yesterday->sub(new \DateInterval('P1D'));
472
            $openingHoursForDayBefore = $this->forDate($dateTimeMinus1Day);
473
            if ($openingHoursForDayBefore->isOpenAtNight(Time::fromDateTime($dateTimeMinus1Day))) {
474
                $previousClose = $openingHoursForDayBefore->previousClose(Time::fromDateTime($dateTime));
475
            }
476
        }
477
478
        $openingHoursForDay = $this->forDate($dateTime);
479
        if (! $previousClose) {
480
            $previousClose = $openingHoursForDay->previousClose(Time::fromDateTime($dateTime));
481
        }
482
483
        $tries = $this->getDayLimit();
484
485 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...
486
            if (--$tries < 0) {
487
                throw MaximumLimitExceeded::forString(
488
                    'No close date/time found in the previous '.$this->getDayLimit().' days,'.
489
                    ' use $openingHours->setDayLimit() to increase the limit.'
490
                );
491
            }
492
493
            $midnight = $dateTime->setTime(0, 0, 0);
494
            $dateTime = clone $midnight;
495
            $dateTime = $dateTime->modify('-1 minute');
496
            $openingHoursForDay = $this->forDate($dateTime);
497
498
            if ($this->isClosedAt($midnight) && $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
499
                return $midnight;
500
            }
501
502
            $previousClose = $openingHoursForDay->previousClose(Time::fromDateTime($dateTime));
503
        }
504
505
        $previousDateTime = $previousClose->toDateTime();
506
507
        return $dateTime->setTime($previousDateTime->format('G'), $previousDateTime->format('i'), 0);
508
    }
509
510
    public function regularClosingDays(): array
511
    {
512
        return array_keys($this->filter(function (OpeningHoursForDay $openingHoursForDay) {
513
            return $openingHoursForDay->isEmpty();
514
        }));
515
    }
516
517
    public function regularClosingDaysISO(): array
518
    {
519
        return Arr::map($this->regularClosingDays(), [Day::class, 'toISO']);
520
    }
521
522
    public function exceptionalClosingDates(): array
523
    {
524
        $dates = array_keys($this->filterExceptions(function (OpeningHoursForDay $openingHoursForDay) {
525
            return $openingHoursForDay->isEmpty();
526
        }));
527
528
        return Arr::map($dates, function ($date) {
529
            return DateTime::createFromFormat('Y-m-d', $date);
530
        });
531
    }
532
533
    public function setTimezone($timezone)
534
    {
535
        $this->timezone = new DateTimeZone($timezone);
536
    }
537
538
    protected function parseOpeningHoursAndExceptions(array $data): array
539
    {
540
        $metaData = Arr::pull($data, 'data', null);
541
        $exceptions = [];
542
        $filters = Arr::pull($data, 'filters', []);
543
        $overflow = (bool) Arr::pull($data, 'overflow', false);
544
545
        foreach (Arr::pull($data, 'exceptions', []) as $key => $exception) {
546
            if (is_callable($exception)) {
547
                $filters[] = $exception;
548
549
                continue;
550
            }
551
552
            $exceptions[$key] = $exception;
553
        }
554
555
        $openingHours = [];
556
557
        foreach ($data as $day => $openingHoursData) {
558
            $openingHours[$this->normalizeDayName($day)] = $openingHoursData;
559
        }
560
561
        return [$openingHours, $exceptions, $metaData, $filters, $overflow];
562
    }
563
564
    protected function setOpeningHoursFromStrings(string $day, array $openingHours)
565
    {
566
        $day = $this->normalizeDayName($day);
567
568
        $data = null;
569
570
        if (isset($openingHours['data'])) {
571
            $data = $openingHours['data'];
572
            unset($openingHours['data']);
573
        }
574
575
        $this->openingHours[$day] = OpeningHoursForDay::fromStrings($openingHours)->setData($data);
576
    }
577
578
    protected function setExceptionsFromStrings(array $exceptions)
579
    {
580
        if (empty($exceptions)) {
581
            return;
582
        }
583
584
        if (! $this->dayLimit) {
585
            $this->dayLimit = 366;
586
        }
587
588
        $this->exceptions = Arr::map($exceptions, function (array $openingHours, string $date) {
589
            $recurring = DateTime::createFromFormat('m-d', $date);
590
591
            if ($recurring === false || $recurring->format('m-d') !== $date) {
592
                $dateTime = DateTime::createFromFormat('Y-m-d', $date);
593
594
                if ($dateTime === false || $dateTime->format('Y-m-d') !== $date) {
595
                    throw InvalidDate::invalidDate($date);
596
                }
597
            }
598
599
            return OpeningHoursForDay::fromStrings($openingHours);
600
        });
601
    }
602
603
    protected function normalizeDayName(string $day)
604
    {
605
        $day = strtolower($day);
606
607
        if (! Day::isValid($day)) {
608
            throw InvalidDayName::invalidDayName($day);
609
        }
610
611
        return $day;
612
    }
613
614
    protected function applyTimezone(DateTimeInterface $date)
615
    {
616
        if ($this->timezone) {
617
            $date = $date->setTimezone($this->timezone);
618
        }
619
620
        return $date;
621
    }
622
623
    public function filter(callable $callback): array
624
    {
625
        return Arr::filter($this->openingHours, $callback);
626
    }
627
628
    public function map(callable $callback): array
629
    {
630
        return Arr::map($this->openingHours, $callback);
631
    }
632
633
    public function flatMap(callable $callback): array
634
    {
635
        return Arr::flatMap($this->openingHours, $callback);
636
    }
637
638
    public function filterExceptions(callable $callback): array
639
    {
640
        return Arr::filter($this->exceptions, $callback);
641
    }
642
643
    public function mapExceptions(callable $callback): array
644
    {
645
        return Arr::map($this->exceptions, $callback);
646
    }
647
648
    public function flatMapExceptions(callable $callback): array
649
    {
650
        return Arr::flatMap($this->exceptions, $callback);
651
    }
652
653
    public function asStructuredData(string $format = 'H:i', $timezone = null): array
654
    {
655 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...
656
            return $openingHoursForDay->map(function (TimeRange $timeRange) use ($format, $timezone, $day) {
657
                return [
658
                    '@type' => 'OpeningHoursSpecification',
659
                    'dayOfWeek' => ucfirst($day),
660
                    'opens' => $timeRange->start()->format($format, $timezone),
661
                    'closes' => $timeRange->end()->format($format, $timezone),
662
                ];
663
            });
664
        });
665
666
        $exceptions = $this->flatMapExceptions(function (OpeningHoursForDay $openingHoursForDay, string $date) use ($format, $timezone) {
667
            if ($openingHoursForDay->isEmpty()) {
668
                $zero = Time::fromString('00:00')->format($format, $timezone);
669
670
                return [[
671
                    '@type' => 'OpeningHoursSpecification',
672
                    'opens' => $zero,
673
                    'closes' => $zero,
674
                    'validFrom' => $date,
675
                    'validThrough' => $date,
676
                ]];
677
            }
678
679 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...
680
                return [
681
                    '@type' => 'OpeningHoursSpecification',
682
                    'opens' => $timeRange->start()->format($format, $timezone),
683
                    'closes' => $timeRange->end()->format($format, $timezone),
684
                    'validFrom' => $date,
685
                    'validThrough' => $date,
686
                ];
687
            });
688
        });
689
690
        return array_merge($regularHours, $exceptions);
691
    }
692
}
693