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 DatetimeField 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 DatetimeField, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | class DatetimeField extends TextField |
||
19 | { |
||
20 | |||
21 | /** |
||
22 | * @var bool |
||
23 | */ |
||
24 | protected $html5 = true; |
||
25 | |||
26 | /** |
||
27 | * Override locale. If empty will default to current locale |
||
28 | * |
||
29 | * @var string |
||
30 | */ |
||
31 | protected $locale = null; |
||
32 | |||
33 | /** |
||
34 | * Min date time |
||
35 | * |
||
36 | * @var string ISO 8601 date time in server timezone |
||
37 | */ |
||
38 | protected $minDatetime = null; |
||
39 | |||
40 | /** |
||
41 | * Max date time |
||
42 | * |
||
43 | * @var string ISO 860 date time in server timezone |
||
44 | */ |
||
45 | protected $maxDatetime = null; |
||
46 | |||
47 | /** |
||
48 | * Override date format. If empty will default to that used by the current locale. |
||
49 | * |
||
50 | * @var null |
||
51 | */ |
||
52 | protected $datetimeFormat = null; |
||
53 | |||
54 | /** |
||
55 | * Length of this date (full, short, etc). |
||
56 | * |
||
57 | * @see http://php.net/manual/en/class.intldateformatter.php#intl.intldateformatter-constants |
||
58 | * @var int |
||
59 | */ |
||
60 | protected $dateLength = null; |
||
61 | |||
62 | /** |
||
63 | * Length of this time (full, short, etc). |
||
64 | * |
||
65 | * @see http://php.net/manual/en/class.intldateformatter.php#intl.intldateformatter-constants |
||
66 | * @var int |
||
67 | */ |
||
68 | protected $timeLength = null; |
||
69 | |||
70 | /** |
||
71 | * Unparsed value, used exclusively for comparing with internal value |
||
72 | * to detect invalid values. |
||
73 | * |
||
74 | * @var mixed |
||
75 | */ |
||
76 | protected $rawValue = null; |
||
77 | |||
78 | /** |
||
79 | * @inheritDoc |
||
80 | */ |
||
81 | protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_DATETIME; |
||
82 | |||
83 | /** |
||
84 | * Custom timezone |
||
85 | * |
||
86 | * @var string |
||
87 | */ |
||
88 | protected $timezone = null; |
||
89 | |||
90 | View Code Duplication | public function getAttributes() |
|
104 | |||
105 | /** |
||
106 | * @inheritDoc |
||
107 | */ |
||
108 | View Code Duplication | public function getSchemaDataDefaults() |
|
120 | |||
121 | /** |
||
122 | * @inheritDoc |
||
123 | */ |
||
124 | public function Type() |
||
128 | |||
129 | /** |
||
130 | * @return bool |
||
131 | */ |
||
132 | public function getHTML5() |
||
136 | |||
137 | /** |
||
138 | * @param $bool |
||
139 | * @return $this |
||
140 | */ |
||
141 | public function setHTML5($bool) |
||
146 | |||
147 | /** |
||
148 | * Assign value posted from form submission, based on {@link $datetimeFormat}. |
||
149 | * When $html5=true, this needs to be normalised ISO format (with "T" separator). |
||
150 | * |
||
151 | * @param mixed $value |
||
152 | * @param mixed $data |
||
153 | * @return $this |
||
154 | */ |
||
155 | View Code Duplication | public function setSubmittedValue($value, $data = null) |
|
171 | |||
172 | /** |
||
173 | * Convert frontend date to the internal representation (ISO 8601). |
||
174 | * The frontend date is also in ISO 8601 when $html5=true. |
||
175 | * Assumes the value is in the defined {@link $timezone} (if one is set), |
||
176 | * and adjusts for server timezone. |
||
177 | * |
||
178 | * @param string $datetime |
||
179 | * @return string The formatted date, or null if not a valid date |
||
180 | */ |
||
181 | View Code Duplication | public function frontendToInternal($datetime) |
|
204 | |||
205 | /** |
||
206 | * Get date formatter with the standard locale / date format |
||
207 | * |
||
208 | * @throws \LogicException |
||
209 | * @return IntlDateFormatter |
||
210 | */ |
||
211 | protected function getFrontendFormatter() |
||
251 | |||
252 | /** |
||
253 | * Get date format in CLDR standard format |
||
254 | * |
||
255 | * This can be set explicitly. If not, this will be generated from the current locale |
||
256 | * with the current date length. |
||
257 | * |
||
258 | * @see http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Field-Symbol-Table |
||
259 | */ |
||
260 | public function getDatetimeFormat() |
||
269 | |||
270 | /** |
||
271 | * Set date format in CLDR standard format. |
||
272 | * Only applicable with {@link setHTML5(false)}. |
||
273 | * |
||
274 | * @see http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Field-Symbol-Table |
||
275 | * @param string $format |
||
276 | * @return $this |
||
277 | */ |
||
278 | public function setDatetimeFormat($format) |
||
283 | |||
284 | /** |
||
285 | * Get a date formatter for the ISO 8601 format |
||
286 | * |
||
287 | * @param String $timezone Optional timezone identifier (defaults to server timezone) |
||
288 | * @return IntlDateFormatter |
||
289 | */ |
||
290 | protected function getInternalFormatter($timezone = null) |
||
309 | |||
310 | /** |
||
311 | * Assign value based on {@link $datetimeFormat}, which might be localised. |
||
312 | * The value needs to be in the server timezone. |
||
313 | * |
||
314 | * When $html5=true, assign value from ISO 8601 normalised string (with a "T" separator). |
||
315 | * Falls back to an ISO 8601 string (with a whitespace separator). |
||
316 | * |
||
317 | * @param mixed $value |
||
318 | * @param mixed $data |
||
319 | * @return $this |
||
320 | */ |
||
321 | public function setValue($value, $data = null) |
||
356 | |||
357 | /** |
||
358 | * Returns the frontend representation of the field value, |
||
359 | * according to the defined {@link dateFormat}. |
||
360 | * With $html5=true, this will be in ISO 8601 format. |
||
361 | * |
||
362 | * @return string |
||
363 | */ |
||
364 | public function Value() |
||
368 | |||
369 | /** |
||
370 | * Convert the internal date representation (ISO 8601) to a format used by the frontend, |
||
371 | * as defined by {@link $dateFormat}. With $html5=true, the frontend date will also be |
||
372 | * in ISO 8601. |
||
373 | * |
||
374 | * @param string $datetime |
||
375 | * @return string The formatted date and time, or null if not a valid date and time |
||
376 | */ |
||
377 | View Code Duplication | public function internalToFrontend($datetime) |
|
392 | |||
393 | /** |
||
394 | * Tidy up the internal date representation (ISO 8601), |
||
395 | * and fall back to strtotime() if there's parsing errors. |
||
396 | * |
||
397 | * @param string $date Date in ISO 8601 or approximate form |
||
398 | * @return string ISO 8601 date, or null if not valid |
||
399 | */ |
||
400 | View Code Duplication | public function tidyInternal($datetime) |
|
417 | |||
418 | /** |
||
419 | * Get length of the date format to use. One of: |
||
420 | * |
||
421 | * - IntlDateFormatter::SHORT |
||
422 | * - IntlDateFormatter::MEDIUM |
||
423 | * - IntlDateFormatter::LONG |
||
424 | * - IntlDateFormatter::FULL |
||
425 | * |
||
426 | * @see http://php.net/manual/en/class.intldateformatter.php#intl.intldateformatter-constants |
||
427 | * @return int |
||
428 | */ |
||
429 | public function getDateLength() |
||
436 | |||
437 | /** |
||
438 | * Get length of the date format to use. |
||
439 | * Only applicable with {@link setHTML5(false)}. |
||
440 | * |
||
441 | * @see http://php.net/manual/en/class.intldateformatter.php#intl.intldateformatter-constants |
||
442 | * |
||
443 | * @param int $length |
||
444 | * @return $this |
||
445 | */ |
||
446 | public function setDateLength($length) |
||
451 | |||
452 | /** |
||
453 | * Get length of the date format to use. One of: |
||
454 | * |
||
455 | * - IntlDateFormatter::SHORT |
||
456 | * - IntlDateFormatter::MEDIUM |
||
457 | * - IntlDateFormatter::LONG |
||
458 | * - IntlDateFormatter::FULL |
||
459 | * |
||
460 | * @see http://php.net/manual/en/class.intldateformatter.php#intl.intldateformatter-constants |
||
461 | * @return int |
||
462 | */ |
||
463 | public function getTimeLength() |
||
470 | |||
471 | /** |
||
472 | * Get length of the date format to use. |
||
473 | * Only applicable with {@link setHTML5(false)}. |
||
474 | * |
||
475 | * @see http://php.net/manual/en/class.intldateformatter.php#intl.intldateformatter-constants |
||
476 | * |
||
477 | * @param int $length |
||
478 | * @return $this |
||
479 | */ |
||
480 | public function setTimeLength($length) |
||
485 | |||
486 | public function setDisabled($bool) |
||
491 | |||
492 | public function setReadonly($bool) |
||
497 | |||
498 | /** |
||
499 | * Set default locale for this field. If omitted will default to the current locale. |
||
500 | * |
||
501 | * @param string $locale |
||
502 | * @return $this |
||
503 | */ |
||
504 | public function setLocale($locale) |
||
509 | |||
510 | /** |
||
511 | * Get locale for this field |
||
512 | * |
||
513 | * @return string |
||
514 | */ |
||
515 | public function getLocale() |
||
519 | |||
520 | /** |
||
521 | * @return string Date in ISO 8601 format, in server timezone. |
||
522 | */ |
||
523 | public function getMinDatetime() |
||
527 | |||
528 | /** |
||
529 | * @param string $minDatetime A string in ISO 8601 format, in server timezone. |
||
530 | * @return $this |
||
531 | */ |
||
532 | public function setMinDatetime($minDatetime) |
||
537 | |||
538 | /** |
||
539 | * @return string Date in ISO 8601 format, in server timezone. |
||
540 | */ |
||
541 | public function getMaxDatetime() |
||
545 | |||
546 | /** |
||
547 | * @param string $maxDatetime A string in ISO 8601 format, in server timezone. |
||
548 | * @return $this |
||
549 | */ |
||
550 | public function setMaxDatetime($maxDatetime) |
||
555 | |||
556 | /** |
||
557 | * @param Validator $validator |
||
558 | * @return bool |
||
559 | */ |
||
560 | View Code Duplication | public function validate($validator) |
|
632 | |||
633 | public function performReadonlyTransformation() |
||
639 | |||
640 | /** |
||
641 | * @return string |
||
642 | */ |
||
643 | public function getTimezone() |
||
647 | |||
648 | /** |
||
649 | * @param string $timezone |
||
650 | * @return $this |
||
651 | */ |
||
652 | View Code Duplication | public function setTimezone($timezone) |
|
662 | } |
||
663 |
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.