Completed
Push — master ( 474cbc...4e1719 )
by Olivier
03:46
created

EventsFlattener   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 92.19%

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 2
dl 0
loc 124
ccs 59
cts 64
cp 0.9219
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
B flatten() 0 39 8
A getAllRecurringEvents() 0 20 2
A getModifier() 0 11 3
A getLastEvent() 0 15 3
A changeDateToGivenDay() 0 19 3
A createEventFromRecurring() 0 12 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shapin\Calendar;
6
7
use Shapin\Calendar\Model\Event;
8
use Shapin\Calendar\Model\RecurrenceRule;
9
10
class EventsFlattener
11
{
12 8
    public function flatten(Event $event): array
13
    {
14 8
        if (!$event->isRecurring()) {
15
            return [
16 1
                $event->getStartAt()->getTimestamp() => $event,
17
            ];
18
        }
19
20 7
        $parts = $event->getRecurrenceRule()->getParts();
21
22 7
        if (!isset($parts['FREQ'])) {
23
            throw new \BadMethodCallException("RecurrenceRule doesn't contains a FREQ. Not implemented yet.");
24
        }
25
26 7
        if (isset($parts['BYDAY']) && is_array($parts['BYDAY'])) {
27 2
            $events = [];
28 2
            foreach ($parts['BYDAY'] as $day) {
29 2
                $startAt = $this->changeDateToGivenDay($event->getStartAt(), $day);
30 2
                $endAt = $this->changeDateToGivenDay($event->getEndAt(), $day);
31
32 2
                $dailyEventRecurrenceRuleParts = array_merge($parts, [
33 2
                    'BYDAY' => $day,
34
                ]);
35
36 2
                $dailyEvent = $this->createEventFromRecurring($event, $startAt, $endAt);
37 2
                $dailyEvent->setRecurrenceRule(RecurrenceRule::createFromArray($dailyEventRecurrenceRuleParts));
38
39 2
                $events += $this->getAllRecurringEvents($dailyEvent);
40
            }
41
42 2
            return $events;
43
        }
44
45 5
        if (!isset($parts['BYDAY']) || !is_array($parts['BYDAY'])) {
46 5
            return $this->getAllRecurringEvents($event);
47
        }
48
49
        throw new \BadMethodCallException('Looks like this case is not implemented yet...');
50
    }
51
52 7
    private function getAllRecurringEvents(Event $event): array
53
    {
54 7
        $freq = $event->getRecurrenceRule()->getParts()['FREQ'];
55
56 7
        $startAt = $event->getStartAt();
57 7
        $duration = $event->getEndAt()->diff($startAt);
58 7
        $modifier = $this->getModifier($freq);
59
60 7
        $events = [$event->getStartAt()->getTimestamp() => $event];
61 7
        $until = $this->getLastEvent($event);
62 7
        $nextEventStartAt = $startAt->modify($modifier);
63 7
        while ($nextEventStartAt < $until) {
64 7
            $newEvent = $this->createEventFromRecurring($event, $nextEventStartAt, $nextEventStartAt->add($duration));
65
66 7
            $events[$newEvent->getStartAt()->getTimestamp()] = $newEvent;
67 7
            $nextEventStartAt = $nextEventStartAt->modify($modifier);
68
        }
69
70 7
        return $events;
71
    }
72
73 7
    private function getModifier(string $freq, int $times = 1): string
74
    {
75 7
        if ('WEEKLY' === $freq) {
76 5
            return "+$times week";
77
        }
78 2
        if ('MONTHLY' === $freq) {
79 2
            return "+$times month";
80
        }
81
82
        throw new \BadMethodCallException("Unknown freq $freq");
83
    }
84
85 7
    private function getLastEvent(Event $event): \DateTimeImmutable
86
    {
87 7
        $parts = $event->getRecurrenceRule()->getParts();
88 7
        $firstEventStartAt = $event->getStartAt();
89
90 7
        if (isset($parts['UNTIL'])) {
91 2
            return $parts['UNTIL'];
92
        }
93
94 6
        if (isset($parts['COUNT'])) {
95 6
            return $firstEventStartAt->modify($this->getModifier($parts['FREQ'], (int) $parts['COUNT']));
96
        }
97
98
        throw new \BadMethodCallException('Not implemented recurring rule.');
99
    }
100
101 2
    private function changeDateToGivenDay(\DateTimeInterface $date, string $day)
102
    {
103 2
        $days = ['MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6, 'SU' => 0];
104
105 2
        $expectedDayNumber = $days[$day];
106 2
        $dateDayNumber = (int) $date->format('w');
107
108 2
        $diff = $expectedDayNumber - $dateDayNumber;
109
110 2
        if (0 === $diff) {
111 2
            return $date;
112
        }
113
114 2
        if ($diff < 0) {
115
            $diff += 7;
116
        }
117
118 2
        return $date->modify("+$diff days");
119
    }
120
121 7
    private function createEventFromRecurring(Event $event, \DateTimeInterface $from, \DateTimeInterface $to): Event
122
    {
123 7
        $newEvent = new Event($from, $to);
0 ignored issues
show
Compatibility introduced by
$from of type object<DateTimeInterface> is not a sub-type of object<DateTimeImmutable>. It seems like you assume a concrete implementation of the interface DateTimeInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Compatibility introduced by
$to of type object<DateTimeInterface> is not a sub-type of object<DateTimeImmutable>. It seems like you assume a concrete implementation of the interface DateTimeInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
124 7
        if (null !== $summary = $event->getSummary()) {
125 5
            $newEvent->setSummary($summary);
126
        }
127 7
        if (null !== $description = $event->getDescription()) {
128 6
            $newEvent->setDescription($description);
129
        }
130
131 7
        return $newEvent;
132
    }
133
}
134