Completed
Push — develop ( 0fe9d6...4e1ddc )
by Seth
03:00
created

Event::save()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 100
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 100
rs 8.2857
cc 3
eloc 46
nc 3
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace smtech\CanvasICSSync\SyncIntoCanvas;
4
5
use vevent;
6
use iCalUtilityFunctions;
7
use Michelf\Markdown;
8
9
class Event extends Syncable
0 ignored issues
show
Bug introduced by
There is at least one abstract method in this class. Maybe declare it as abstract, or implement the remaining methods: delete, getId
Loading history...
10
{
11
    /*
12
     * TODO:0 Can we detect the timezone for the Canvas instance and use it? issue:18
13
     */
14
    const LOCAL_TIMEZONE = 'US/Eastern';
15
    const CANVAS_TIMESTAMP_FORMAT = 'Y-m-d\TH:iP';
16
17
    const FIELD_MAP = [
18
        'calendar_event[title]' => 'SUMMARY',
19
        'calendar_event[description]' => 'DESCRIPTION',
20
        'calendar_event[start_at]' => [
21
            0 => 'X-CURRENT-DTSTART',
22
            1 => 'DTSTART'
23
        ],
24
        'calendar_event[end_at]' => [
25
            0 => 'X-CURRENT-DTEND',
26
            1 => 'DTEND'
27
        ],
28
        'calendar_event[location_name]' => 'LOCATION'
29
    ];
30
31
    /**
32
     * VEVENT
33
     * @var vevent
34
     */
35
    protected $vevent;
36
37
    /**
38
     * Calendar
39
     * @var Calendar
40
     */
41
    protected $calendar;
42
43
    /**
44
     * Unique hash identifying this version of this event
45
     * @var string
46
     */
47
    protected $hash;
48
49
    protected $canvasId;
50
51
    public function __construct(vevent $vevent, Calendar $calendar)
52
    {
53
        if (empty($vevent)) {
54
            throw new Exception(
55
                'Valid VEVENT required'
56
            );
57
        }
58
        $this->vevent = $vevent;
59
60
        if (empty($calendar)) {
61
            throw new Exception(
62
                'Valid Calendar required'
63
            );
64
        }
65
        $this->calendar = $calendar;
66
67
        $this->getHash();
68
    }
69
70
    public function getProperty($property)
71
    {
72
        return (empty($this->vevent) ? false : $this->vevent->getProperty($property));
73
    }
74
75
    public function getCalendar()
76
    {
77
        return $this->calendar;
78
    }
79
80
    /**
81
     * Generate a hash of this version of an event to cache in the database
82
     **/
83
    public function getHash($algorithm = 'md5')
84
    {
85
        if (empty($this->hash)) {
86
            $blob = '';
87
            foreach (static::$FIELD_MAP as $field) {
88
                if (is_array($field)) {
89
                    foreach ($field as $option) {
90
                        if (!empty($property = $this->getProperty($option))) {
91
                            $blob .= serialize($property);
92
                            break;
93
                        }
94
                    }
95
                } else {
96
                    if (!empty($property = $this->getProperty($field))) {
97
                        $blob .= serialize($property);
98
                    }
99
                }
100
            }
101
            $this->hash = hash($algorithm, $blob);
102
        }
103
        return $this->hash;
104
    }
105
106
    public function save()
107
    {
108
        $db = static::getDatabase();
109
        $api = static::getApi();
110
111
        $select = $db->prepare(
112
            "SELECT *
113
                FROM `events`
114
                WHERE
115
                    `event_hash` = :event_hash AND
116
                    `calendar` = :calendar"
117
        );
118
        $update = $db->prepare(
119
            "UPDATE `events`
120
                SET
121
                    `synced` = :synced
122
                WHERE
123
                    `event_hash` = :event_hash AND
124
                    `calendar` = :calendar"
125
        );
126
        $insert = $db->prepare(
127
            "INSERT INTO `events`
128
                (
129
                    `calendar`,
130
                    `calendar_event[id]`,
131
                    `event_hash`,
132
                    `synced`
133
                ) VALUES (
134
                    :calendar,
135
                    :calendar_event_id,
136
                    :event_hash,
137
                    :synced
138
                )"
139
        );
140
141
        $params = [
142
            'calendar' => $this->getCalendar()->getId(),
0 ignored issues
show
Bug introduced by
The method getId() cannot be called from this context as it is declared protected in class smtech\CanvasICSSync\SyncIntoCanvas\Calendar.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
143
            'event_hash' => $this->getHash(),
144
            'synced' => static::getTimestamp()
145
        ];
146
147
        $select->execute($params);
148
        if ($select->fetch() !== false) {
149
            $update->execute($params);
150
        } else {
151
            /*
152
             * FIXME: how sure are we of this? issue:14
153
             */
154
            /*
155
             * multi-day event instance start times need to be changed to
156
             * _this_ date
157
             */
158
            $start = new DateTime(
159
                iCalUtilityFunctions::_date2strdate(
160
                    $this->getProperty('DTSTART')
161
                )
162
            );
163
            $end = new DateTime(
164
                iCalUtilityFunctions::_date2strdate(
165
                    $this->getProperty('DTEND')
166
                )
167
            );
168
            if ($this->getProperty('X-RECURRENCE')) {
169
                $start = new DateTime($this->getProperty('X-CURRENT-DTSTART')[1]);
170
                $end = new DateTime($this->getProperty('X-CURRENT-DTEND')[1]);
171
            }
172
            $start->setTimeZone(new DateTimeZone(self::LOCAL_TIMEZONE));
173
            $end->setTimeZone(new DateTimeZone(self::LOCAL_TIMEZONE));
174
175
            $calendarEvent = $api->post(
176
                "/calendar_events",
177
                [
0 ignored issues
show
Documentation introduced by
array('calendar_event' =...tProperty('LOCATION'))) is of type array<string,array<strin...ation_name\":\"*\"}>"}>, but the function expects a string|array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
178
                    'calendar_event' => [
179
                        'context_code' => $this->getCalendar()->getContextCode(),
180
                        /*
181
                         * TODO this should be configurable issue:5
182
                         */
183
                        /* removing trailing [TAGS] from event title */
184
                        'title' => preg_replace(
185
                            '%^([^\]]+)(\s*\[[^\]]+\]\s*)+$%',
186
                            '\\1',
187
                            strip_tags($this->getProperty('SUMMARY'))
188
                        ),
189
                        'description' => Markdown::defaultTransform(
190
                            str_replace(
191
                                '\n',
192
                                "\n\n",
193
                                $this->getProperty('DESCRIPTION', 1)
0 ignored issues
show
Unused Code introduced by
The call to Event::getProperty() has too many arguments starting with 1.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
194
                            )
195
                        ),
196
                        'start_at' => $start->format(self::CANVAS_TIMESTAMP_FORMAT),
197
                        'end_at' => $end->format(self::CANVAS_TIMESTAMP_FORMAT),
198
                        'location_name' => $this->getProperty('LOCATION')
199
                    ]
200
                ]
201
            );
202
            $params['calendar_event_id'] = $calendarEvent['id'];
203
            $insert->execute($params);
204
        }
