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

EventsFlattener::flatten()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 8.064

Importance

Changes 0
Metric Value
dl 0
loc 39
ccs 18
cts 20
cp 0.9
rs 8.0515
c 0
b 0
f 0
cc 8
nc 6
nop 1
crap 8.064
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