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 ( acd138...bac0ed )
by Kyle
14s queued 11s
created

OpeningHours::forWeekConsecutiveDays()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
cc 4
nc 3
nop 0
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 forWeekConsecutiveDays(): array
215
    {
216
        $concatenatedDays = [];
217
        $allOpeningHours = $this->openingHours;
218
        foreach ($allOpeningHours as $day => $value) {
219
            $previousDay = end($concatenatedDays);
220
            if ($previousDay && (string) $previousDay['opening_hours'] === (string) $value) {
221
                $key = key($concatenatedDays);
222
                $concatenatedDays[$key]['days'][] = $day;
223
                continue;
224
            }
225
226
            $concatenatedDays[$day] = [
227
                'opening_hours' => $value,
228
                'days' => [$day],
229
            ];
230
        }
231
232
        return $concatenatedDays;
233
    }
234
235
    public function forDay(string $day): OpeningHoursForDay
236
    {
237
        $day = $this->normalizeDayName($day);
238
239
        return $this->openingHours[$day];
240
    }
241
242
    public function forDate(DateTimeInterface $date): OpeningHoursForDay
243
    {
244
        $date = $this->applyTimezone($date);
245
246
        foreach ($this->filters as $filter) {
247
            $result = $filter($date);
248
249
            if (is_array($result)) {
250
                return OpeningHoursForDay::fromStrings($result);
251
            }
252
        }
253
254
        return $this->exceptions[$date->format('Y-m-d')] ?? ($this->exceptions[$date->format('m-d')] ?? $this->forDay(Day::onDateTime($date)));
255
    }
256
257
    /**
258
     * @param DateTimeInterface $date
259
     *
260
     * @return TimeRange[]
261
     */
262
    public function forDateTime(DateTimeInterface $date): array
263
    {
264
        return array_merge(
265
            iterator_to_array($this->forDate(
266
                $this->yesterday($date)
267
            )->forNightTime(Time::fromDateTime($date))),
268
            iterator_to_array($this->forDate($date)->forTime(Time::fromDateTime($date)))
269
        );
270
    }
271
272
    public function exceptions(): array
273
    {
274
        return $this->exceptions;
275
    }
276
277
    public function isOpenOn(string $day): bool
278
    {
279
        return count($this->forDay($day)) > 0;
280
    }
281
282
    public function isClosedOn(string $day): bool
283
    {
284
        return ! $this->isOpenOn($day);
285
    }
286
287
    public function isOpenAt(DateTimeInterface $dateTime): bool
288
    {
289
        $dateTime = $this->applyTimezone($dateTime);
290
291
        if ($this->overflow) {
292
            $dateTimeMinus1Day = $this->yesterday($dateTime);
293
            $openingHoursForDayBefore = $this->forDate($dateTimeMinus1Day);
294
            if ($openingHoursForDayBefore->isOpenAtNight(Time::fromDateTime($dateTimeMinus1Day))) {
295
                return true;
296
            }
297
        }
298
299
        $openingHoursForDay = $this->forDate($dateTime);
300
301
        return $openingHoursForDay->isOpenAt(Time::fromDateTime($dateTime));
302
    }
303
304
    public function isClosedAt(DateTimeInterface $dateTime): bool
305
    {
306
        return ! $this->isOpenAt($dateTime);
307
    }
308
309
    public function isOpen(): bool
310
    {
311
        return $this->isOpenAt(new DateTime());
312
    }
313
314
    public function isClosed(): bool
315
    {
316
        return $this->isClosedAt(new DateTime());
317
    }
318
319
    public function currentOpenRange(DateTimeInterface $dateTime)
320
    {
321
        $list = $this->forDateTime($dateTime);
322
323
        return end($list) ?: false;
324
    }
325
326 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...
327
    {
328
        /** @var TimeRange $range */
329
        $range = $this->currentOpenRange($dateTime);
330
331
        if (! $range) {
332
            return false;
333
        }
334
335
        $dateTime = $this->copyDateTime($dateTime);
336
337
        $nextDateTime = $range->start()->toDateTime();
338
339
        if ($range->overflowsNextDay() && $nextDateTime->format('Hi') > $dateTime->format('Hi')) {
340
            $dateTime = $dateTime->modify('-1 day');
341
        }
342
343
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
344
    }
