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 ( f6c114...c14204 )
by Kyle
13s queued 11s
created

OpeningHours::getFilters()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Spatie\OpeningHours;
4
5
use DateTime;
6
use DateTimeZone;
7
use DateTimeImmutable;
8
use DateTimeInterface;
9
use Spatie\OpeningHours\Helpers\Arr;
10
use Spatie\OpeningHours\Helpers\DataTrait;
11
use Spatie\OpeningHours\Exceptions\Exception;
12
use Spatie\OpeningHours\Exceptions\InvalidDate;
13
use Spatie\OpeningHours\Exceptions\InvalidDayName;
14
15
class OpeningHours
16
{
17
    use DataTrait;
18
19
    /** @var \Spatie\OpeningHours\Day[] */
20
    protected $openingHours = [];
21
22
    /** @var \Spatie\OpeningHours\OpeningHoursForDay[] */
23
    protected $exceptions = [];
24
25
    /** @var callable[] */
26
    protected $filters = [];
27
28
    /** @var DateTimeZone|null */
29
    protected $timezone = null;
30
31
    public function __construct($timezone = null)
32
    {
33
        $this->timezone = $timezone ? new DateTimeZone($timezone) : null;
34
35
        $this->openingHours = Day::mapDays(function () {
36
            return new OpeningHoursForDay();
37
        });
38
    }
39
40
    /**
41
     * @param array $data
42
     *
43
     * @return static
44
     */
45
    public static function create(array $data)
46
    {
47
        return (new static())->fill($data);
48
    }
49
50
    /**
51
     * @param array $data
52
     *
53
     * @return array
54
     */
55
    public static function mergeOverlappingRanges(array $data)
56
    {
57
        $result = [];
58
        $ranges = [];
59
        foreach ($data as $key => $value) {
60
            $value = is_array($value)
61
                ? static::mergeOverlappingRanges($value)
62
                : (is_string($value) ? TimeRange::fromString($value) : $value);
63
64
            if ($value instanceof TimeRange) {
65
                $newRanges = [];
66
                foreach ($ranges as $range) {
67
                    if ($value->format() === $range->format()) {
68
                        continue 2;
69
                    }
70
71
                    if ($value->overlaps($range) || $range->overlaps($value)) {
72
                        $value = TimeRange::fromList([$value, $range]);
73
74
                        continue;
75
                    }
76
77
                    $newRanges[] = $range;
78
                }
79
80
                $newRanges[] = $value;
81
                $ranges = $newRanges;
82
83
                continue;
84
            }
85
86
            $result[$key] = $value;
87
        }
88
89
        foreach ($ranges as $range) {
90
            $result[] = $range;
91
        }
92
93
        return $result;
94
    }
95
96
    /**
97
     * @param array $data
98
     *
99
     * @return static
100
     */
101
    public static function createAndMergeOverlappingRanges(array $data)
102
    {
103
        return static::create(static::mergeOverlappingRanges($data));
104
    }
105
106
    /**
107
     * @param array $data
108
     *
109
     * @return bool
110
     */
111
    public static function isValid(array $data): bool
112
    {
113
        try {
114
            static::create($data);
115
116
            return true;
117
        } catch (Exception $exception) {
118
            return false;
119
        }
120
    }
121
122
    public function setFilters(array $filters)
123
    {
124
        $this->filters = $filters;
125
126
        return $this;
127
    }
128
129
    public function getFilters(): array
130
    {
131
        return $this->filters;
132
    }
133
134
    public function fill(array $data)
135
    {
136
        list($openingHours, $exceptions, $metaData, $filters) = $this->parseOpeningHoursAndExceptions($data);
137
138
        foreach ($openingHours as $day => $openingHoursForThisDay) {
139
            $this->setOpeningHoursFromStrings($day, $openingHoursForThisDay);
140
        }
141
142
        $this->setExceptionsFromStrings($exceptions);
143
144
        return $this->setFilters($filters)->setData($metaData);
145
    }
146
147
    public function forWeek(): array
148
    {
149
        return $this->openingHours;
150
    }
151
152
    public function forWeekCombined(): array
153
    {
154
        $equalDays = [];
155
        $allOpeningHours = $this->openingHours;
156
        $uniqueOpeningHours = array_unique($allOpeningHours);
157
        $nonUniqueOpeningHours = $allOpeningHours;
158
159
        foreach ($uniqueOpeningHours as $day => $value) {
160
            $equalDays[$day] = ['days' => [$day], 'opening_hours' => $value];
161
            unset($nonUniqueOpeningHours[$day]);
162
        }
163
164
        foreach ($uniqueOpeningHours as $uniqueDay => $uniqueValue) {
165
            foreach ($nonUniqueOpeningHours as $nonUniqueDay => $nonUniqueValue) {
166
                if ((string) $uniqueValue === (string) $nonUniqueValue) {
167
                    $equalDays[$uniqueDay]['days'][] = $nonUniqueDay;
168
                }
169
            }
170
        }
171
172
        return $equalDays;
173
    }
174
175
    public function forDay(string $day): OpeningHoursForDay
176
    {
177
        $day = $this->normalizeDayName($day);
178
179
        return $this->openingHours[$day];
180
    }
181
182
    public function forDate(DateTimeInterface $date): OpeningHoursForDay
183
    {
184
        $date = $this->applyTimezone($date);
185
186
        foreach ($this->filters as $filter) {
187
            $result = $filter($date);
188
189
            if (is_array($result)) {
190
                return OpeningHoursForDay::fromStrings($result);
191
            }
192
        }
193
194
        return $this->exceptions[$date->format('Y-m-d')] ?? ($this->exceptions[$date->format('m-d')] ?? $this->forDay(Day::onDateTime($date)));
195
    }
196
197
    public function exceptions(): array
198
    {
199
        return $this->exceptions;
200
    }
201
202
    public function isOpenOn(string $day): bool
203
    {
204
        return count($this->forDay($day)) > 0;
205
    }
206
207
    public function isClosedOn(string $day): bool
208
    {
209
        return ! $this->isOpenOn($day);
210
    }
211
212
    public function isOpenAt(DateTimeInterface $dateTime): bool
213
    {
214
        $dateTime = $this->applyTimezone($dateTime);
215
216
        $openingHoursForDay = $this->forDate($dateTime);
217
218
        return $openingHoursForDay->isOpenAt(Time::fromDateTime($dateTime));
219
    }
220
221
    public function isClosedAt(DateTimeInterface $dateTime): bool
222
    {
223
        return ! $this->isOpenAt($dateTime);
224
    }
225
226
    public function isOpen(): bool
227
    {
228
        return $this->isOpenAt(new DateTime());
229
    }
230
231
    public function isClosed(): bool
232
    {
233
        return $this->isClosedAt(new DateTime());
234
    }
235
236 View Code Duplication
    public function nextOpen(DateTimeInterface $dateTime): DateTimeInterface
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...
237
    {
238
        if (! ($dateTime instanceof DateTimeImmutable)) {
239
            $dateTime = clone $dateTime;
240
        }
241
242
        $openingHoursForDay = $this->forDate($dateTime);
243
        $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime));
