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 ( d488be...7ae63c )
by Brent
01:26
created

Period   F

Complexity

Total Complexity 61

Size/Duplication

Total Lines 320
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 61
lcom 1
cbo 3
dl 0
loc 320
rs 3.52
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A make() 0 15 3
B resolveDate() 0 28 6
A resolveFormat() 0 15 4
A getStart() 0 4 1
A getEnd() 0 4 1
A length() 0 4 1
A overlapsWith() 0 12 3
A touchesWith() 0 12 3
A startsAfterOrAt() 0 4 1
A endsAfterOrAt() 0 4 1
A startsBeforeOrAt() 0 4 1
A endsBeforeOrAt() 0 4 1
A startsAfter() 0 4 1
A endsAfter() 0 4 1
A startsBefore() 0 4 1
A endsBefore() 0 4 1
A contains() 0 12 3
A equals() 0 12 3
A gap() 0 22 4
A overlapSingle() 0 16 4
A overlap() 0 10 2
A overlapAll() 0 14 3
B diffSingle() 0 37 6
A diff() 0 22 4

How to fix   Complexity   

Complex Class

Complex classes like Period often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Period, and based on these observations, apply Extract Interface, too.

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
    /** @var \DateTimeImmutable */
15
    protected $start;
16
17
    /** @var \DateTimeImmutable */
18
    protected $end;
19
20
    public function __construct(DateTimeImmutable $start, DateTimeImmutable $end)
21
    {
22
        if ($start > $end) {
23
            throw InvalidPeriod::endBeforeStart($start, $end);
24
        }
25
26
        $this->start = $start;
27
        $this->end = $end;
28
    }
29
30
    /**
31
     * @param \DateTimeInterface|string $start
32
     * @param \DateTimeInterface|string $end
33
     * @param string|null $format
34
     *
35
     * @return \Spatie\Period\Period|static
36
     */
37
    public static function make($start, $end, string $format = null): Period
38
    {
39
        if ($start === null) {
40
            throw InvalidDate::cannotBeNull('Start date');
41
        }
42
43
        if ($end === null) {
44
            throw InvalidDate::cannotBeNull('End date');
45
        }
46
47
        return new static(
48
            self::resolveDate($start, $format),
49
            self::resolveDate($end, $format)
50
        );
51
    }
52
53
    protected static function resolveDate($date, ?string $format): DateTimeImmutable
54
    {
55
        if ($date instanceof DateTimeImmutable) {
56
            return $date;
57
        }
58
59
        if ($date instanceof DateTime) {
60
            return DateTimeImmutable::createFromMutable($date);
61
        }
62
63
        $format = self::resolveFormat($date, $format);
64
65
        if (! is_string($date)) {
66
            throw InvalidDate::forFormat($date, $format);
67
        }
68
69
        $dateTime = DateTimeImmutable::createFromFormat($format, $date);
70
71
        if ($dateTime === false) {
72
            throw InvalidDate::forFormat($date, $format);
73
        }
74
75
        if (strpos($format, ' ') === false) {
76
            $dateTime = $dateTime->setTime(0, 0, 0);
77
        }
78
79
        return $dateTime;
80
    }
81
82
    protected static function resolveFormat($date, ?string $format): string
83
    {
84
        if ($format !== null) {
85
            return $format;
86
        }
87
88
        if (
89
            strpos($format, ' ') === false
90
            && strpos($date, ' ') !== false
91
        ) {
92
            return 'Y-m-d H:i:s';
93
        }
94
95
        return 'Y-m-d';
96
    }
97
98
    public function getStart(): DateTimeImmutable
99
    {
100
        return $this->start;
101
    }
102
103
    public function getEnd(): DateTimeImmutable
104
    {
105
        return $this->end;
106
    }
107
108
    public function length(): int
109
    {
110
        return $this->start->diff($this->end)->days + 1;
111
    }
112
113
    public function overlapsWith(Period $period): bool
114
    {
115
        if ($this->start > $period->end) {
116
            return false;
117
        }
118
119
        if ($period->start > $this->end) {
120
            return false;
121
        }
122
123
        return true;
124
    }
125
126
    public function touchesWith(Period $period): bool
127
    {
128
        if ($this->end->diff($period->start)->days <= 1) {
129
            return true;
130
        }
131
132
        if ($this->start->diff($period->end)->days <= 1) {
133
            return true;
134
        }
135
136
        return false;
137
    }
138
139
    public function startsAfterOrAt(DateTimeInterface $date): bool
140
    {
141
        return $this->start >= $date;
142
    }
143
144
    public function endsAfterOrAt(DateTimeInterface $date): bool
145
    {
146
        return $this->end >= $date;
147
    }
148
149
    public function startsBeforeOrAt(DateTimeInterface $date): bool
