SimpleCalendarMini   B
last analyzed

Complexity

Total Complexity 52

Size/Duplication

Total Lines 338
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 137
c 1
b 0
f 0
dl 0
loc 338
rs 7.44
wmc 52

13 Methods

Rating   Name   Duplication   Size   Complexity  
A rotate() 0 8 3
A __construct() 0 4 1
A show() 0 7 2
A setStartOfWeek() 0 12 5
A setWeekDayNames() 0 6 4
A setToday() 0 7 3
A parseDate() 0 12 4
A addDailyHtml() 0 30 6
A weekdays() 0 12 3
A setCalendarClasses() 0 7 3
F render() 0 87 15
A setDate() 0 2 2
A clearDailyHtml() 0 1 1

How to fix   Complexity   

Complex Class

Complex classes like SimpleCalendarMini 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.

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 SimpleCalendarMini, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace XoopsModules\Wgevents\SimpleCalendar;
4
5
/**
6
 * Simple Calendar
7
 *
8
 * @author Jesse G. Donat <[email protected]>
9
 * @see http://donatstudios.com
10
 * @license http://opensource.org/licenses/mit-license.php
11
 */
12
class SimpleCalendarMini {
13
14
    /**
15
     * Array of Week Day Names
16
     *
17
     * @var string[]|null
18
     */
19
    private $weekDayNames;
20
21
    /**
22
     * @var \DateTimeInterface
23
     */
24
    private $now;
25
26
    /**
27
     * @var \DateTimeInterface|null
28
     */
29
    private $today;
30
31
    private $classes = [
32
        'calendar'     => 'SimpleCalendar',
33
        'leading_day'  => 'SCprefix',
34
        'trailing_day' => 'SCsuffix',
35
        'today'        => 'today',
36
        'event'        => 'event',
37
        'events'       => 'events',
38
    ];
39
40
    private $dailyHtml = [];
41
    private $offset = 0;
42
43
    /**
44
     * @param \DateTimeInterface|int|string|null $calendarDate
45
     * @param \DateTimeInterface|false|int|string|null $today
46
     *
47
     * @throws \Exception
48
     * @throws \Exception
49
     * @see setToday
50
     * @see setDate
51
     */
52
    public function __construct( $calendarDate = null, $today = null ) {
53
        $this->setDate($calendarDate);
54
        $this->setToday($today);
55
        $this->setCalendarClasses();
56
    }
57
58
    /**
59
     * Sets the date for the calendar.
60
     *
61
     * @param \DateTimeInterface|int|string|null $date DateTimeInterface or Date string parsed by strtotime for the
62
     *     calendar date. If null set to current timestamp.
63
     * @throws \Exception
64
     */
65
    public function setDate( $date = null ) {
66
        $this->now = $this->parseDate($date) ?: new \DateTimeImmutable();
67
    }
68
69
    /**
70
     * @param \DateTimeInterface|int|string|null $date
71
     * @return \DateTimeInterface|null
72
     * @throws \Exception
73
     */
74
    private function parseDate( $date = null ) {
75
        if( $date instanceof \DateTimeInterface ) {
76
            return $date;
77
        }
78
        if( \is_int($date) ) {
79
            return (new \DateTimeImmutable())->setTimestamp($date);
80
        }
81
        if( \is_string($date) ) {
82
            return new \DateTimeImmutable($date);
83
        }
84
85
        return null;
86
    }
87
88
    /**
89
     * Sets the class names used in the calendar
90
     *
91
     * ```php
92
     * [
93
     *    'calendar'     => 'SimpleCalendar',
94
     *    'leading_day'  => 'SCprefix',
95
     *    'trailing_day' => 'SCsuffix',
96
     *    'today'        => 'today',
97
     *    'event'        => 'event',
98
     *    'events'       => 'events',
99
     * ]
100
     * ```
101
     *
102
     * @param array $classes Map of element to class names used by the calendar.
103
     */
104
    public function setCalendarClasses( array $classes = [] ) {
105
        foreach( $classes as $key => $value ) {
106
            if( !isset($this->classes[$key]) ) {
107
                throw new \InvalidArgumentException("class '$key' not supported");
108
            }
109
110
            $this->classes[$key] = $value;
111
        }
112
    }
113
114
    /**
115
     * Sets "today"'s date. Defaults to today.
116
     *
117
     * @param \DateTimeInterface|false|string|null $today `null` will default to today, `false` will disable the
118
     *     rendering of Today.
119
     * @throws \Exception
120
     */
121
    public function setToday( $today = null ) {
122
        if( $today === false ) {
123
            $this->today = null;
124
        } elseif( $today === null ) {
125
            $this->today = new \DateTimeImmutable();
126
        } else {
127
            $this->today = $this->parseDate($today);
128
        }
129
    }
130
131
    /**
132
     * @param string[]|null $weekDayNames
133
     */
134
    public function setWeekDayNames( array $weekDayNames = null ) {
135
        if( \is_array($weekDayNames) && \count($weekDayNames) !== 7 ) {
136
            throw new \InvalidArgumentException('week array must have exactly 7 values');
137
        }
138
139
        $this->weekDayNames = $weekDayNames ? \array_values($weekDayNames) : null;
140
    }
141
142
    /**
143
     * Add a daily event to the calendar
144
     *
145
     * @param string $html The raw HTML to place on the calendar for this event
146
     * @param \DateTimeInterface|int|string $startDate Date string for when the event starts
147
     * @param null $endDate Date string for when the event ends. Defaults to start date
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $endDate is correct as it would always require null to be passed?
Loading history...
148
     * @throws \Exception
149
     */
150
    public function addDailyHtml(string $html, $startDate, $endDate = null) {
151
        static $htmlCount = 0;
152
153
        $start = $this->parseDate($startDate);
154
        if( !$start ) {
0 ignored issues
show
introduced by
$start is of type DateTimeInterface, thus it always evaluated to true.
Loading history...
155
            throw new \InvalidArgumentException('invalid start time');
156
        }
157
158
        $end = $start;
159
        if( $endDate ) {
0 ignored issues
show
introduced by
$endDate is of type null, thus it always evaluated to false.
Loading history...
160
            $end = $this->parseDate($endDate);
161
        }
162
        if( !$end ) {
0 ignored issues
show
introduced by
$end is of type DateTimeInterface, thus it always evaluated to true.
Loading history...
163
            throw new \InvalidArgumentException('invalid end time');
164
        }
165
166
        if( $end->getTimestamp() < $start->getTimestamp() ) {
167
            throw new \InvalidArgumentException('end must come after start');
168
        }
169
170
        $working = (new \DateTimeImmutable())->setTimestamp($start->getTimestamp());
171
        do {
172
            $tDate = getdate($working->getTimestamp());
173
174
            $this->dailyHtml[$tDate['year']][$tDate['mon']][$tDate['mday']][$htmlCount] = $html;
175
176
            $working = $working->add(new \DateInterval('P1D'));
177
        } while( $working->getTimestamp() < $end->getTimestamp() + 1 );
178
179
        $htmlCount++;
180
    }
181
182
    /**
183
     * Clear all daily events for the calendar
184
     */
185
    public function clearDailyHtml() { $this->dailyHtml = []; }
186
187
    /**
188
     * Sets the first day of the week
189
     *
190
     * @param $offset Day the week starts on. ex: "Monday" or 0-6 where 0 is Sunday
0 ignored issues
show
Bug introduced by
The type XoopsModules\Wgevents\SimpleCalendar\Day was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
191
     */
192
    public function setStartOfWeek($offset) {
193
        if( \is_int($offset) ) {
194
            $this->offset = $offset % 7;
195
        } elseif( $this->weekDayNames !== null && ($weekOffset = \array_search($offset, $this->weekDayNames, true)) !== false ) {
196
            $this->offset = $weekOffset;
197
        } else {
198
            $weekTime = \strtotime($offset);
199
            if( $weekTime === 0 ) {
200
                throw new \InvalidArgumentException('invalid offset');
201
            }
202
203
            $this->offset = date('N', $weekTime) % 7;
204
        }
205
    }
206
207
    /**
208
     * Returns/Outputs the Calendar
209
     *
210
     * @param bool $echo Whether to echo resulting calendar
211
     * @return string HTML of the Calendar
212
     * @throws \Exception
213
     * @throws \Exception
214
     * @deprecated Use `render()` method instead.
215
     */
216
    public function show( $echo = true ) {
217
        $out = $this->render();
218
        if( $echo ) {
219
            echo $out;
220
        }
221
222
        return $out;
223
    }
224
225
    /**
226
     * Returns the generated Calendar
227
     *
228
     * @return string
229
     * @throws \Exception
230
     */
231
    public function render() {
232
        $now   = getdate($this->now->getTimestamp());
233
        $today = [ 'mday' => -1, 'mon' => -1, 'year' => -1 ];
234
        if( $this->today !== null ) {
235
            $today = getdate($this->today->getTimestamp());
236
        }
237
238
        $daysOfWeek = $this->weekdays();
239
        $this->rotate($daysOfWeek, $this->offset);
240
241
        $weekDayIndex = date('N', \mktime(0, 0, 1, $now['mon'], 1, $now['year'])) - $this->offset;
242
        $daysInMonth  = cal_days_in_month(CAL_GREGORIAN, $now['mon'], $now['year']);
243
244
        $out = <<<TAG
245
<table cellpadding="0" cellspacing="0" class="{$this->classes['calendar']}"><thead><tr>
246
TAG;
247
248
        foreach( $daysOfWeek as $dayName ) {
249
            $out .= "<th>$dayName</th>";
250
        }
251
252
        $out .= <<<'TAG'
253
</tr></thead>
254
<tbody>
255
<tr>
256
TAG;
257
258
        $weekDayIndex = ($weekDayIndex + 7) % 7;
259
260
        if( $weekDayIndex === 7 ) {
261
            $weekDayIndex = 0;
262
        } else {
263
            $out .= \str_repeat(<<<TAG
264
<td class="{$this->classes['leading_day']}">&nbsp;</td>
265
TAG
266
                , $weekDayIndex);
267
        }
268
269
        $count = $weekDayIndex + 1;
270
        for( $i = 1; $i <= $daysInMonth; $i++ ) {
271
            $date = (new \DateTimeImmutable())->setDate($now['year'], $now['mon'], $i);
272
273
            $isToday = false;
274
            if( $this->today !== null ) {
275
                $isToday = $i == $today['mday']
276
                    && $today['mon'] == $date->format('n')
277
                    && $today['year'] == $date->format('Y');
278
            }
279
280
            $out .= '<td' . ($isToday ? ' class="' . $this->classes['today'] . '"' : '') . '>';
281
282
            // line removed by goffy
283
            //$out .= \sprintf('<time datetime="%s">%d</time>', $date->format('Y-m-d'), $i);
284
285
            $dailyHTML = null;
286
            if( isset($this->dailyHtml[$now['year']][$now['mon']][$i]) ) {
287
                $dailyHTML = $this->dailyHtml[$now['year']][$now['mon']][$i];
288
            }
289
290
            if( \is_array($dailyHTML) ) {
291
                //new line by goffy
292
                $out .= \sprintf('<time datetime="%s"><span class="badge badge-primary">%d</span></time>', $date->format('Y-m-d'), $i);
293
                $out .= '<div class="' . $this->classes['events'] . '">';
294
                foreach( $dailyHTML as $dHtml ) {
295
                    $out .= \sprintf('<div class="%s">%s</div>', $this->classes['event'], $dHtml);
296
                }
297
                $out .= '</div>';
298
            } else {
299
                $out .= \sprintf('<time datetime="%s">%d</time>', $date->format('Y-m-d'), $i);
300
            }
301
302
            $out .= '</td>';
303
304
            if( $count > 6 ) {
305
                $out   .= "</tr>\n" . ($i < $daysInMonth ? '<tr>' : '');
306
                $count = 0;
307
            }
308
            $count++;
309
        }
310
311
        if( $count !== 1 ) {
312
            $out .= \str_repeat('<td class="' . $this->classes['trailing_day'] . '">&nbsp;</td>', 8 - $count) . '</tr>';
313
        }
314
315
        $out .= "\n</tbody></table>\n";
316
317
        return $out;
318
    }
319
320
    /**
321
     * @param array $data
322
     * @param int $steps
323
     */
324
    private function rotate(array &$data, int $steps) {
325
        $count = \count($data);
326
        if( $steps < 0 ) {
327
            $steps = $count + $steps;
328
        }
329
        $steps %= $count;
330
        for( $i = 0; $i < $steps; $i++ ) {
331
            $data[] = \array_shift($data);
332
        }
333
    }
334
335
    /**
336
     * @return string[]
337
     */
338
    private function weekdays() {
339
        if( $this->weekDayNames !== null ) {
340
            $wDays = $this->weekDayNames;
341
        } else {
342
            $today = (86400 * (date('N')));
343
            $wDays = [];
344
            for( $n = 0; $n < 7; $n++ ) {
345
                $wDays[] = \strftime('%a', \time() - $today + ($n * 86400));
346
            }
347
        }
348
349
        return $wDays;
350
    }
351
352
}
353