244
245
        while ($nextOpen === false) {
246
            $dateTime = $dateTime
247
                ->modify('+1 day')
248
                ->setTime(0, 0, 0);
249
250
            $openingHoursForDay = $this->forDate($dateTime);
251
252
            $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime));
253
        }
254
255
        $nextDateTime = $nextOpen->toDateTime();
256
        $dateTime = $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
257
258
        return $dateTime;
259
    }
260
261 View Code Duplication
    public function nextClose(DateTimeInterface $dateTime): DateTimeInterface
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...
262
    {
263
        if (! ($dateTime instanceof DateTimeImmutable)) {
264
            $dateTime = clone $dateTime;
265
        }
266
267
        $openingHoursForDay = $this->forDate($dateTime);
268
        $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
269
270
        while ($nextClose === false) {
271
            $dateTime = $dateTime
272
                ->modify('+1 day')
273
                ->setTime(0, 0, 0);
274
275
            $openingHoursForDay = $this->forDate($dateTime);
276
277
            $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
278
        }
279
280
        $nextDateTime = $nextClose->toDateTime();
281
        $dateTime = $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
282
283
        return $dateTime;
284
    }
285
286
    public function regularClosingDays(): array
287
    {
288
        return array_keys($this->filter(function (OpeningHoursForDay $openingHoursForDay) {
289
            return $openingHoursForDay->isEmpty();
290
        }));
291
    }
292
293
    public function regularClosingDaysISO(): array
294
    {
295
        return Arr::map($this->regularClosingDays(), [Day::class, 'toISO']);
296
    }
297
298
    public function exceptionalClosingDates(): array
299
    {
300
        $dates = array_keys($this->filterExceptions(function (OpeningHoursForDay $openingHoursForDay, $date) {
0 ignored issues
show
Unused Code introduced by
The parameter $date is not used and could be removed.

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

Loading history...
301
            return $openingHoursForDay->isEmpty();
302
        }));
303
304
        return Arr::map($dates, function ($date) {
305
            return DateTime::createFromFormat('Y-m-d', $date);
306
        });
307
    }
308
309
    public function setTimezone($timezone)
310
    {
311
        $this->timezone = new DateTimeZone($timezone);
312
    }
