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:57
created

Period::startExcluded()   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\InvalidDate;
10
use Spatie\Period\Exceptions\InvalidPeriod;
11
12
class Period
13
{
14
    const EXCLUDE_NONE = 0;
15
    const EXCLUDE_START = 2;
16
    const EXCLUDE_END = 4;
17
    const EXCLUDE_ALL = 6;
18
19
    /** @var \DateTimeImmutable */
20
    protected $start;
21
22
    /** @var \DateTimeImmutable */
23
    protected $end;
24
25
    /** @var \DateInterval */
26
    protected $interval;
27
28
    /** @var int */
29
    private $exclusionMask;
30
31
    /** @var \DateTimeImmutable */
32
    private $includedStart;
33
34
    /** @var \DateTimeImmutable */
35
    private $includedEnd;
36
37
    public function __construct(
38
        DateTimeImmutable $start,
39
        DateTimeImmutable $end,
40
        int $exclusionMask = 0
41
    ) {
42
        if ($start > $end) {
43
            throw InvalidPeriod::endBeforeStart($start, $end);
44
        }
45
46
        $this->start = $start;
47
        $this->end = $end;
48
        $this->exclusionMask = $exclusionMask;
49
        $this->interval = new DateInterval('P1D');
50
51
        $this->includedStart = $this->startIncluded()
52
            ? $this->start
53
            : $this->start->add($this->interval);
54
55
        $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...
56
            ? $this->end
57
            : $this->end->sub($this->interval);
58
    }
59
60
    /**
61
     * @param \DateTimeInterface|string $start
62
     * @param \DateTimeInterface|string $end
63
     * @param string|null $format
64
     *
65
     * @return \Spatie\Period\Period|static
66
     */
67
    public static function make(
68
        $start,
69
        $end,
70
        ?string $format = null,
71
        int $exclusionMask = 0
72
    ): Period {
73
        if ($start === null) {
74
            throw InvalidDate::cannotBeNull('Start date');
75
        }
76
77
        if ($end === null) {
78
            throw InvalidDate::cannotBeNull('End date');
79
        }
80
81
        return new static(
82
            self::resolveDate($start, $format),
83
            self::resolveDate($end, $format),
84
            $exclusionMask
85
        );
86
    }
87
88
    public function startIncluded(): bool
89
    {
90
        return ! $this->startExcluded();
91
    }
92
93
    public function startExcluded(): bool
94
    {
95
        return self::EXCLUDE_START & $this->exclusionMask;
96
    }
97
98
    public function endIncluded(): bool
99
    {
100
        return ! $this->endExcluded();
101
    }
102
103
    public function endExcluded(): bool
104
    {
105
        return self::EXCLUDE_END & $this->exclusionMask;
106
    }
107
108
    protected static function resolveDate($date, ?string $format): DateTimeImmutable
109
    {
110
        if ($date instanceof DateTimeImmutable) {
111
            return $date;
112
        }
113
114
        if ($date instanceof DateTime) {
115
            return DateTimeImmutable::createFromMutable($date);
116
        }
117
118
        $format = self::resolveFormat($date, $format);
119
120
        if (! is_string($date)) {
121
            throw InvalidDate::forFormat($date, $format);
122
        }
123
124
        $dateTime = DateTimeImmutable::createFromFormat($format, $date);
125
126
        if ($dateTime === false) {
127
            throw InvalidDate::forFormat($date, $format);
128
        }
129
130
        if (strpos($format, ' ') === false) {
131
            $dateTime = $dateTime->setTime(0, 0, 0);
132
        }
133
134
        return $dateTime;
135
    }
136
137
    protected static function resolveFormat($date, ?string $format): string
138
    {
139
        if ($format !== null) {
140
            return $format;
141
        }
142
143
        if (
144
            strpos($format, ' ') === false
145
            && strpos($date, ' ') !== false
146
        ) {
147
            return 'Y-m-d H:i:s';
148
        }
149
150
        return 'Y-m-d';
151
    }
152
153
    public function getStart(): DateTimeImmutable
154
    {
155
        return $this->start;
156
    }
157
158
    public function getIncludedStart(): DateTimeImmutable
159
    {
160
        return $this->includedStart;
161
    }
162
163
    public function getEnd(): DateTimeImmutable
164
    {
165
        return $this->end;
166
    }
167
168
    public function getIncludedEnd(): DateTimeImmutable
169
    {
170
        return $this->includedEnd;
171
    }
172
173
    public function length(): int
174
    {
175
        $length = $this->getIncludedStart()->diff($this->getIncludedEnd())->days + 1;
176
177
        return $length;
178
    }
179
180
    public function overlapsWith(Period $period): bool
181
    {
182
        if ($this->getIncludedStart() > $period->getIncludedEnd()) {
183
            return false;
184
        }
185
186
        if ($period->getIncludedStart() > $this->getIncludedEnd()) {
187
            return false;
188
        }
189
190
        return true;
191
    }
192
193
    public function touchesWith(Period $period): bool
194
    {
195
        if ($this->getIncludedEnd()->diff($period->getIncludedStart())->days <= 1) {
196
            return true;
197
        }
198
199
        if ($this->getIncludedStart()->diff($period->getIncludedEnd())->days <= 1) {
200
            return true;
201
        }
202
203
        return false;
204
    }
205
206
    public function startsAfterOrAt(DateTimeInterface $date): bool
207
    {
208
        return $this->getIncludedStart() >= $date;
209
    }
210
211
    public function endsAfterOrAt(DateTimeInterface $date): bool
212
    {
213
        return $this->end >= $date;
214
    }
215
216
    public function startsBeforeOrAt(DateTimeInterface $date): bool
217
    {
218
        return $this->getIncludedStart() <= $date;
219
    }
220
221
    public function endsBeforeOrAt(DateTimeInterface $date): bool
222
    {
223
        return $this->end <= $date;
224
    }
225
226
    public function startsAfter(DateTimeInterface $date): bool
227
    {
228
        return $this->getIncludedStart() > $date;
229
    }
230
231
    public function endsAfter(DateTimeInterface $date): bool
232
    {
233
        return $this->end > $date;
234
    }
235
236
    public function startsBefore(DateTimeInterface $date): bool
237
    {
238
        return $this->getIncludedStart() < $date;
239
    }
240
241
    public function endsBefore(DateTimeInterface $date): bool
242
    {
243
        return $this->getIncludedEnd() < $date;
244
    }
245
246
    public function contains(DateTimeInterface $date): bool
247
    {
248
        if ($date < $this->getIncludedStart()) {
249
            return false;
250
        }
251
252
        if ($date > $this->getIncludedEnd()) {
253
            return false;
254
        }
255
256
        return true;
257
    }
258
259
    public function equals(Period $period): bool
260
    {
261
        if ($period->getIncludedStart()->getTimestamp() !== $this->getIncludedStart()->getTimestamp()) {
262
            return false;
263
        }
264
265
        if ($period->getIncludedEnd()->getTimestamp() !== $this->getIncludedEnd()->getTimestamp()) {
266
            return false;
267
        }
268
269
        return true;
270
    }
271
272
    /**
273
     * @param \Spatie\Period\Period $period
274
     *
275
     * @return \Spatie\Period\Period|static|null
276
     * @throws \Exception
277
     */
278
    public function gap(Period $period): ?Period
279
    {
280
        if ($this->overlapsWith($period)) {
281
            return null;
282
        }
283
284
        if ($this->touchesWith($period)) {
285
            return null;
286
        }
287
288
        if ($this->getIncludedStart() >= $period->getIncludedEnd()) {
289
            return static::make(
290
                $period->getIncludedEnd()->add($this->interval),
291
                $this->getIncludedStart()->sub($this->interval)
0 ignored issues
show
Security Bug introduced by
It seems like $this->getIncludedStart()->sub($this->interval) targeting DateTimeImmutable::sub() can also be of type false; however, Spatie\Period\Period::make() does only seem to accept object<DateTimeInterface>|string, did you maybe forget to handle an error condition?
Loading history...
292
            );
293
        }
294
295
        return static::make(
296
            $this->getIncludedEnd()->add($this->interval),
297
            $period->getIncludedStart()->sub($this->interval)
0 ignored issues
show
Security Bug introduced by
It seems like $period->getIncludedStart()->sub($this->interval) targeting DateTimeImmutable::sub() can also be of type false; however, Spatie\Period\Period::make() does only seem to accept object<DateTimeInterface>|string, did you maybe forget to handle an error condition?
Loading history...
298
        );
299
    }
300
301
    /**
302
     * @param \Spatie\Period\Period $period
303
     *
304
     * @return \Spatie\Period\Period|static|null
305
     */
306
    public function overlapSingle(Period $period): ?Period
307
    {
308
        $start = $this->getIncludedStart() > $period->getIncludedStart()
309
            ? $this->getIncludedStart()
310
            : $period->getIncludedStart();
311
312
        $end = $this->getIncludedEnd() < $period->getIncludedEnd()
313
            ? $this->getIncludedEnd()
314
            : $period->getIncludedEnd();
315
316
        if ($start > $end) {
317
            return null;
318
        }
319
320
        return static::make($start, $end);
321
    }
322
323
    /**
324
     * @param \Spatie\Period\Period ...$periods
325
     *
326
     * @return \Spatie\Period\PeriodCollection|static[]
327
     */
328
    public function overlap(Period ...$periods): PeriodCollection
329
    {
330
        $overlapCollection = new PeriodCollection();
331
332
        foreach ($periods as $period) {
333
            $overlapCollection[] = $this->overlapSingle($period);
334
        }
335
336
        return $overlapCollection;
337
    }
338
339
    /**
340
     * @param \Spatie\Period\Period ...$periods
341
     *
342
     * @return \Spatie\Period\Period|static
343
     */
344
    public function overlapAll(Period ...$periods): Period
345
    {
346
        $overlap = clone $this;
347
348
        if (! count($periods)) {
349
            return $overlap;
350
        }
351
352
        foreach ($periods as $period) {
353
            $overlap = $overlap->overlapSingle($period);
354
        }
355
356
        return $overlap;
357
    }
358
359
    public function diffSingle(Period $period): PeriodCollection
360
    {
361
        $periodCollection = new PeriodCollection();
362
363
        if (! $this->overlapsWith($period)) {
364
            $periodCollection[] = clone $this;
365
            $periodCollection[] = clone $period;
366
367
            return $periodCollection;
368
        }
369
370
        $overlap = $this->overlapSingle($period);
371
372
        $start = $this->getIncludedStart() < $period->getIncludedStart()
373
            ? $this->getIncludedStart()
374
            : $period->getIncludedStart();
375
376
        $end = $this->getIncludedEnd() > $period->getIncludedEnd()
377
            ? $this->getIncludedEnd()
378
            : $period->getIncludedEnd();
379
380
        if ($overlap->getIncludedStart() > $start) {
381
            $periodCollection[] = static::make(
382
                $start,
383
                $overlap->getIncludedStart()->sub($this->interval)
384
            );
385
        }
386
387
        if ($overlap->getIncludedEnd() < $end) {
388
            $periodCollection[] = static::make(
389
                $overlap->getIncludedEnd()->add($this->interval),
390
                $end
391
            );
392
        }
393
394
        return $periodCollection;
395
    }
396
397
    /**
398
     * @param \Spatie\Period\Period ...$periods
399
     *
400
     * @return \Spatie\Period\PeriodCollection|static[]
401
     */
402
    public function diff(Period ...$periods): PeriodCollection
403
    {
404
        if (count($periods) === 1 && ! $this->overlapsWith($periods[0])) {
405
            $collection = new PeriodCollection();
406
407
            $collection[] = $this->gap($periods[0]);
408
409
            return $collection;
410
        }
411
412
        $diffs = [];
413
414
        foreach ($periods as $period) {
415
            $diffs[] = $this->diffSingle($period);
416
        }
417
418
        $collection = (new PeriodCollection($this))->overlap(...$diffs);
419
420
        return $collection;
421
    }
422
}
423