345
346 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...
347
    {
348
        /** @var TimeRange $range */
349
        $range = $this->currentOpenRange($dateTime);
350
351
        if (! $range) {
352
            return false;
353
        }
354
355
        $dateTime = $this->copyDateTime($dateTime);
356
357
        $nextDateTime = $range->end()->toDateTime();
358
359
        if ($range->overflowsNextDay() && $nextDateTime->format('Hi') < $dateTime->format('Hi')) {
360
            $dateTime = $dateTime->modify('+1 day');
361
        }
362
363
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
364
    }
365
366
    public function nextOpen(DateTimeInterface $dateTime): DateTimeInterface
367
    {
368
        $dateTime = $this->copyDateTime($dateTime);
369
        $openingHoursForDay = $this->forDate($dateTime);
370
        $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime));
371
        $tries = $this->getDayLimit();
372
373 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...
374
            if (--$tries < 0) {
375
                throw MaximumLimitExceeded::forString(
376
                    'No open date/time found in the next '.$this->getDayLimit().' days,'.
377
                    ' use $openingHours->setDayLimit() to increase the limit.'
378
                );
379
            }
380
381
            $dateTime = $dateTime
382
                ->modify('+1 day')
383
                ->setTime(0, 0, 0);
384
385
            if ($this->isOpenAt($dateTime) && ! $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
386
                return $dateTime;
387
            }
388
389
            $openingHoursForDay = $this->forDate($dateTime);
390
391
            $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime));
392
        }
393
394
        if ($dateTime->format('H:i') === '00:00' && $this->isOpenAt((clone $dateTime)->modify('-1 second'))) {
395
            return $this->nextOpen($dateTime->modify('+1 minute'));
396
        }
397
398
        $nextDateTime = $nextOpen->toDateTime();
399
400
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
401
    }
402
403
    public function nextClose(DateTimeInterface $dateTime): DateTimeInterface
404
    {
405
        $dateTime = $this->copyDateTime($dateTime);
406
        $nextClose = null;
407 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...
408
            $dateTimeMinus1Day = $this->copyDateTime($dateTime)->modify('-1 day');
409
            $openingHoursForDayBefore = $this->forDate($dateTimeMinus1Day);
410
            if ($openingHoursForDayBefore->isOpenAtNight(Time::fromDateTime($dateTimeMinus1Day))) {
411
                $nextClose = $openingHoursForDayBefore->nextClose(Time::fromDateTime($dateTime));
412
            }
413
        }
414
415
        $openingHoursForDay = $this->forDate($dateTime);
416
        if (! $nextClose) {
417
            $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
418
419
            if ($nextClose && $nextClose->hours() < 24 && $nextClose->format('Gi') < $dateTime->format('Gi')) {
420
                $dateTime = $dateTime->modify('+1 day');
421
            }
422
        }
423
424
        $tries = $this->getDayLimit();
425
426 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...
427
            if (--$tries < 0) {
428
                throw MaximumLimitExceeded::forString(
429
                    'No close date/time found in the next '.$this->getDayLimit().' days,'.
430
                    ' use $openingHours->setDayLimit() to increase the limit.'
431
                );
432
            }
433
434
            $dateTime = $dateTime
435
                ->modify('+1 day')
436
                ->setTime(0, 0, 0);
437
438
            if ($this->isClosedAt($dateTime) && $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
439
                return $dateTime;
440
            }
441
442
            $openingHoursForDay = $this->forDate($dateTime);
443
444
            $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
445
        }
446
447
        $nextDateTime = $nextClose->toDateTime();
448
449
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
450
    }
451
452
    public function previousOpen(DateTimeInterface $dateTime): DateTimeInterface
453
    {
454
        $dateTime = $this->copyDateTime($dateTime);
455
        $openingHoursForDay = $this->forDate($dateTime);
456
        $previousOpen = $openingHoursForDay->previousOpen(Time::fromDateTime($dateTime));
457
        $tries = $this->getDayLimit();
458
459 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...
460
            if (--$tries < 0) {
461
                throw MaximumLimitExceeded::forString(
462
                    'No open date/time found in the previous '.$this->getDayLimit().' days,'.
463
                    ' use $openingHours->setDayLimit() to increase the limit.'
464
                );
465
            }
466
467
            $midnight = $dateTime->setTime(0, 0, 0);
468
            $dateTime = clone $midnight;
469
            $dateTime = $dateTime->modify('-1 minute');
470
471
            $openingHoursForDay = $this->forDate($dateTime);
472
473
            if ($this->isOpenAt($midnight) && ! $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
474
                return $midnight;
475
            }
476
477
            $previousOpen = $openingHoursForDay->previousOpen(Time::fromDateTime($dateTime));
478
        }