313
314
    protected function parseOpeningHoursAndExceptions(array $data): array
315
    {
316
        $metaData = Arr::pull($data, 'data', null);
317
        $exceptions = [];
318
        $filters = Arr::pull($data, 'filters', []);
319
        foreach (Arr::pull($data, 'exceptions', []) as $key => $exception) {
320
            if (is_callable($exception)) {
321
                $filters[] = $exception;
322
323
                continue;
324
            }
325
326
            $exceptions[$key] = $exception;
327
        }
328
        $openingHours = [];
329
330
        foreach ($data as $day => $openingHoursData) {
331
            $openingHours[$this->normalizeDayName($day)] = $openingHoursData;
332
        }
333
334
        return [$openingHours, $exceptions, $metaData, $filters];
335
    }
336
337
    protected function setOpeningHoursFromStrings(string $day, array $openingHours)
338
    {
339
        $day = $this->normalizeDayName($day);
340
341
        $data = null;
342
343
        if (isset($openingHours['data'])) {
344
            $data = $openingHours['data'];
345
            unset($openingHours['data']);
346
        }
347
348
        $this->openingHours[$day] = OpeningHoursForDay::fromStrings($openingHours)->setData($data);
349
    }
350
351
    protected function setExceptionsFromStrings(array $exceptions)
352
    {
353
        $this->exceptions = Arr::map($exceptions, function (array $openingHours, string $date) {
354
            $recurring = DateTime::createFromFormat('m-d', $date);
355
356
            if ($recurring === false || $recurring->format('m-d') !== $date) {
357
                $dateTime = DateTime::createFromFormat('Y-m-d', $date);
358
359
                if ($dateTime === false || $dateTime->format('Y-m-d') !== $date) {
360
                    throw InvalidDate::invalidDate($date);
361
                }
362
            }
363
364
            return OpeningHoursForDay::fromStrings($openingHours);
365
        });
366
    }
367
368
    protected function normalizeDayName(string $day)
369
    {
370
        $day = strtolower($day);
371
372
        if (! Day::isValid($day)) {
373
            throw InvalidDayName::invalidDayName($day);
374
        }
375
376
        return $day;
377
    }
378
379
    protected function applyTimezone(DateTimeInterface $date)
380
    {
381
        if ($this->timezone) {
382
            $date = $date->setTimezone($this->timezone);
383
        }
384
385
        return $date;
386
    }
387
388
    public function filter(callable $callback): array
389
    {
390
        return Arr::filter($this->openingHours, $callback);
391
    }
392
393
    public function map(callable $callback): array
394
    {
395
        return Arr::map($this->openingHours, $callback);
396
    }
397
398
    public function flatMap(callable $callback): array
399
    {
400
        return Arr::flatMap($this->openingHours, $callback);
401
    }
402
403
    public function filterExceptions(callable $callback): array
404
    {
405
        return Arr::filter($this->exceptions, $callback);
406
    }
407
408
    public function mapExceptions(callable $callback): array
409
    {
410
        return Arr::map($this->exceptions, $callback);
411
    }
412
413
    public function flatMapExceptions(callable $callback): array
414
    {
415
        return Arr::flatMap($this->exceptions, $callback);
416
    }
417
418
    public function asStructuredData(): array
419
    {
420 View Code Duplication
        $regularHours = $this->flatMap(function (OpeningHoursForDay $openingHoursForDay, string $day) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
421
            return $openingHoursForDay->map(function (TimeRange $timeRange) use ($day) {
422
                return [
423
                    '@type' => 'OpeningHoursSpecification',
424
                    'dayOfWeek' => ucfirst($day),
425
                    'opens' => (string) $timeRange->start(),
426
                    'closes' => (string) $timeRange->end(),
427
                ];
428
            });
429
        });
430
431
        $exceptions = $this->flatMapExceptions(function (OpeningHoursForDay $openingHoursForDay, string $date) {
432
            if ($openingHoursForDay->isEmpty()) {
433
                return [[
434
                    '@type' => 'OpeningHoursSpecification',
435
                    'opens' => '00:00',
436
                    'closes' => '00:00',
437
                    'validFrom' => $date,
438
                    'validThrough' => $date,
439
                ]];
440
            }
441
442 View Code Duplication
            return $openingHoursForDay->map(function (TimeRange $timeRange) use ($date) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
443
                return [
444
                    '@type' => 'OpeningHoursSpecification',
445
                    'opens' => (string) $timeRange->start(),
446
                    'closes' => (string) $timeRange->end(),
447
                    'validFrom' => $date,
448
                    'validThrough' => $date,
449
                ];
450
            });
451
        });
452
453
        return array_merge($regularHours, $exceptions);
454
    }
455
}
456