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 CentralDirectoryHeader 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 CentralDirectoryHeader, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | final class CentralDirectoryHeader |
||
18 | { |
||
19 | const SIGNATURE = 0x504b0102; |
||
20 | |||
21 | /// Minimum length of this entry if neither file name, extra field nor file comment are set |
||
22 | const MIN_LENGTH = 46; |
||
23 | |||
24 | /// Maximum length of this entry file name, extra field and file comment have the maximum allowed length |
||
25 | const MAX_LENGTH = self::MIN_LENGTH + self::FILE_NAME_MAX_LENGTH + self::EXTRA_FIELD_MAX_LENGTH + self::FILE_COMMENT_MAX_LENGTH; |
||
26 | |||
27 | /// File name can not be longer than this (the length field has only 2 bytes) |
||
28 | const FILE_NAME_MAX_LENGTH = (255 * 255) - 1; |
||
29 | |||
30 | /// Extra field can not be longer than this (the length field has only 2 bytes) |
||
31 | const EXTRA_FIELD_MAX_LENGTH = (255 * 255) - 1; |
||
32 | |||
33 | /// File comment can not be longer than this (the length field has only 2 bytes) |
||
34 | const FILE_COMMENT_MAX_LENGTH = (255 * 255) - 1; |
||
35 | |||
36 | /** |
||
37 | * @var int |
||
38 | */ |
||
39 | private $versionMadeBy; |
||
40 | |||
41 | /** |
||
42 | * @var int |
||
43 | */ |
||
44 | private $versionNeededToExtract; |
||
45 | |||
46 | /** |
||
47 | * @var int |
||
48 | */ |
||
49 | private $generalPurposeBitFlags; |
||
50 | |||
51 | /** |
||
52 | * @var int |
||
53 | */ |
||
54 | private $compressionMethod; |
||
55 | |||
56 | /** |
||
57 | * @var int |
||
58 | */ |
||
59 | private $lastModificationFileTime; |
||
60 | |||
61 | /** |
||
62 | * @var int |
||
63 | */ |
||
64 | private $lastModificationFileDate; |
||
65 | |||
66 | /** |
||
67 | * @var int |
||
68 | */ |
||
69 | private $crc32; |
||
70 | |||
71 | /** |
||
72 | * @var int |
||
73 | */ |
||
74 | private $compressedSize; |
||
75 | |||
76 | /** |
||
77 | * @var int |
||
78 | */ |
||
79 | private $uncompressedSize; |
||
80 | |||
81 | /** |
||
82 | * @var int |
||
83 | */ |
||
84 | private $fileNameLength; |
||
85 | |||
86 | /** |
||
87 | * @var int |
||
88 | */ |
||
89 | private $extraFieldLength; |
||
90 | |||
91 | /** |
||
92 | * @var int |
||
93 | */ |
||
94 | private $fileCommentLength; |
||
95 | |||
96 | /** |
||
97 | * @var int |
||
98 | */ |
||
99 | private $diskNumberStart; |
||
100 | |||
101 | /** |
||
102 | * @var int |
||
103 | */ |
||
104 | private $internalFileAttributes; |
||
105 | |||
106 | /** |
||
107 | * @var int |
||
108 | */ |
||
109 | private $externalFileAttributes; |
||
110 | |||
111 | /** |
||
112 | * @var int |
||
113 | */ |
||
114 | private $relativeOffsetOfLocalHeader; |
||
115 | |||
116 | /** |
||
117 | * @var string |
||
118 | */ |
||
119 | private $fileName = ""; |
||
120 | |||
121 | /** |
||
122 | * @var string |
||
123 | */ |
||
124 | private $extraField = ""; |
||
125 | |||
126 | /** |
||
127 | * @var string |
||
128 | */ |
||
129 | private $fileComment = ""; |
||
130 | |||
131 | /** |
||
132 | * File system or operating system of encoder. |
||
133 | * One of the HOST_COMPATIBILITY_* constants. |
||
134 | * @var int |
||
135 | */ |
||
136 | private $encodingHost; |
||
137 | |||
138 | /** |
||
139 | * Maximum supported version of the encoding software. |
||
140 | * @var int |
||
141 | */ |
||
142 | private $encodingVersion; |
||
143 | |||
144 | /** |
||
145 | * Required host compatibility to decode. |
||
146 | * One of the HOST_COMPATIBILITY_* constants. |
||
147 | * @var int |
||
148 | */ |
||
149 | private $requiredHost; |
||
150 | |||
151 | /** |
||
152 | * Zip format version required to decode. |
||
153 | * @var int |
||
154 | */ |
||
155 | private $requiredVersion; |
||
156 | |||
157 | /** |
||
158 | * @var \DateTimeInterface |
||
159 | */ |
||
160 | private $lastModification; |
||
161 | |||
162 | /** |
||
163 | * @var int |
||
164 | */ |
||
165 | private $dosExternalAttributes; |
||
166 | |||
167 | /** |
||
168 | * @var int |
||
169 | */ |
||
170 | private $unixExternalAttributes; |
||
171 | |||
172 | /** |
||
173 | * @var bool |
||
174 | */ |
||
175 | private $requireAdditionalData = false; |
||
176 | |||
177 | /** |
||
178 | * @var ExtraFieldInterface[] |
||
179 | */ |
||
180 | private $extraFields = []; |
||
181 | |||
182 | 2051 | public function __construct( |
|
239 | |||
240 | /** |
||
241 | * Parse the binary representation of a central directory header from $input, optionally start at $offset instead of the beginning of the string. |
||
242 | * To complete the parsing process, parseAdditionalData() must be called with at least the number of bytes as input as returned by getRequireAdditionalData(). |
||
243 | * After the parseAdditionalData() call the object is immutable. |
||
244 | * |
||
245 | * @param string $input |
||
246 | * @param int $offset |
||
247 | * @return CentralDirectoryHeader |
||
248 | */ |
||
249 | 2048 | public static function parse(string $input, int $offset = 0) |
|
301 | |||
302 | /** |
||
303 | * After a new object has been created by parse(), this method must be called to initialize the file name, extra field and file comment entries which have dynamic field length. |
||
304 | * The required number of bytes is written to the $requireAdditionalData attribute by parse(). |
||
305 | * |
||
306 | * @param string $input |
||
307 | * @param int $offset |
||
308 | * @return int The number of bytes consumed (equals getVariableLength()) |
||
309 | */ |
||
310 | 2048 | public function parseAdditionalData(string $input, int $offset = 0) : int |
|
333 | |||
334 | /** |
||
335 | * Create the binary on disk representation |
||
336 | * |
||
337 | * @return string |
||
338 | */ |
||
339 | public function marshal() : string |
||
366 | |||
367 | /** |
||
368 | * Initialize a new central directory header from the supplied archive entry object |
||
369 | * |
||
370 | * @param ArchiveEntry $archiveEntry |
||
371 | * @return CentralDirectoryHeader |
||
372 | */ |
||
373 | public static function createFromArchiveEntry(ArchiveEntry $archiveEntry) : self |
||
398 | |||
399 | /** |
||
400 | * Create an archive entry from the data of this central directory header |
||
401 | * |
||
402 | * @return ArchiveEntry |
||
403 | */ |
||
404 | public function toArchiveEntry() : ArchiveEntry |
||
420 | |||
421 | /** |
||
422 | * The number of bytes the fields with variable length require. |
||
423 | * |
||
424 | * @return int |
||
425 | */ |
||
426 | 2048 | public function getVariableLength(): int |
|
430 | |||
431 | /** |
||
432 | * @return int |
||
433 | */ |
||
434 | 1 | public function getVersionMadeBy(): int |
|
438 | |||
439 | /** |
||
440 | * @param int $versionMadeBy |
||
441 | * @return CentralDirectoryHeader |
||
442 | */ |
||
443 | 1 | public function setVersionMadeBy(int $versionMadeBy): CentralDirectoryHeader |
|
449 | |||
450 | /** |
||
451 | * @return int |
||
452 | */ |
||
453 | 1 | public function getVersionNeededToExtract(): int |
|
457 | |||
458 | /** |
||
459 | * @param int $versionNeededToExtract |
||
460 | * @return CentralDirectoryHeader |
||
461 | */ |
||
462 | 1 | public function setVersionNeededToExtract(int $versionNeededToExtract): CentralDirectoryHeader |
|
468 | |||
469 | /** |
||
470 | * @return int |
||
471 | */ |
||
472 | 1 | public function getGeneralPurposeBitFlags(): int |
|
476 | |||
477 | /** |
||
478 | * @param int $generalPurposeBitFlags |
||
479 | * @return CentralDirectoryHeader |
||
480 | */ |
||
481 | 1 | public function setGeneralPurposeBitFlags(int $generalPurposeBitFlags): CentralDirectoryHeader |
|
487 | |||
488 | /** |
||
489 | * @return int |
||
490 | */ |
||
491 | 376 | public function getCompressionMethod(): int |
|
495 | |||
496 | /** |
||
497 | * @param int $compressionMethod |
||
498 | * @return CentralDirectoryHeader |
||
499 | */ |
||
500 | 1 | public function setCompressionMethod(int $compressionMethod): CentralDirectoryHeader |
|
506 | |||
507 | /** |
||
508 | * @return int |
||
509 | */ |
||
510 | 1 | public function getLastModificationFileTime(): int |
|
514 | |||
515 | /** |
||
516 | * @param int $lastModificationFileTime |
||
517 | * @return CentralDirectoryHeader |
||
518 | */ |
||
519 | 1 | public function setLastModificationFileTime(int $lastModificationFileTime): CentralDirectoryHeader |
|
525 | |||
526 | /** |
||
527 | * @return int |
||
528 | */ |
||
529 | 1 | public function getLastModificationFileDate(): int |
|
533 | |||
534 | /** |
||
535 | * @param int $lastModificationFileDate |
||
536 | * @return CentralDirectoryHeader |
||
537 | */ |
||
538 | 1 | public function setLastModificationFileDate(int $lastModificationFileDate): CentralDirectoryHeader |
|
544 | |||
545 | /** |
||
546 | * @return int |
||
547 | */ |
||
548 | 55 | public function getCrc32(): int |
|
552 | |||
553 | /** |
||
554 | * @param int $crc32 |
||
555 | * @return CentralDirectoryHeader |
||
556 | */ |
||
557 | 1 | public function setCrc32(int $crc32): CentralDirectoryHeader |
|
563 | |||
564 | /** |
||
565 | * @return int |
||
566 | */ |
||
567 | 1623 | public function getCompressedSize(): int |
|
571 | |||
572 | /** |
||
573 | * @param int $compressedSize |
||
574 | * @return CentralDirectoryHeader |
||
575 | */ |
||
576 | 1 | public function setCompressedSize(int $compressedSize): CentralDirectoryHeader |
|
582 | |||
583 | /** |
||
584 | * @return int |
||
585 | */ |
||
586 | 1137 | public function getUncompressedSize(): int |
|
590 | |||
591 | /** |
||
592 | * @param int $uncompressedSize |
||
593 | * @return CentralDirectoryHeader |
||
594 | */ |
||
595 | 1 | public function setUncompressedSize(int $uncompressedSize): CentralDirectoryHeader |
|
601 | |||
602 | /** |
||
603 | * @return int |
||
604 | */ |
||
605 | 1 | public function getFileNameLength(): int |
|
609 | |||
610 | /** |
||
611 | * @return int |
||
612 | */ |
||
613 | 1 | public function getExtraFieldLength(): int |
|
617 | |||
618 | /** |
||
619 | * @return int |
||
620 | */ |
||
621 | 1 | public function getFileCommentLength(): int |
|
625 | |||
626 | /** |
||
627 | * @return int |
||
628 | */ |
||
629 | 1083 | public function getDiskNumberStart(): int |
|
633 | |||
634 | /** |
||
635 | * @param int $diskNumberStart |
||
636 | * @return CentralDirectoryHeader |
||
637 | */ |
||
638 | 1 | public function setDiskNumberStart(int $diskNumberStart): CentralDirectoryHeader |
|
644 | |||
645 | /** |
||
646 | * @return int |
||
647 | */ |
||
648 | 1 | public function getInternalFileAttributes(): int |
|
652 | |||
653 | /** |
||
654 | * @param int $internalFileAttributes |
||
655 | * @return CentralDirectoryHeader |
||
656 | */ |
||
657 | 1 | public function setInternalFileAttributes(int $internalFileAttributes): CentralDirectoryHeader |
|
663 | |||
664 | /** |
||
665 | * @return int |
||
666 | */ |
||
667 | 55 | public function getExternalFileAttributes(): int |
|
671 | |||
672 | /** |
||
673 | * @param int $externalFileAttributes |
||
674 | * @return CentralDirectoryHeader |
||
675 | */ |
||
676 | 1 | public function setExternalFileAttributes(int $externalFileAttributes): CentralDirectoryHeader |
|
682 | |||
683 | /** |
||
684 | * @return int |
||
685 | */ |
||
686 | 1404 | public function getRelativeOffsetOfLocalHeader(): int |
|
690 | |||
691 | /** |
||
692 | * @param int $relativeOffsetOfLocalHeader |
||
693 | * @return CentralDirectoryHeader |
||
694 | */ |
||
695 | 1 | public function setRelativeOffsetOfLocalHeader(int $relativeOffsetOfLocalHeader): CentralDirectoryHeader |
|
701 | |||
702 | /** |
||
703 | * @return string |
||
704 | */ |
||
705 | 1892 | public function getFileName(): string |
|
709 | |||
710 | /** |
||
711 | * @param string $fileName |
||
712 | * @return CentralDirectoryHeader |
||
713 | */ |
||
714 | 1 | View Code Duplication | public function setFileName(string $fileName): CentralDirectoryHeader |
721 | |||
722 | /** |
||
723 | * @return string |
||
724 | */ |
||
725 | 1 | public function getExtraField(): string |
|
729 | |||
730 | /** |
||
731 | * @param string $extraField |
||
732 | * @return CentralDirectoryHeader |
||
733 | */ |
||
734 | 1 | View Code Duplication | public function setExtraField(string $extraField): CentralDirectoryHeader |
741 | |||
742 | /** |
||
743 | * @return string |
||
744 | */ |
||
745 | 73 | public function getFileComment(): string |
|
749 | |||
750 | /** |
||
751 | * @param string $fileComment |
||
752 | * @return CentralDirectoryHeader |
||
753 | */ |
||
754 | 1 | public function setFileComment(string $fileComment): CentralDirectoryHeader |
|
761 | |||
762 | /** |
||
763 | * @return int |
||
764 | */ |
||
765 | 55 | public function getEncodingHost(): int |
|
769 | |||
770 | /** |
||
771 | * @param int $encodingHost |
||
772 | * @return CentralDirectoryHeader |
||
773 | */ |
||
774 | 1 | public function setEncodingHost(int $encodingHost): CentralDirectoryHeader |
|
780 | |||
781 | /** |
||
782 | * @return int |
||
783 | */ |
||
784 | 1 | public function getEncodingVersion(): int |
|
788 | |||
789 | /** |
||
790 | * @param int $encodingVersion |
||
791 | * @return CentralDirectoryHeader |
||
792 | */ |
||
793 | 1 | public function setEncodingVersion(int $encodingVersion): CentralDirectoryHeader |
|
799 | |||
800 | /** |
||
801 | * @return int |
||
802 | */ |
||
803 | 1 | public function getRequiredHost(): int |
|
807 | |||
808 | /** |
||
809 | * @param int $requiredHost |
||
810 | * @return CentralDirectoryHeader |
||
811 | */ |
||
812 | 1 | public function setRequiredHost(int $requiredHost): CentralDirectoryHeader |
|
818 | |||
819 | /** |
||
820 | * @return int |
||
821 | */ |
||
822 | 1 | public function getRequiredVersion(): int |
|
826 | |||
827 | /** |
||
828 | * @param int $requiredVersion |
||
829 | * @return CentralDirectoryHeader |
||
830 | */ |
||
831 | 1 | public function setRequiredVersion(int $requiredVersion): CentralDirectoryHeader |
|
837 | |||
838 | /** |
||
839 | * @return \DateTimeInterface |
||
840 | */ |
||
841 | 54 | public function getLastModification(): \DateTimeInterface |
|
845 | |||
846 | /** |
||
847 | * @param \DateTimeInterface $lastModification |
||
848 | * @return CentralDirectoryHeader |
||
849 | */ |
||
850 | public function setLastModification(\DateTimeInterface $lastModification): CentralDirectoryHeader |
||
856 | |||
857 | /** |
||
858 | * @return int |
||
859 | */ |
||
860 | 1 | public function getDosExternalAttributes(): int |
|
864 | |||
865 | /** |
||
866 | * @param int $dosExternalAttributes |
||
867 | * @return CentralDirectoryHeader |
||
868 | */ |
||
869 | 1 | public function setDosExternalAttributes(int $dosExternalAttributes): CentralDirectoryHeader |
|
875 | |||
876 | /** |
||
877 | * @return int|null |
||
878 | */ |
||
879 | 1 | public function getUnixExternalAttributes() |
|
883 | |||
884 | /** |
||
885 | * @param int $unixExternalAttributes |
||
886 | * @return CentralDirectoryHeader |
||
887 | */ |
||
888 | 1 | public function setUnixExternalAttributes(int $unixExternalAttributes): CentralDirectoryHeader |
|
894 | |||
895 | /** |
||
896 | * Whether this entry represents a directory or not |
||
897 | * |
||
898 | * @return bool |
||
899 | */ |
||
900 | 822 | public function isDirectory() : bool |
|
904 | } |
||
905 |
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.