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 (#11)
by Brent
01:24
created

Period::getPrecisionMask()   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\Period;
4
5
use DateTime;
6
use DateInterval;
7
use DateTimeImmutable;
8
use DateTimeInterface;
9
use Spatie\Period\Exceptions\CannotComparePeriods;
10
use Spatie\Period\Exceptions\InvalidDate;
11
use Spatie\Period\Exceptions\InvalidPeriod;
12
13
class Period
14
{
15
    /** @var \DateTimeImmutable */
16
    protected $start;
17
18
    /** @var \DateTimeImmutable */
19
    protected $end;
20
21
    /** @var \DateInterval */
22
    protected $interval;
23
24
    /** @var \DateTimeImmutable */
25
    private $includedStart;
26
27
    /** @var \DateTimeImmutable */
28
    private $includedEnd;
29
30
    /** @var int */
31
    private $boundaryExclusionMask;
32
33
    /** @var int */
34
    private $precisionMask;
35
36
    public function __construct(
37
        DateTimeImmutable $start,
38
        DateTimeImmutable $end,
39
        ?int $precisionMask = null,
40
        ?int $boundaryExclusionMask = null
41
    ) {
42
        if ($start > $end) {
43
            throw InvalidPeriod::endBeforeStart($start, $end);
44
        }
45
46
        $this->boundaryExclusionMask = $boundaryExclusionMask ?? Boundaries::EXCLUDE_NONE;
47
        $this->precisionMask = $precisionMask ?? Precision::DAY;
48
49
        $this->start = $this->roundDate($start, $this->precisionMask);
50
        $this->end = $this->roundDate($end, $this->precisionMask);
51
        $this->interval = $this->createDateInterval($this->precisionMask);
52
53
        $this->includedStart = $this->startIncluded()
54
            ? $this->start
55
            : $this->start->add($this->interval);
56
57
        $this->includedEnd = $this->endIncluded()
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->endIncluded() ? $...d->sub($this->interval) can also be of type false. However, the property $includedEnd is declared as type object<DateTimeImmutable>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
58
            ? $this->end
59
            : $this->end->sub($this->interval);
60
    }
61
62
    /**
63
     * @param $start
64
     * @param $end
65
     * @param int|null $precisionMask
66
     * @param int|null $boundaryExclusionMask
67
     * @param string|null $format
68
     *
69
     * @return \Spatie\Period\Period|static
70
     */
71
    public static function make(
72
        $start,
73
        $end,
74
        ?int $precisionMask = null,
75
        ?int $boundaryExclusionMask = null,
76
        ?string $format = null
77
    ): Period {
78
        if ($start === null) {
79
            throw InvalidDate::cannotBeNull('Start date');
80
        }
81
82
        if ($end === null) {
83
            throw InvalidDate::cannotBeNull('End date');
84
        }
85
86
        return new static(
87
            self::resolveDate($start, $format),
88
            self::resolveDate($end, $format),
89
            $precisionMask,
90
            $boundaryExclusionMask
91
        );
92
    }
93
94
    public function startIncluded(): bool
95
    {
96
        return ! $this->startExcluded();
97
    }
98
99
    public function startExcluded(): bool
100
    {
101
        return Boundaries::EXCLUDE_START & $this->boundaryExclusionMask;
102
    }
103
104
    public function endIncluded(): bool
105
    {
106
        return ! $this->endExcluded();
107
    }
108
109
    public function endExcluded(): bool
110
    {
111
        return Boundaries::EXCLUDE_END & $this->boundaryExclusionMask;
112
    }
113
114
    public function getStart(): DateTimeImmutable
115
    {
116
        return $this->start;
117
    }
118
119
    public function getIncludedStart(): DateTimeImmutable
120
    {
121
        return $this->includedStart;
122
    }
123
124
    public function getEnd(): DateTimeImmutable
125
    {
126
        return $this->end;
127
    }
128
129
    public function getIncludedEnd(): DateTimeImmutable
130
    {
131
        return $this->includedEnd;
132
    }
133
134
    public function length(): int
135
    {
136
        $length = $this->getIncludedStart()->diff($this->getIncludedEnd())->days + 1;
137
138
        return $length;
139
    }
140
141
    public function overlapsWith(Period $period): bool
142
    {
143
        $this->ensurePrecisionMatches($period);
144
145
        if ($this->getIncludedStart() > $period->getIncludedEnd()) {
146
            return false;
147
        }
148
149
        if ($period->getIncludedStart() > $this->getIncludedEnd()) {
150
            return false;
151
        }
152
153
        return true;
154
    }
155
156
    public function touchesWith(Period $period): bool
157
    {
158
        $this->ensurePrecisionMatches($period);
159
160
        if ($this->getIncludedEnd()->diff($period->getIncludedStart())->days <= 1) {
161
            return true;
162
        }
163
164
        if ($this->getIncludedStart()->diff($period->getIncludedEnd())->days <= 1) {
165
            return true;
166
        }
167
168
        return false;
169
    }
170
171
    public function startsAfterOrAt(DateTimeInterface $date): bool
172
    {
173
        return $this->getIncludedStart() >= $date;
174
    }
175
176
    public function endsAfterOrAt(DateTimeInterface $date): bool
177
    {
178
        return $this->getIncludedEnd() >= $date;
179
    }
180
181
    public function startsBeforeOrAt(DateTimeInterface $date): bool
182
    {
183
        return $this->getIncludedStart() <= $date;
184
    }
185
186
    public function endsBeforeOrAt(DateTimeInterface $date): bool
187
    {
188
        return $this->getIncludedEnd() <= $date;
189
    }
190
191
    public function startsAfter(DateTimeInterface $date): bool
192
    {
193
        return $this->getIncludedStart() > $date;
194
    }
195
196
    public function endsAfter(DateTimeInterface $date): bool
197
    {
198
        return $this->getIncludedEnd() > $date;
199
    }
200
201
    public function startsBefore(DateTimeInterface $date): bool
202
    {
203
        return $this->getIncludedStart() < $date;
204
    }
205
206
    public function endsBefore(DateTimeInterface $date): bool
207
    {
208
        return $this->getIncludedEnd() < $date;
209
    }
210
211
    public function contains(DateTimeInterface $date): bool
212
    {
213
        if ($date < $this->getIncludedStart()) {
214
            return false;
215
        }
216
217
        if ($date > $this->getIncludedEnd()) {
218
            return false;
219
        }
220
221
        return true;
222
    }
223
224
    public function equals(Period $period): bool
225
    {
226
        $this->ensurePrecisionMatches($period);
227
228
        if ($period->getIncludedStart()->getTimestamp() !== $this->getIncludedStart()->getTimestamp()) {
229
            return false;
230
        }
231
232
        if ($period->getIncludedEnd()->getTimestamp() !== $this->getIncludedEnd()->getTimestamp()) {
233
            return false;
234
        }
235
236
        return true;
237
    }
238
239
    /**
240
     * @param \Spatie\Period\Period $period
241
     *
242
     * @return \Spatie\Period\Period|static|null
243
     * @throws \Exception
244
     */
245
    public function gap(Period $period): ?Period
246
    {
247
        $this->ensurePrecisionMatches($period);
248
249
        if ($this->overlapsWith($period)) {
250
            return null;
251
        }
252
253
        if ($this->touchesWith($period)) {
254
            return null;
255
        }
256
257
        if ($this->getIncludedStart() >= $period->getIncludedEnd()) {
258
            return static::make(
259
                $period->getIncludedEnd()->add($this->interval),
260
                $this->getIncludedStart()->sub($this->interval)
261
            );
262
        }
263
264
        return static::make(
265
            $this->getIncludedEnd()->add($this->interval),
266
            $period->getIncludedStart()->sub($this->interval)
267
        );
268
    }
269
270
    /**
271
     * @param \Spatie\Period\Period $period
272
     *
273
     * @return \Spatie\Period\Period|static|null
274
     */
275
    public function overlapSingle(Period $period): ?Period
276
    {
277
        $this->ensurePrecisionMatches($period);
278
279
        $start = $this->getIncludedStart() > $period->getIncludedStart()
280
            ? $this->getIncludedStart()
281
            : $period->getIncludedStart();
282
283
        $end = $this->getIncludedEnd() < $period->getIncludedEnd()
284
            ? $this->getIncludedEnd()
285
            : $period->getIncludedEnd();
286
287
        if ($start > $end) {
288
            return null;
289
        }
290
291
        return static::make($start, $end);
292
    }
293
294
    /**
295
     * @param \Spatie\Period\Period ...$periods
296
     *
297
     * @return \Spatie\Period\PeriodCollection|static[]
298
     */
299
    public function overlap(Period ...$periods): PeriodCollection
300
    {
301
        $overlapCollection = new PeriodCollection();
302
303
        foreach ($periods as $period) {
304
            $overlapCollection[] = $this->overlapSingle($period);
305
        }
306
307
        return $overlapCollection;
308
    }
309
310
    /**
311
     * @param \Spatie\Period\Period ...$periods
312
     *
313
     * @return \Spatie\Period\Period|static
314
     */
315
    public function overlapAll(Period ...$periods): Period
316
    {
317
        $overlap = clone $this;
318
319
        if (! count($periods)) {
320
            return $overlap;
321
        }
322
323
        foreach ($periods as $period) {
324
            $overlap = $overlap->overlapSingle($period);
325
        }
326
327
        return $overlap;
328
    }
329
330
    public function diffSingle(Period $period): PeriodCollection
331
    {
332
        $periodCollection = new PeriodCollection();
333
334
        if (! $this->overlapsWith($period)) {
335
            $periodCollection[] = clone $this;
336
            $periodCollection[] = clone $period;
337
338
            return $periodCollection;
339
        }
340
341
        $overlap = $this->overlapSingle($period);
342
343
        $start = $this->getIncludedStart() < $period->getIncludedStart()
344
            ? $this->getIncludedStart()
345
            : $period->getIncludedStart();
346
347
        $end = $this->getIncludedEnd() > $period->getIncludedEnd()
348
            ? $this->getIncludedEnd()
349
            : $period->getIncludedEnd();
350
351
        if ($overlap->getIncludedStart() > $start) {
352
            $periodCollection[] = static::make(
353
                $start,
354
                $overlap->getIncludedStart()->sub($this->interval)
355
            );
356
        }
357
358
        if ($overlap->getIncludedEnd() < $end) {
359
            $periodCollection[] = static::make(
360
                $overlap->getIncludedEnd()->add($this->interval),
361
                $end
362
            );
363
        }
364
365
        return $periodCollection;
366
    }
367
368
    /**
369
     * @param \Spatie\Period\Period ...$periods
370
     *
371
     * @return \Spatie\Period\PeriodCollection|static[]
372
     */
373
    public function diff(Period ...$periods): PeriodCollection
374
    {
375
        if (count($periods) === 1 && ! $this->overlapsWith($periods[0])) {
376
            $collection = new PeriodCollection();
377
378
            $collection[] = $this->gap($periods[0]);
379
380
            return $collection;
381
        }
382
383
        $diffs = [];
384
385
        foreach ($periods as $period) {
386
            $diffs[] = $this->diffSingle($period);
387
        }
388
389
        $collection = (new PeriodCollection($this))->overlap(...$diffs);
390
391
        return $collection;
392
    }
393
394
    public function getPrecisionMask(): int
395
    {
396
        return $this->precisionMask;
397
    }
398
399
    protected static function resolveDate($date, ?string $format): DateTimeImmutable
400
    {
401
        if ($date instanceof DateTimeImmutable) {
402
            return $date;
403
        }
404
405
        if ($date instanceof DateTime) {
406
            return DateTimeImmutable::createFromMutable($date);
407
        }
408
409
        $format = self::resolveFormat($date, $format);
410
411
        if (! is_string($date)) {
412
            throw InvalidDate::forFormat($date, $format);
413
        }
414
415
        $dateTime = DateTimeImmutable::createFromFormat($format, $date);
416
417
        if ($dateTime === false) {
418
            throw InvalidDate::forFormat($date, $format);
419
        }
420
421
        if (strpos($format, ' ') === false) {
422
            $dateTime = $dateTime->setTime(0, 0, 0);
423
        }
424
425
        return $dateTime;
426
    }
427
428
    protected static function resolveFormat($date, ?string $format): string
429
    {
430
        if ($format !== null) {
431
            return $format;
432
        }
433
434
        if (
435
            strpos($format, ' ') === false
436
            && strpos($date, ' ') !== false
437
        ) {
438
            return 'Y-m-d H:i:s';
439
        }
440
441
        return 'Y-m-d';
442
    }
443
444
    protected function roundDate(DateTimeImmutable $date, int $precision): DateTimeImmutable
445
    {
446
        [$year, $month, $day, $hour, $minute, $second] = explode(' ', $date->format('Y m d H i s'));
0 ignored issues
show
Bug introduced by
The variable $year seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $month seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $day seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $hour seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $minute seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $second seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
447
448
        $year = (Precision::YEAR & $precision) === Precision::YEAR ? $year : '00';
0 ignored issues
show
Bug introduced by
The variable $year seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
449
        $month = (Precision::MONTH & $precision) === Precision::MONTH ? $month : '01';
0 ignored issues
show
Bug introduced by
The variable $month seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
450
        $day = (Precision::DAY & $precision) === Precision::DAY ? $day : '01';
0 ignored issues
show
Bug introduced by
The variable $day seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
451
        $hour = (Precision::HOUR & $precision) === Precision::HOUR ? $hour : '00';
0 ignored issues
show
Bug introduced by
The variable $hour seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
452
        $minute = (Precision::MINUTE & $precision) === Precision::MINUTE ? $minute : '00';
0 ignored issues
show
Bug introduced by
The variable $minute seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
453
        $second = (Precision::SECOND & $precision) === Precision::SECOND ? $second : '00';
0 ignored issues
show
Bug introduced by
The variable $second seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
454
455
        return DateTimeImmutable::createFromFormat(
456
            'Y m d H i s',
457
            implode(' ', [$year, $month, $day, $hour, $minute, $second])
458
        );
459
    }
460
461
    protected function createDateInterval(int $precision): DateInterval
462
    {
463
        $interval = [
464
            Precision::SECOND => 'PT1S',
465
            Precision::MINUTE => 'PT1M',
466
            Precision::HOUR => 'PT1H',
467
            Precision::DAY => 'P1D',
468
            Precision::MONTH => 'P1M',
469
            Precision::YEAR => 'P1Y',
470
        ][$precision];
471
472
        return new DateInterval($interval);
473
    }
474
475
    protected function ensurePrecisionMatches(Period $period): void
476
    {
477
        if ($this->precisionMask === $period->precisionMask) {
478
            return;
479
        }
480
481
        throw CannotComparePeriods::precisionDoesNotMatch();
482
    }
483
}
484