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 IPv6 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 IPv6, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
12 | class IPv6 implements AddressInterface |
||
13 | { |
||
14 | /** |
||
15 | * The long string representation of the address. |
||
16 | * |
||
17 | * @var string |
||
18 | * |
||
19 | * @example '0000:0000:0000:0000:0000:0000:0000:0001' |
||
20 | */ |
||
21 | protected $longAddress; |
||
22 | |||
23 | /** |
||
24 | * The long string representation of the address. |
||
25 | * |
||
26 | * @var string|null |
||
27 | * |
||
28 | * @example '::1' |
||
29 | */ |
||
30 | protected $shortAddress; |
||
31 | |||
32 | /** |
||
33 | * The byte list of the IP address. |
||
34 | * |
||
35 | * @var int[]|null |
||
36 | */ |
||
37 | protected $bytes; |
||
38 | |||
39 | /** |
||
40 | * The word list of the IP address. |
||
41 | * |
||
42 | * @var int[]|null |
||
43 | */ |
||
44 | protected $words; |
||
45 | |||
46 | /** |
||
47 | * The type of the range of this IP address. |
||
48 | * |
||
49 | * @var int|null |
||
50 | */ |
||
51 | protected $rangeType; |
||
52 | |||
53 | /** |
||
54 | * An array containing RFC designated address ranges. |
||
55 | * |
||
56 | * @var array|null |
||
57 | */ |
||
58 | private static $reservedRanges = null; |
||
59 | |||
60 | /** |
||
61 | * Initializes the instance. |
||
62 | * |
||
63 | * @param string $longAddress |
||
64 | */ |
||
65 | 477 | public function __construct($longAddress) |
|
73 | |||
74 | /** |
||
75 | * {@inheritdoc} |
||
76 | * |
||
77 | * @see \IPLib\Address\AddressInterface::__toString() |
||
78 | */ |
||
79 | 24 | public function __toString() |
|
83 | |||
84 | /** |
||
85 | * {@inheritdoc} |
||
86 | * |
||
87 | * @see \IPLib\Address\AddressInterface::getNumberOfBits() |
||
88 | */ |
||
89 | public static function getNumberOfBits() |
||
93 | 528 | ||
94 | /** |
||
95 | 528 | * Parse a string and returns an IPv6 instance if the string is valid, or null otherwise. |
|
96 | 528 | * |
|
97 | 490 | * @param string|mixed $address the address to parse |
|
98 | 490 | * @param bool $mayIncludePort set to false to avoid parsing addresses with ports |
|
99 | 2 | * @param bool $mayIncludeZoneID set to false to avoid parsing addresses with zone IDs (see RFC 4007) |
|
100 | * |
||
101 | 490 | * @return static|null |
|
102 | 490 | */ |
|
103 | 490 | public static function fromString($address, $mayIncludePort = true, $mayIncludeZoneID = true) |
|
171 | 305 | ||
172 | 305 | /** |
|
173 | 285 | * Parse an array of bytes and returns an IPv6 instance if the array is valid, or null otherwise. |
|
174 | 285 | * |
|
175 | 285 | * @param int[]|array $bytes |
|
176 | 285 | * |
|
177 | * @return static|null |
||
178 | 285 | */ |
|
179 | 285 | public static function fromBytes(array $bytes) |
|
203 | 56 | ||
204 | 56 | /** |
|
205 | 36 | * Parse an array of words and returns an IPv6 instance if the array is valid, or null otherwise. |
|
206 | 36 | * |
|
207 | 36 | * @param int[]|array $words |
|
208 | 36 | * |
|
209 | 36 | * @return static|null |
|
210 | */ |
||
211 | public static function fromWords(array $words) |
||
232 | |||
233 | 292 | /** |
|
234 | 292 | * {@inheritdoc} |
|
235 | 7 | * |
|
236 | 7 | * @see \IPLib\Address\AddressInterface::toString() |
|
237 | */ |
||
238 | 285 | public function toString($long = false) |
|
271 | 312 | ||
272 | 312 | /** |
|
273 | 312 | * {@inheritdoc} |
|
274 | * |
||
275 | 312 | * @see \IPLib\Address\AddressInterface::getBytes() |
|
276 | */ |
||
277 | public function getBytes() |
||
290 | 16 | ||
291 | /** |
||
292 | * {@inheritdoc} |
||
293 | 16 | * |
|
294 | * @see \IPLib\Address\AddressInterface::getBits() |
||
295 | */ |
||
296 | View Code Duplication | public function getBits() |
|
305 | 460 | ||
306 | 460 | /** |
|
307 | 460 | * Get the word list of the IP address. |
|
308 | 460 | * |
|
309 | * @return int[] |
||
310 | */ |
||
311 | public function getWords() |
||
324 | |||
325 | /** |
||
326 | * {@inheritdoc} |
||
327 | * |
||
328 | * @see \IPLib\Address\AddressInterface::getAddressType() |
||
329 | */ |
||
330 | 100 | public function getAddressType() |
|
334 | |||
335 | /** |
||
336 | * {@inheritdoc} |
||
337 | * |
||
338 | * @see \IPLib\Address\AddressInterface::getDefaultReservedRangeType() |
||
339 | */ |
||
340 | 116 | public static function getDefaultReservedRangeType() |
|
344 | |||
345 | /** |
||
346 | 1 | * {@inheritdoc} |
|
347 | * |
||
348 | * @see \IPLib\Address\AddressInterface::getReservedRanges() |
||
349 | */ |
||
350 | public static function getReservedRanges() |
||
414 | 59 | ||
415 | 6 | /** |
|
416 | * {@inheritdoc} |
||
417 | 53 | * |
|
418 | 53 | * @see \IPLib\Address\AddressInterface::getRangeType() |
|
419 | 53 | */ |
|
420 | 53 | public function getRangeType() |
|
440 | 19 | ||
441 | /** |
||
442 | 57 | * Create an IPv4 representation of this address (if possible, otherwise returns null). |
|
443 | * |
||
444 | 4 | * @return \IPLib\Address\IPv4|null |
|
445 | */ |
||
446 | public function toIPv4() |
||
459 | |||
460 | /** |
||
461 | * Render this IPv6 address in the "mixed" IPv6 (first 12 bytes) + IPv4 (last 4 bytes) mixed syntax. |
||
462 | * |
||
463 | * @param bool $ipV6Long render the IPv6 part in "long" format? |
||
464 | * @param bool $ipV4Long render the IPv4 part in "long" format? |
||
465 | 6 | * |
|
466 | * @return string |
||
467 | 6 | * |
|
468 | 6 | * @example '::13.1.68.3' |
|
469 | 6 | * @example '0000:0000:0000:0000:0000:0000:13.1.68.3' when $ipV6Long is true |
|
470 | 6 | * @example '::013.001.068.003' when $ipV4Long is true |
|
471 | 6 | * @example '0000:0000:0000:0000:0000:0000:013.001.068.003' when $ipV6Long and $ipV4Long are true |
|
472 | * |
||
473 | 6 | * @see https://tools.ietf.org/html/rfc4291#section-2.2 point 3. |
|
474 | */ |
||
475 | public function toMixedIPv6IPv4String($ipV6Long = false, $ipV4Long = false) |
||
485 | |||
486 | /** |
||
487 | * {@inheritdoc} |
||
488 | * |
||
489 | * @see \IPLib\Address\AddressInterface::getComparableString() |
||
490 | */ |
||
491 | 13 | public function getComparableString() |
|
495 | |||
496 | /** |
||
497 | * {@inheritdoc} |
||
498 | * |
||
499 | * @see \IPLib\Address\AddressInterface::matches() |
||
500 | */ |
||
501 | 8 | public function matches(RangeInterface $range) |
|
505 | 8 | ||
506 | 8 | /** |
|
507 | 5 | * {@inheritdoc} |
|
508 | 1 | * |
|
509 | 1 | * @see \IPLib\Address\AddressInterface::getNextAddress() |
|
510 | */ |
||
511 | 5 | View Code Duplication | public function getNextAddress() |
530 | 8 | ||
531 | 8 | /** |
|
532 | 5 | * {@inheritdoc} |
|
533 | 1 | * |
|
534 | 1 | * @see \IPLib\Address\AddressInterface::getPreviousAddress() |
|
535 | */ |
||
536 | 5 | View Code Duplication | public function getPreviousAddress() |
555 | 22 | ||
556 | 22 | /** |
|
557 | * {@inheritdoc} |
||
558 | * |
||
559 | * @see \IPLib\Address\AddressInterface::getReverseDNSLookupName() |
||
560 | */ |
||
561 | public function getReverseDNSLookupName() |
||
568 | } |
||
569 |
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.