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 ( 5f72cb...f6c114 )
by Kyle
12s queued 11s
created

OpeningHours::createAndMergeOverlappingRanges()   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 1
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\Exceptions\Exception;
11
use Spatie\OpeningHours\Exceptions\InvalidDate;
12
use Spatie\OpeningHours\Exceptions\InvalidDayName;
13
14
class OpeningHours
15
{
16
    /** @var \Spatie\OpeningHours\Day[] */
17
    protected $openingHours = [];
18
19
    /** @var array */
20
    protected $exceptions = [];
21
22
    /** @var DateTimeZone|null */
23
    protected $timezone = null;
24
25
    public function __construct($timezone = null)
26
    {
27
        $this->timezone = $timezone ? new DateTimeZone($timezone) : null;
28
29
        $this->openingHours = Day::mapDays(function () {
30
            return new OpeningHoursForDay();
31
        });
32
    }
33
34
    /**
35
     * @param array $data
36
     *
37
     * @return static
38
     */
39
    public static function create(array $data)
40
    {
41
        return (new static())->fill($data);
42
    }
43
44
    /**
45
     * @param array $data
46
     *
47
     * @return array
48
     */
49
    public static function mergeOverlappingRanges(array $data)
50
    {
51
        $result = [];
52
        $ranges = [];
53
        foreach ($data as $key => $value) {
54
            $value = is_array($value)
55
                ? static::mergeOverlappingRanges($value)
56
                : (is_string($value) ? TimeRange::fromString($value) : $value);
57
58
            if ($value instanceof TimeRange) {
59
                $newRanges = [];
60
                foreach ($ranges as $range) {
61
                    if ($value->format() === $range->format()) {
62
                        continue 2;
63
                    }
64
65
                    if ($value->overlaps($range) || $range->overlaps($value)) {
66
                        $value = TimeRange::fromList([$value, $range]);
67
68
                        continue;
69
                    }
70
71
                    $newRanges[] = $range;
72
                }
73
74
                $newRanges[] = $value;
75
                $ranges = $newRanges;
76
77
                continue;
78
            }
79
80
            $result[$key] = $value;
81
        }
82
83
        foreach ($ranges as $range) {
84
            $result[] = $range;
85
        }
86
87
        return $result;
88
    }
89
90
    /**
91
     * @param array $data
92
     *
93
     * @return static
94
     */
95
    public static function createAndMergeOverlappingRanges(array $data)
96
    {
97
        return static::create(static::mergeOverlappingRanges($data));
98
    }
99
100
    /**
101
     * @param array $data
102
     *
103
     * @return bool
104
     */
105
    public static function isValid(array $data): bool
106
    {
107
        try {
108
            static::create($data);
109
110
            return true;
111
        } catch (Exception $exception) {
112
            return false;
113
        }
114
    }
115
116
    public function fill(array $data)
117
    {
118
        list($openingHours, $exceptions) = $this->parseOpeningHoursAndExceptions($data);
119
120
        foreach ($openingHours as $day => $openingHoursForThisDay) {
121
            $this->setOpeningHoursFromStrings($day, $openingHoursForThisDay);
122
        }
123
124
        $this->setExceptionsFromStrings($exceptions);
125
126
        return $this;
127
    }
128
129
    public function forWeek(): array
130
    {
131
        return $this->openingHours;
132
    }
133
134
    public function forWeekCombined(): array
135
    {
136
        $equalDays = [];
137
        $allOpeningHours = $this->openingHours;
138
        $uniqueOpeningHours = array_unique($allOpeningHours);
139
        $nonUniqueOpeningHours = $allOpeningHours;
140
141
        foreach ($uniqueOpeningHours as $day => $value) {
142
            $equalDays[$day] = ['days' => [$day], 'opening_hours' => $value];
143
            unset($nonUniqueOpeningHours[$day]);
144
        }
145
146
        foreach ($uniqueOpeningHours as $uniqueDay => $uniqueValue) {
147
            foreach ($nonUniqueOpeningHours as $nonUniqueDay => $nonUniqueValue) {
148
                if ((string) $uniqueValue === (string) $nonUniqueValue) {
149
                    $equalDays[$uniqueDay]['days'][] = $nonUniqueDay;
150
                }
151
            }
152
        }
153
154
        return $equalDays;
155
    }
156
157
    public function forDay(string $day): OpeningHoursForDay
158
    {
159
        $day = $this->normalizeDayName($day);
160
161
        return $this->openingHours[$day];
162
    }
163
164
    public function forDate(DateTimeInterface $date): OpeningHoursForDay
165
    {
166
        $date = $this->applyTimezone($date);
167
168
        return $this->exceptions[$date->format('Y-m-d')] ?? ($this->exceptions[$date->format('m-d')] ?? $this->forDay(Day::onDateTime($date)));
169
    }
170
171
    public function exceptions(): array
172
    {
173
        return $this->exceptions;
174
    }
175
176
    public function isOpenOn(string $day): bool
177
    {
178
        return count($this->forDay($day)) > 0;
179
    }
180
181
    public function isClosedOn(string $day): bool
182
    {
183
        return ! $this->isOpenOn($day);
184
    }
185
186
    public function isOpenAt(DateTimeInterface $dateTime): bool
187
    {
188
        $dateTime = $this->applyTimezone($dateTime);
189
190
        $openingHoursForDay = $this->forDate($dateTime);
191
192
        return $openingHoursForDay->isOpenAt(Time::fromDateTime($dateTime));
193
    }
194
195
    public function isClosedAt(DateTimeInterface $dateTime): bool
196
    {
197
        return ! $this->isOpenAt($dateTime);
198
    }
199
200
    public function isOpen(): bool
201
    {
202
        return $this->isOpenAt(new DateTime());
203
    }
204
205
    public function isClosed(): bool
206
    {
207
        return $this->isClosedAt(new DateTime());
208
    }
209
210
    /**
211
     * Returns the next open time.
212
     *
213
     * Notice: This will return DateTimeInterface on next major release
214
     * https://github.com/spatie/opening-hours/pull/75
215
     */
216 View Code Duplication
    public function nextOpen(DateTimeInterface $dateTime): 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...
217
    {
218
        if (! ($dateTime instanceof DateTimeImmutable)) {
219
            $dateTime = clone $dateTime;
220
        }
221
222
        $openingHoursForDay = $this->forDate($dateTime);
223
        $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime));