479
480
        $nextDateTime = $previousOpen->toDateTime();
481
482
        return $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
483
    }
484
485
    public function previousClose(DateTimeInterface $dateTime): DateTimeInterface
486
    {
487
        $dateTime = $this->copyDateTime($dateTime);
488
        $previousClose = null;
489 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...
490
            $dateTimeMinus1Day = $this->copyDateTime($dateTime)->modify('-1 day');
491
            $openingHoursForDayBefore = $this->forDate($dateTimeMinus1Day);
492
            if ($openingHoursForDayBefore->isOpenAtNight(Time::fromDateTime($dateTimeMinus1Day))) {
493
                $previousClose = $openingHoursForDayBefore->previousClose(Time::fromDateTime($dateTime));
494
            }
495
        }
496
497
        $openingHoursForDay = $this->forDate($dateTime);
498
        if (! $previousClose) {
499
            $previousClose = $openingHoursForDay->previousClose(Time::fromDateTime($dateTime));
500
        }
501
502
        $tries = $this->getDayLimit();
503
504 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...
505
            if (--$tries < 0) {
506
                throw MaximumLimitExceeded::forString(
507
                    'No close date/time found in the previous '.$this->getDayLimit().' days,'.
508
                    ' use $openingHours->setDayLimit() to increase the limit.'
509
                );
510
            }
511
512
            $midnight = $dateTime->setTime(0, 0, 0);
513
            $dateTime = clone $midnight;
514
            $dateTime = $dateTime->modify('-1 minute');
515
            $openingHoursForDay = $this->forDate($dateTime);
516
517
            if ($this->isClosedAt($midnight) && $openingHoursForDay->isOpenAt(Time::fromString('23:59'))) {
518
                return $midnight;
519
            }
520
521
            $previousClose = $openingHoursForDay->previousClose(Time::fromDateTime($dateTime));
522
        }
523
524
        $previousDateTime = $previousClose->toDateTime();
525
526
        return $dateTime->setTime($previousDateTime->format('G'), $previousDateTime->format('i'), 0);
527
    }
528
529
    public function regularClosingDays(): array
530
    {
531
        return array_keys($this->filter(function (OpeningHoursForDay $openingHoursForDay) {
532
            return $openingHoursForDay->isEmpty();
533
        }));
534
    }
535
536
    public function regularClosingDaysISO(): array
537
    {
538
        return Arr::map($this->regularClosingDays(), [Day::class, 'toISO']);
539
    }
540
541
    public function exceptionalClosingDates(): array
542
    {
543
        $dates = array_keys($this->filterExceptions(function (OpeningHoursForDay $openingHoursForDay) {
544
            return $openingHoursForDay->isEmpty();
545
        }));
546
547
        return Arr::map($dates, function ($date) {
548
            return DateTime::createFromFormat('Y-m-d', $date);
549
        });
550
    }
551
552
    public function setTimezone($timezone)
553
    {
554
        $this->timezone = new DateTimeZone($timezone);
555
    }
556
557
    protected function parseOpeningHoursAndExceptions(array $data): array
558
    {
559
        $metaData = Arr::pull($data, 'data', null);
560
        $exceptions = [];
561
        $filters = Arr::pull($data, 'filters', []);
562
        $overflow = (bool) Arr::pull($data, 'overflow', false);
563
564
        foreach (Arr::pull($data, 'exceptions', []) as $key => $exception) {
565
            if (is_callable($exception)) {
566
                $filters[] = $exception;
567
568
                continue;
569
            }
570
571
            $exceptions[$key] = $exception;
572
        }
573
574
        $openingHours = [];
575
576
        foreach ($data as $day => $openingHoursData) {
577
            $openingHours[$this->normalizeDayName($day)] = $openingHoursData;
578
        }
579
580
        return [$openingHours, $exceptions, $metaData, $filters, $overflow];
581
    }
