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 (#133)
by Bram
01:29 queued 17s
created

OpeningHours::concatnatedDays()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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