224
225
        while ($nextOpen === false) {
226
            $dateTime = $dateTime
227
                ->modify('+1 day')
228
                ->setTime(0, 0, 0);
229
230
            $openingHoursForDay = $this->forDate($dateTime);
231
232
            $nextOpen = $openingHoursForDay->nextOpen(Time::fromDateTime($dateTime));
233
        }
234
235
        $nextDateTime = $nextOpen->toDateTime();
236
        $dateTime = $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
237
238
        return $dateTime;
239
    }
240
241
    /**
242
     * Returns the next closed time.
243
     *
244
     * Notice: This will return DateTimeInterface on next major release
245
     * https://github.com/spatie/opening-hours/pull/75
246
     */
247 View Code Duplication
    public function nextClose(DateTimeInterface $dateTime): 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...
248
    {
249
        if (! ($dateTime instanceof DateTimeImmutable)) {
250
            $dateTime = clone $dateTime;
251
        }
252
253
        $openingHoursForDay = $this->forDate($dateTime);
254
        $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
255
256
        while ($nextClose === false) {
257
            $dateTime = $dateTime
258
                ->modify('+1 day')
259
                ->setTime(0, 0, 0);
260
261
            $openingHoursForDay = $this->forDate($dateTime);
262
263
            $nextClose = $openingHoursForDay->nextClose(Time::fromDateTime($dateTime));
264
        }
265
266
        $nextDateTime = $nextClose->toDateTime();
267
        $dateTime = $dateTime->setTime($nextDateTime->format('G'), $nextDateTime->format('i'), 0);
268
269
        return $dateTime;
270
    }
271
272
    public function regularClosingDays(): array
273
    {
274
        return array_keys($this->filter(function (OpeningHoursForDay $openingHoursForDay) {
275
            return $openingHoursForDay->isEmpty();
276
        }));
277
    }
278
279
    public function regularClosingDaysISO(): array
280
    {
281
        return Arr::map($this->regularClosingDays(), [Day::class, 'toISO']);
282
    }
283
284
    public function exceptionalClosingDates(): array