205
    }
206
207
    public static function purgeUnmatched($timestamp, $calendar)
208
    {
209
        $db = static::getDatabase();
210
        $api = static::getApi();
211
212
        $findDeletedEvents = $db->prepare(
213
            "SELECT *
214
                FROM `events`
215
                WHERE
216
                    `calendar` = :calendar AND
217
                    `synced` != :synced"
218
        );
219
        $deleteCachedEvent = $db->prepare(
220
            "DELETE FROM `events` WHERE `id` = :id"
221
        );
222
223
        $findDeletedEvents->execute([
224
            'calendar' => $calendar->getId(),
225
            'synced' => $timestamp
226
        ]);
227
        if (($deletedEvents = $findDeletedEvents->fetchAll()) !== false) {
228
            foreach ($deletedEvents as $event) {
229
                $params['cancel_reason'] = $timestamp;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
230
                if ($calendar->getContext()->getContext() == 'user') {
231
                    $params['as_user_id'] = $calendar->getContext()->getId();
0 ignored issues
show
Bug introduced by
The variable $params does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
232
                }
233
                try {
234
                    $api->delete(
235
                        "/calendar_events/{$event['calendar_event[id]']}",
236
                        $params
237
                    );
238
                } catch (Pest_Unauthorized $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
Bug introduced by
The class smtech\CanvasICSSync\Syn...anvas\Pest_Unauthorized does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
239
                    /*
240
                     * Do nothing: an event was cached that had been deleted
241
                     * from Canvas -- deleting the cached event is
242
                     * inconsequential
243
                     */
244
                }
245
                $deleteCachedEvent->execute($event['id']);
246
            }
247
        }
248
    }
249
}
250