Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Events 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 Events, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
22 | class Events { |
||
23 | |||
24 | /** |
||
25 | * Events. |
||
26 | * |
||
27 | * @access public |
||
28 | * @var array |
||
29 | */ |
||
30 | protected $events = array(); |
||
31 | |||
32 | /** |
||
33 | * Timezone. |
||
34 | * |
||
35 | * @access public |
||
36 | * @var string |
||
37 | */ |
||
38 | protected $timezone = 'UTC'; |
||
39 | |||
40 | /** |
||
41 | * Constructor. |
||
42 | * |
||
43 | * @since 3.0.0 |
||
44 | * |
||
45 | * @param array $e Events. |
||
46 | * @param string|\DateTimeZone $tz Timezone. |
||
47 | */ |
||
48 | public function __construct( $e = array(), $tz = 'UTC' ) { |
||
52 | |||
53 | /** |
||
54 | * Get events. |
||
55 | * |
||
56 | * @since 3.0.0 |
||
57 | * |
||
58 | * @param string|int $n Amount of events (optional). |
||
59 | * |
||
60 | * @return array |
||
61 | */ |
||
62 | public function get_events( $n = '' ) { |
||
69 | |||
70 | /** |
||
71 | * Set events. |
||
72 | * |
||
73 | * @since 3.0.0 |
||
74 | * |
||
75 | * @param array $ev Events. |
||
76 | */ |
||
77 | public function set_events( array $ev ) { |
||
80 | |||
81 | /** |
||
82 | * Set timezone. |
||
83 | * |
||
84 | * @since 3.0.0 |
||
85 | * |
||
86 | * @param string|\DateTimeZone $tz Timezone. |
||
87 | * |
||
88 | * @return Events |
||
89 | */ |
||
90 | public function set_timezone( $tz ) { |
||
97 | |||
98 | /** |
||
99 | * Shift events. |
||
100 | * |
||
101 | * @since 3.0.0 |
||
102 | * |
||
103 | * @param int $n |
||
104 | * |
||
105 | * @return Events |
||
106 | */ |
||
107 | public function shift( $n ) { |
||
115 | |||
116 | /** |
||
117 | * Filter private events. |
||
118 | * |
||
119 | * @since 3.0.0 |
||
120 | * |
||
121 | * @return Events |
||
122 | */ |
||
123 | public function private_only() { |
||
127 | |||
128 | /** |
||
129 | * Filter public events. |
||
130 | * |
||
131 | * @since 3.0.0 |
||
132 | * |
||
133 | * @return Events |
||
134 | */ |
||
135 | public function public_only() { |
||
139 | |||
140 | /** |
||
141 | * Filter recurring events in the current block. |
||
142 | * |
||
143 | * @since 3.0.0 |
||
144 | * |
||
145 | * @return Events |
||
146 | */ |
||
147 | public function recurring() { |
||
151 | |||
152 | /** |
||
153 | * Filter non recurring events in the current block. |
||
154 | * |
||
155 | * @since 3.0.0 |
||
156 | * |
||
157 | * @return Events |
||
158 | */ |
||
159 | public function not_recurring() { |
||
163 | |||
164 | /** |
||
165 | * Filter whole day events in the current block. |
||
166 | * |
||
167 | * @since 3.0.0 |
||
168 | * |
||
169 | * @return Events |
||
170 | */ |
||
171 | public function whole_day() { |
||
175 | |||
176 | /** |
||
177 | * Filter non whole day in the current block. |
||
178 | * |
||
179 | * @since 3.0.0 |
||
180 | * |
||
181 | * @return Events |
||
182 | */ |
||
183 | public function not_whole_day() { |
||
187 | |||
188 | /** |
||
189 | * Filter events spanning multiple days in the current block. |
||
190 | * |
||
191 | * @since 3.0.0 |
||
192 | * |
||
193 | * @return Events |
||
194 | */ |
||
195 | public function multi_day() { |
||
199 | |||
200 | /** |
||
201 | * Filter events that do not span multiple days in the current block. |
||
202 | * |
||
203 | * @since 3.0.0 |
||
204 | * |
||
205 | * @return Events |
||
206 | */ |
||
207 | public function single_day() { |
||
211 | |||
212 | /** |
||
213 | * Filter events in the current block that have a location. |
||
214 | * |
||
215 | * @since 3.0.0 |
||
216 | * |
||
217 | * @return Events |
||
218 | */ |
||
219 | public function with_location() { |
||
223 | |||
224 | /** |
||
225 | * Filter events in the current block that do not have a location. |
||
226 | * |
||
227 | * @since 3.0.0 |
||
228 | * |
||
229 | * @return Events |
||
230 | */ |
||
231 | public function without_location() { |
||
235 | |||
236 | /** |
||
237 | * Filter whole day events. |
||
238 | * |
||
239 | * @since 3.0.0 |
||
240 | * @access private |
||
241 | * |
||
242 | * @param string $property |
||
243 | * @param string $toggle |
||
244 | * |
||
245 | * @return array |
||
246 | */ |
||
247 | private function filter_property( $property, $toggle ) { |
||
266 | |||
267 | /** |
||
268 | * Filter events in the past. |
||
269 | * |
||
270 | * @since 3.0.0 |
||
271 | * |
||
272 | * @param int|string $present |
||
273 | * |
||
274 | * @return Events |
||
275 | */ |
||
276 | View Code Duplication | public function future( $present = '' ) { |
|
|
|||
277 | $last = $this->get_last(); |
||
278 | $to = $last instanceof Event ? $last->start_utc : false; |
||
279 | if ( $to ) { |
||
280 | if ( empty( $present ) ) { |
||
281 | $present = Carbon::now( $this->timezone )->getTimestamp(); |
||
282 | } |
||
283 | $this->set_events( $this->filter_events( intval( $present ), $to ) ); |
||
284 | } |
||
285 | return $this; |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Filter events in the future. |
||
290 | * |
||
291 | * @since 3.0.0 |
||
292 | * |
||
293 | * @param int|string $present |
||
294 | * |
||
295 | * @return Events |
||
296 | */ |
||
297 | View Code Duplication | public function past( $present = '' ) { |
|
298 | $first = $this->get_last(); |
||
299 | $from = $first instanceof Event ? $first->start_utc : false; |
||
300 | if ( $from ) { |
||
301 | if ( empty( $present ) ) { |
||
302 | $present = Carbon::now( $this->timezone )->getTimestamp(); |
||
303 | } |
||
304 | $this->set_events( $this->filter_events( $from, intval( $present ) ) ); |
||
305 | } |
||
306 | return $this; |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Filter events after time. |
||
311 | * |
||
312 | * @since 3.0.0 |
||
313 | * |
||
314 | * @param int|string|\DateTime|Carbon $time |
||
315 | * |
||
316 | * @return Events |
||
317 | */ |
||
318 | public function after( $time ) { |
||
322 | |||
323 | /** |
||
324 | * Filter events before time. |
||
325 | * |
||
326 | * @since 3.0.0 |
||
327 | * |
||
328 | * @param int|string|\DateTime|Carbon $time |
||
329 | * |
||
330 | * @return Events |
||
331 | */ |
||
332 | public function before( $time ) { |
||
336 | |||
337 | /** |
||
338 | * Filter events from a certain time onwards. |
||
339 | * |
||
340 | * @since 3.0.0 |
||
341 | * |
||
342 | * @param int|string|\DateTime|Carbon $time |
||
343 | * |
||
344 | * @return Events |
||
345 | */ |
||
346 | View Code Duplication | public function from( $time ) { |
|
347 | $last = $this->parse( $time ); |
||
348 | if ( ! is_null( $last ) ) { |
||
349 | $this->set_events( $this->filter_events( $time, $last->getTimestamp() ) ); |
||
350 | } |
||
351 | return $this; |
||
352 | } |
||
353 | |||
354 | /** |
||
355 | * Filter events up to to a certain time. |
||
356 | * |
||
357 | * @since 3.0.0 |
||
358 | * |
||
359 | * @param int|string|\DateTime|Carbon $time |
||
360 | * |
||
361 | * @return Events |
||
362 | */ |
||
363 | View Code Duplication | public function to( $time ) { |
|
364 | $first = $this->parse( $time ); |
||
365 | if ( ! is_null( $first ) ) { |
||
366 | $this->set_events( $this->filter_events( $first->getTimestamp(), $time ) ); |
||
367 | } |
||
368 | return $this; |
||
369 | } |
||
370 | |||
371 | /** |
||
372 | * Parse time. |
||
373 | * |
||
374 | * @since 3.0.0 |
||
375 | * |
||
376 | * @param int|string|\DateTime|Carbon $time |
||
377 | * |
||
378 | * @return null|Carbon |
||
379 | */ |
||
380 | private function parse( $time ) { |
||
392 | |||
393 | /** |
||
394 | * Get first event of the current block. |
||
395 | * |
||
396 | * @since 3.0.0 |
||
397 | * |
||
398 | * @return null|Event |
||
399 | */ |
||
400 | public function get_first() { |
||
403 | |||
404 | /** |
||
405 | * Get last event of the current block. |
||
406 | * |
||
407 | * @since 3.0.0 |
||
408 | * |
||
409 | * @return null|Event |
||
410 | */ |
||
411 | public function get_last() { |
||
414 | |||
415 | /** |
||
416 | * Get the closest event in the future. |
||
417 | * |
||
418 | * @since 3.0.0 |
||
419 | * |
||
420 | * @return null|Event |
||
421 | */ |
||
422 | public function get_upcoming() { |
||
425 | |||
426 | /** |
||
427 | * Get the closest event in the past. |
||
428 | * |
||
429 | * @since 3.0.0 |
||
430 | * |
||
431 | * @return null|Event |
||
432 | */ |
||
433 | public function get_latest() { |
||
436 | |||
437 | /** |
||
438 | * Get the closest event compared to now. |
||
439 | * |
||
440 | * @since 3.0.0 |
||
441 | * @access private |
||
442 | * |
||
443 | * @param string $dir Direction: 'future' or 'past'. |
||
444 | * |
||
445 | * @return null|Event |
||
446 | */ |
||
447 | private function get_closest( $dir ) { |
||
455 | |||
456 | /** |
||
457 | * Get events for the given year. |
||
458 | * |
||
459 | * @since 3.0.0 |
||
460 | * |
||
461 | * @param int $year Year. |
||
462 | * |
||
463 | * @return array Multidimensional array with month number, week number and Event objects for each weekday. |
||
464 | */ |
||
465 | public function get_year( $year ) { |
||
473 | |||
474 | /** |
||
475 | * Get events for the given month in the given year. |
||
476 | * |
||
477 | * @since 3.0.0 |
||
478 | * |
||
479 | * @param int $year Year. |
||
480 | * @param int $month Month number. |
||
481 | * |
||
482 | * @return array Multidimensional array with week number, day of the week and array of Event objects for each week day. |
||
483 | */ |
||
484 | public function get_month( $year, $month ) { |
||
497 | |||
498 | /** |
||
499 | * Get events for the given week in the given year. |
||
500 | * |
||
501 | * @since 3.0.0 |
||
502 | * |
||
503 | * @param int $year Year. |
||
504 | * @param int $week Week number. |
||
505 | * |
||
506 | * @return array Associative array with day of the week for key and array of Event objects for value. |
||
507 | */ |
||
508 | public function get_week( $year, $week ) { |
||
522 | |||
523 | /** |
||
524 | * Get events for the given day of the given month in the given year. |
||
525 | * |
||
526 | * @since 3.0.0 |
||
527 | * |
||
528 | * @param int $year Year. |
||
529 | * @param int $month Month number. |
||
530 | * @param int $day Day of the month number. |
||
531 | * |
||
532 | * @return array Event objects for the day. |
||
533 | */ |
||
534 | public function get_day( $year, $month, $day ) { |
||
542 | |||
543 | /** |
||
544 | * Get events for today. |
||
545 | * |
||
546 | * @since 3.0.0 |
||
547 | * |
||
548 | * @return array Event objects for today. |
||
549 | */ |
||
550 | public function get_today() { |
||
555 | |||
556 | /** |
||
557 | * Get events for tomorrow. |
||
558 | * |
||
559 | * @since 3.0.0 |
||
560 | * |
||
561 | * @return array Event objects for tomorrow. |
||
562 | */ |
||
563 | public function get_tomorrow() { |
||
568 | |||
569 | /** |
||
570 | * Get events for yesterday. |
||
571 | * |
||
572 | * @since 3.0.0 |
||
573 | * |
||
574 | * @return array Event objects for yesterday. |
||
575 | */ |
||
576 | public function get_yesterday() { |
||
581 | |||
582 | /** |
||
583 | * Filter events by timestamps. |
||
584 | * |
||
585 | * @since 3.0.0 |
||
586 | * @access private |
||
587 | * |
||
588 | * @param int $from Lower bound timestamp. |
||
589 | * @param int $to Upper bound timestamp. |
||
590 | * |
||
591 | * @return array Filtered array of Event objects. |
||
592 | */ |
||
593 | private function filter_events( $from, $to ) { |
||
604 | |||
605 | } |
||
606 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.