150
    {
151
        return $this->start <= $date;
152
    }
153
154
    public function endsBeforeOrAt(DateTimeInterface $date): bool
155
    {
156
        return $this->end <= $date;
157
    }
158
159
    public function startsAfter(DateTimeInterface $date): bool
160
    {
161
        return $this->start > $date;
162
    }
163
164
    public function endsAfter(DateTimeInterface $date): bool
165
    {
166
        return $this->end > $date;
167
    }
168
169
    public function startsBefore(DateTimeInterface $date): bool
170
    {
171
        return $this->start < $date;
172
    }
173
174
    public function endsBefore(DateTimeInterface $date): bool
175
    {
176
        return $this->end < $date;
177
    }
178
179
    public function contains(DateTimeInterface $date): bool
180
    {
181
        if ($date < $this->start) {
182
            return false;
183
        }
184
185
        if ($date > $this->end) {
186
            return false;
187
        }
188
189
        return true;
190
    }
191
192
    public function equals(Period $period): bool
193
    {
194
        if ($period->start->getTimestamp() !== $this->start->getTimestamp()) {
195
            return false;
196
        }
197
198
        if ($period->end->getTimestamp() !== $this->end->getTimestamp()) {
199
            return false;
200
        }
201
202
        return true;
203
    }
204
205
    public function gap(Period $period): ?Period
206
    {
207
        if ($this->overlapsWith($period)) {
208
            return null;
209
        }
210
211
        if ($this->touchesWith($period)) {
212
            return null;
213
        }
214
215
        if ($this->start >= $period->end) {
216
            return static::make(
217
                $period->end->add(new DateInterval('P1D')),
218
                $this->start->sub(new DateInterval('P1D'))
0 ignored issues
show
Security Bug introduced by
It seems like $this->start->sub(new \DateInterval('P1D')) 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...
219
            );
220
        }
221
222
        return static::make(
223
            $this->end->add(new DateInterval('P1D')),
224
            $period->start->sub(new DateInterval('P1D'))
0 ignored issues
show
Security Bug introduced by
It seems like $period->start->sub(new \DateInterval('P1D')) 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...
225
        );
226
    }
227
228
    public function overlapSingle(Period $period): ?Period
229
    {
230
        $start = $this->start > $period->start
231
            ? $this->start
232
            : $period->start;
233
234
        $end = $this->end < $period->end
235
            ? $this->end
236
            : $period->end;
237
238
        if ($start > $end) {
239
            return null;
240
        }
241
242
        return static::make($start, $end);
243
    }
244
245
    public function overlap(Period ...$periods): PeriodCollection
246
    {
247
        $overlapCollection = new PeriodCollection();
248
249
        foreach ($periods as $period) {
250
            $overlapCollection[] = $this->overlapSingle($period);
251
        }
252
253
        return $overlapCollection;
254
    }
255
256
    public function overlapAll(Period ...$periods): Period
257
    {
258
        $overlap = clone $this;
259
260
        if (! count($periods)) {
261
            return $overlap;
262
        }
263
264
        foreach ($periods as $period) {
265
            $overlap = $overlap->overlapSingle($period);
266
        }
267
268
        return $overlap;
269
    }
270
271
    public function diffSingle(Period $period): PeriodCollection
272
    {
273
        $periodCollection = new PeriodCollection();
274
275
        if (! $this->overlapsWith($period)) {
276
            $periodCollection[] = clone $this;
277
            $periodCollection[] = clone $period;
278
279
            return $periodCollection;
280
        }
281
282
        $overlap = $this->overlapSingle($period);
283
284
        $start = $this->start < $period->start
285
            ? $this->start
286
            : $period->start;
287
288
        $end = $this->end > $period->end
289
            ? $this->end
290
            : $period->end;
291
292
        if ($overlap->start > $start) {
293
            $periodCollection[] = static::make(
294
                $start,
295
                $overlap->start->sub(new DateInterval('P1D'))
296
            );
297
        }
298
299
        if ($overlap->end < $end) {
300
            $periodCollection[] = static::make(
301
                $overlap->end->add(new DateInterval('P1D')),
302
                $end
303
            );
304
        }
305
306
        return $periodCollection;
307
    }
308
309
    public function diff(Period ...$periods): PeriodCollection
310
    {
311
        if (count($periods) === 1) {
312
            $collection = new PeriodCollection();
313
314
            if (! $this->overlapsWith($periods[0])) {
315
                $collection[] = $this->gap($periods[0]);
316
            }
317
318
            return $collection;
319
        }
320
321
        $diffs = [];
322
323
        foreach ($periods as $period) {
324
            $diffs[] = $this->diffSingle($period);
325
        }
326
327
        $collection = (new PeriodCollection($this))->overlap(...$diffs);
328
329
        return $collection;
330
    }
331
}
332