285
    {
286
        $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...
287
            return $openingHoursForDay->isEmpty();
288
        }));
289
290
        return Arr::map($dates, function ($date) {
291
            return DateTime::createFromFormat('Y-m-d', $date);
292
        });
293
    }
294
295
    public function setTimezone($timezone)
296
    {
297
        $this->timezone = new DateTimeZone($timezone);
298
    }
299
300
    protected function parseOpeningHoursAndExceptions(array $data): array
301
    {
302
        $exceptions = Arr::pull($data, 'exceptions', []);
303
        $openingHours = [];
304
305
        foreach ($data as $day => $openingHoursData) {
306
            $openingHours[$this->normalizeDayName($day)] = $openingHoursData;
307
        }
308
309
        return [$openingHours, $exceptions];
310
    }
311
312
    protected function setOpeningHoursFromStrings(string $day, array $openingHours)
313
    {
314
        $day = $this->normalizeDayName($day);
315
316
        $this->openingHours[$day] = OpeningHoursForDay::fromStrings($openingHours);
317
    }
318
319
    protected function setExceptionsFromStrings(array $exceptions)
320
    {
321
        $this->exceptions = Arr::map($exceptions, function (array $openingHours, string $date) {
322
            $recurring = DateTime::createFromFormat('m-d', $date);
323
324
            if ($recurring === false || $recurring->format('m-d') !== $date) {
325
                $dateTime = DateTime::createFromFormat('Y-m-d', $date);
326
327
                if ($dateTime === false || $dateTime->format('Y-m-d') !== $date) {
328
                    throw InvalidDate::invalidDate($date);
329
                }
330
            }
331
332
            return OpeningHoursForDay::fromStrings($openingHours);
333
        });
334
    }
335
336
    protected function normalizeDayName(string $day)
337
    {
338
        $day = strtolower($day);
339
340
        if (! Day::isValid($day)) {
341
            throw new InvalidDayName();
342
        }
343
344
        return $day;
345
    }
346
347
    protected function applyTimezone(DateTimeInterface $date)
348
    {
349
        if ($this->timezone) {
350
            $date = $date->setTimezone($this->timezone);
351
        }
352
353
        return $date;
354
    }
355
356
    public function filter(callable $callback): array
357
    {
358
        return Arr::filter($this->openingHours, $callback);
359
    }
360
361
    public function map(callable $callback): array
362
    {
363
        return Arr::map($this->openingHours, $callback);
364
    }
365
366
    public function flatMap(callable $callback): array
367
    {
368
        return Arr::flatMap($this->openingHours, $callback);
369
    }
370
371
    public function filterExceptions(callable $callback): array
372
    {
373
        return Arr::filter($this->exceptions, $callback);
374
    }
375
376
    public function mapExceptions(callable $callback): array
377
    {
378
        return Arr::map($this->exceptions, $callback);
379
    }
380
381
    public function flatMapExceptions(callable $callback): array
382
    {
383
        return Arr::flatMap($this->exceptions, $callback);
384
    }
385
386
    public function asStructuredData(): array
387
    {
388 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...
389
            return $openingHoursForDay->map(function (TimeRange $timeRange) use ($day) {
390
                return [
391
                    '@type' => 'OpeningHoursSpecification',
392
                    'dayOfWeek' => ucfirst($day),
393
                    'opens' => (string) $timeRange->start(),
394
                    'closes' => (string) $timeRange->end(),
395
                ];
396
            });
397
        });
398
399
        $exceptions = $this->flatMapExceptions(function (OpeningHoursForDay $openingHoursForDay, string $date) {
400
            if ($openingHoursForDay->isEmpty()) {
401
                return [[
402
                    '@type' => 'OpeningHoursSpecification',
403
                    'opens' => '00:00',
404
                    'closes' => '00:00',
405
                    'validFrom' => $date,
406
                    'validThrough' => $date,
407
                ]];
408
            }
409
410 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...
411
                return [
412
                    '@type' => 'OpeningHoursSpecification',
413
                    'opens' => (string) $timeRange->start(),
414
                    'closes' => (string) $timeRange->end(),
415
                    'validFrom' => $date,
416
                    'validThrough' => $date,
417
                ];
418
            });
419
        });
420
421
        return array_merge($regularHours, $exceptions);
422
    }
423
}
424