582
583
    protected function setOpeningHoursFromStrings(string $day, array $openingHours)
584
    {
585
        $day = $this->normalizeDayName($day);
586
587
        $data = null;
588
589
        if (isset($openingHours['data'])) {
590
            $data = $openingHours['data'];
591
            unset($openingHours['data']);
592
        }
593
594
        $this->openingHours[$day] = OpeningHoursForDay::fromStrings($openingHours)->setData($data);
595
    }
596
597
    protected function setExceptionsFromStrings(array $exceptions)
598
    {
599
        if (empty($exceptions)) {
600
            return;
601
        }
602
603
        if (! $this->dayLimit) {
604
            $this->dayLimit = 366;
605
        }
606
607
        $this->exceptions = Arr::map($exceptions, function (array $openingHours, string $date) {
608
            $recurring = DateTime::createFromFormat('m-d', $date);
609
610
            if ($recurring === false || $recurring->format('m-d') !== $date) {
611
                $dateTime = DateTime::createFromFormat('Y-m-d', $date);
612
613
                if ($dateTime === false || $dateTime->format('Y-m-d') !== $date) {
614
                    throw InvalidDate::invalidDate($date);
615
                }
616
            }
617
618
            return OpeningHoursForDay::fromStrings($openingHours);
619
        });
620
    }
621
622
    protected function normalizeDayName(string $day)
623
    {
624
        $day = strtolower($day);
625
626
        if (! Day::isValid($day)) {
627
            throw InvalidDayName::invalidDayName($day);
628
        }
629
630
        return $day;
631
    }
632
633
    protected function applyTimezone(DateTimeInterface $date)
634
    {
635
        if ($this->timezone) {
636
            $date = $date->setTimezone($this->timezone);
637
        }
638
639
        return $date;
640
    }
641
642
    public function filter(callable $callback): array
643
    {
644
        return Arr::filter($this->openingHours, $callback);
645
    }
646
647
    public function map(callable $callback): array
648
    {
649
        return Arr::map($this->openingHours, $callback);
650
    }
651
652
    public function flatMap(callable $callback): array
653
    {
654
        return Arr::flatMap($this->openingHours, $callback);
655
    }
656
657
    public function filterExceptions(callable $callback): array
658
    {
659
        return Arr::filter($this->exceptions, $callback);
660
    }
661
662
    public function mapExceptions(callable $callback): array
663
    {
664
        return Arr::map($this->exceptions, $callback);
665
    }
666
667
    public function flatMapExceptions(callable $callback): array
668
    {
669
        return Arr::flatMap($this->exceptions, $callback);
670
    }
671
672
    public function asStructuredData(string $format = 'H:i', $timezone = null): array
673
    {
674 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...
675
            return $openingHoursForDay->map(function (TimeRange $timeRange) use ($format, $timezone, $day) {
676
                return [
677
                    '@type' => 'OpeningHoursSpecification',
678
                    'dayOfWeek' => ucfirst($day),
679
                    'opens' => $timeRange->start()->format($format, $timezone),
680
                    'closes' => $timeRange->end()->format($format, $timezone),
681
                ];
682
            });
683
        });
684
685
        $exceptions = $this->flatMapExceptions(function (OpeningHoursForDay $openingHoursForDay, string $date) use ($format, $timezone) {
686
            if ($openingHoursForDay->isEmpty()) {
687
                $zero = Time::fromString('00:00')->format($format, $timezone);
688
689
                return [[
690
                    '@type' => 'OpeningHoursSpecification',
691
                    'opens' => $zero,
692
                    'closes' => $zero,
693
                    'validFrom' => $date,
694
                    'validThrough' => $date,
695
                ]];
696
            }
697
698 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...
699
                return [
700
                    '@type' => 'OpeningHoursSpecification',
701
                    'opens' => $timeRange->start()->format($format, $timezone),
702
                    'closes' => $timeRange->end()->format($format, $timezone),
703
                    'validFrom' => $date,
704
                    'validThrough' => $date,
705
                ];
706
            });
707
        });
708
709
        return array_merge($regularHours, $exceptions);
710
    }
711
}
712