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 LocalDate 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 LocalDate, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
25 | class LocalDate |
||
26 | { |
||
27 | /** @var \DateTime */ |
||
28 | private $date; |
||
29 | /** @var \DateTimeZone */ |
||
30 | private $timezone; |
||
31 | |||
32 | /** |
||
33 | * @param int $timestamp The unix timestamp |
||
34 | * @param \DateTimeZone|string $timezone The timezone or a string the DateTimeZone c'tor can understand |
||
35 | * |
||
36 | * @return LocalDate |
||
37 | */ |
||
38 | 109 | public static function fromTimestamp($timestamp, $timezone) |
|
42 | |||
43 | /** |
||
44 | * @return LocalDate |
||
45 | */ |
||
46 | 2 | public static function now() |
|
50 | |||
51 | /** |
||
52 | * @param \DateTime $dateTime |
||
53 | * |
||
54 | * @return LocalDate |
||
55 | */ |
||
56 | 18 | public static function raw(\DateTime $dateTime) |
|
67 | |||
68 | /** |
||
69 | * @param \DateTime|string|float|int $input The date or a string the DateTime c'tor can understand or a timestamp |
||
70 | * @param \DateTimeZone|string $timezone The timezone or a string the DateTimeZone c'tor can understand |
||
71 | */ |
||
72 | 226 | public function __construct($input, $timezone) |
|
117 | |||
118 | /** |
||
119 | * @return string |
||
120 | */ |
||
121 | 1 | public function __toString() |
|
125 | |||
126 | /** |
||
127 | * @return \DateTime |
||
128 | */ |
||
129 | 85 | public function getDate() |
|
133 | |||
134 | /** |
||
135 | * @return int |
||
136 | */ |
||
137 | 125 | public function getTimestamp() |
|
141 | |||
142 | /** |
||
143 | * @return \DateTimeZone |
||
144 | */ |
||
145 | 174 | public function getTimezone() |
|
149 | |||
150 | /** |
||
151 | * @return int |
||
152 | */ |
||
153 | 4 | public function getOffset() |
|
157 | |||
158 | /** |
||
159 | * @return float |
||
160 | */ |
||
161 | 1 | public function getOffsetInMinutes() |
|
167 | |||
168 | /** |
||
169 | * @return float |
||
170 | */ |
||
171 | 1 | public function getOffsetInHours() |
|
177 | |||
178 | /** |
||
179 | * @param string $format |
||
180 | * |
||
181 | * @return string |
||
182 | */ |
||
183 | 210 | public function format($format = 'c') |
|
187 | |||
188 | /** |
||
189 | * @return LocalDate |
||
190 | */ |
||
191 | 1 | public function getClone() |
|
195 | |||
196 | /** |
||
197 | * Get the time saving shift on this date in seconds |
||
198 | * |
||
199 | * @return int |
||
200 | */ |
||
201 | 1 | public function getDaylightSavingShift() |
|
207 | |||
208 | //// Daylight saving time shift aware methods ////////////////////////////////////////////////////////////////////// |
||
209 | |||
210 | /** |
||
211 | * Add hours to start of day while also respecting Daylight-saving-time shift |
||
212 | * |
||
213 | * E.g. on 2016-03-27T10:00:00 in Berlin is only 9 hours after the start of the day. |
||
214 | * |
||
215 | * @param $hours |
||
216 | * |
||
217 | * @return LocalDate |
||
218 | */ |
||
219 | 2 | public function getDstStartOfDayPlusHours($hours) |
|
229 | |||
230 | //// Modification methods ////////////////////////////////////////////////////////////////////////////////////////// |
||
231 | |||
232 | /** |
||
233 | * @return LocalDate |
||
234 | */ |
||
235 | 10 | public function getStartOfDay() |
|
239 | |||
240 | /** |
||
241 | * Get the nearest start of a day, either the current or the next day depending on the time in the day |
||
242 | * |
||
243 | * @return LocalDate |
||
244 | */ |
||
245 | 1 | public function getNearestStartOfDay() |
|
253 | |||
254 | /** |
||
255 | * @return LocalDate |
||
256 | */ |
||
257 | 2 | View Code Duplication | public function getStartOfPreviousDay() |
272 | |||
273 | /** |
||
274 | * @return LocalDate |
||
275 | */ |
||
276 | 2 | View Code Duplication | public function getStartOfNextDay() |
291 | |||
292 | /** |
||
293 | * @return LocalDate |
||
294 | */ |
||
295 | 1 | public function getEndOfDay() |
|
299 | |||
300 | /** |
||
301 | * @param int $hour |
||
302 | * @param int $minute |
||
303 | * @param int $second |
||
304 | * |
||
305 | * @return LocalDate |
||
306 | */ |
||
307 | 10 | public function modifyTime($hour, $minute, $second) |
|
319 | |||
320 | /** |
||
321 | * @param int $numSeconds |
||
322 | * |
||
323 | * @return LocalDate |
||
324 | */ |
||
325 | 94 | public function modifyBySeconds($numSeconds) |
|
332 | |||
333 | /** |
||
334 | * @param float $numMinutes |
||
335 | * |
||
336 | * @return LocalDate |
||
337 | */ |
||
338 | 19 | public function modifyByMinutes($numMinutes) |
|
342 | |||
343 | /** |
||
344 | * @param float $numHours |
||
345 | * |
||
346 | * @return LocalDate |
||
347 | */ |
||
348 | 24 | public function modifyByHours($numHours) |
|
352 | |||
353 | /** |
||
354 | * @param float $numDays |
||
355 | * |
||
356 | * @return LocalDate |
||
357 | */ |
||
358 | 32 | public function modifyByDays($numDays) |
|
362 | |||
363 | /** |
||
364 | * @param int $numDays |
||
365 | * |
||
366 | * @return LocalDate |
||
367 | */ |
||
368 | 3 | public function modifyByDaysDaylightSavingAware($numDays) |
|
386 | |||
387 | /** |
||
388 | * @param \DateInterval|string $interval |
||
389 | * |
||
390 | * @deprecated use addInterval or subInterval |
||
391 | * |
||
392 | * @return LocalDate |
||
393 | * |
||
394 | * @throws \Exception When the given param is not a valid date interval |
||
395 | */ |
||
396 | public function modifyByInterval($interval) |
||
400 | |||
401 | /** |
||
402 | * @param \DateInterval|string $interval |
||
403 | * |
||
404 | * @return LocalDate |
||
405 | * |
||
406 | * @throws \Exception When the given param is not a valid date interval |
||
407 | */ |
||
408 | 10 | View Code Duplication | public function addInterval($interval) |
419 | |||
420 | /** |
||
421 | * @param \DateInterval|string $interval |
||
422 | * |
||
423 | * @return LocalDate |
||
424 | * |
||
425 | * @throws \Exception When the given param is not a valid date interval |
||
426 | */ |
||
427 | 10 | View Code Duplication | public function subInterval($interval) |
438 | |||
439 | /** |
||
440 | * @param $minutesInterval |
||
441 | * |
||
442 | * @return LocalDate |
||
443 | */ |
||
444 | 1 | public function alignToMinutesInterval($minutesInterval) |
|
459 | |||
460 | /** |
||
461 | * @return int |
||
462 | */ |
||
463 | 1 | public function getMinutesIntoDay() |
|
470 | |||
471 | /** |
||
472 | * Get the hours portion of the current time |
||
473 | * |
||
474 | * @return int |
||
475 | */ |
||
476 | 1 | public function getHours() |
|
480 | |||
481 | /** |
||
482 | * Get the weekday with Sun = 0, Mon = 1, ... Sat = 6 |
||
483 | * |
||
484 | * @return int |
||
485 | */ |
||
486 | 1 | public function getWeekday() |
|
496 | |||
497 | //// COMPARISON METHODS ////////////////////////////////////////////////////////////////////////////////////////// |
||
498 | |||
499 | /** |
||
500 | * Returns the data that is earlier, either the current or the other |
||
501 | * |
||
502 | * @param LocalDate|\DateTime $other |
||
503 | * |
||
504 | * @return LocalDate |
||
505 | */ |
||
506 | 1 | public function min($other) |
|
512 | |||
513 | /** |
||
514 | * Returns the data that is later, either the current or the other |
||
515 | * |
||
516 | * @param LocalDate|\DateTime $other |
||
517 | * |
||
518 | * @return LocalDate |
||
519 | */ |
||
520 | 1 | public function max($other) |
|
526 | |||
527 | /** |
||
528 | * Get the number of seconds between this and the other. |
||
529 | * |
||
530 | * The result will be negative when this is after the other |
||
531 | * |
||
532 | * @param LocalDate|\DateTime $other |
||
533 | * |
||
534 | * @return float |
||
535 | */ |
||
536 | 82 | public function diffInSeconds($other) |
|
542 | |||
543 | /** |
||
544 | * Get the number of minutes between this and the other. |
||
545 | * |
||
546 | * The result will be negative when this is after the other |
||
547 | * |
||
548 | * @param LocalDate|\DateTime $other |
||
549 | * |
||
550 | * @return float |
||
551 | */ |
||
552 | 18 | public function diffInMinutes($other) |
|
558 | |||
559 | /** |
||
560 | * Get the number of minutes between this and the other. |
||
561 | * |
||
562 | * The result will be negative when this is after the other |
||
563 | * |
||
564 | * @param LocalDate|\DateTime $other |
||
565 | * |
||
566 | * @return float |
||
567 | */ |
||
568 | 18 | public function diffInHours($other) |
|
574 | |||
575 | /** |
||
576 | * Get the number of minutes between this and the other. |
||
577 | * |
||
578 | * The result will be negative when this is after the other |
||
579 | * |
||
580 | * @param LocalDate|\DateTime $other |
||
581 | * |
||
582 | * @return float |
||
583 | */ |
||
584 | 28 | public function diffInDays($other) |
|
590 | |||
591 | /** |
||
592 | * @param LocalDate|\DateTime $other |
||
593 | * |
||
594 | * @return bool |
||
595 | */ |
||
596 | 6 | public function isBefore($other) |
|
602 | |||
603 | /** |
||
604 | * @param LocalDate|\DateTime $other |
||
605 | * |
||
606 | * @return bool |
||
607 | */ |
||
608 | 1 | public function isBeforeOrEqual($other) |
|
614 | |||
615 | /** |
||
616 | * @param LocalDate|\DateTime $other |
||
617 | * |
||
618 | * @return bool |
||
619 | */ |
||
620 | 7 | public function isEqual($other) |
|
626 | |||
627 | /** |
||
628 | * @param LocalDate|\DateTime $other |
||
629 | * |
||
630 | * @return bool |
||
631 | */ |
||
632 | 1 | public function isAfter($other) |
|
638 | |||
639 | /** |
||
640 | * @param LocalDate|\DateTime $other |
||
641 | * |
||
642 | * @return bool |
||
643 | */ |
||
644 | 1 | public function isAfterOrEqual($other) |
|
650 | |||
651 | //// PRIVATE HELPER ////////////////////////////////////////////////////////////////////////////////////////////// |
||
652 | |||
653 | /** |
||
654 | * @param mixed $input |
||
655 | * |
||
656 | * @return LocalDate |
||
657 | */ |
||
658 | 98 | private function ensure($input) |
|
670 | } |
||
671 |
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.