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 ArchiveEntry 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 ArchiveEntry, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | final class ArchiveEntry |
||
19 | { |
||
20 | const UNIX_ATTRIBUTES_DEFAULT = UNIX_ATTRIBUTE_USER_READ|UNIX_ATTRIBUTE_USER_WRITE|UNIX_ATTRIBUTE_GROUP_READ|UNIX_ATTRIBUTE_OTHER_READ; |
||
21 | const UNIX_ATTRIBUTES_DEFAULT_FILE = self::UNIX_ATTRIBUTES_DEFAULT|UNIX_ATTRIBUTE_TYPE_FILE; |
||
22 | const UNIX_ATTRIBUTES_DEFAULT_DIRECTORY = self::UNIX_ATTRIBUTES_DEFAULT|UNIX_ATTRIBUTE_TYPE_DIRECTORY|UNIX_ATTRIBUTE_USER_EXECUTE|UNIX_ATTRIBUTE_GROUP_EXECUTE|UNIX_ATTRIBUTE_OTHER_EXECUTE; |
||
23 | |||
24 | /** |
||
25 | * The name of this entry, relative to the archive root or absolute/fully qualified |
||
26 | * @var string |
||
27 | */ |
||
28 | private $name; |
||
29 | |||
30 | /** |
||
31 | * @var int |
||
32 | */ |
||
33 | private $uncompressedSize; |
||
34 | |||
35 | /** |
||
36 | * @var int |
||
37 | */ |
||
38 | private $sourceSize; |
||
39 | |||
40 | /** |
||
41 | * @var int |
||
42 | */ |
||
43 | private $targetSize; |
||
44 | |||
45 | /** |
||
46 | * @var \DateTimeInterface |
||
47 | */ |
||
48 | private $creationTime; |
||
49 | |||
50 | /** |
||
51 | * @var \DateTimeInterface |
||
52 | */ |
||
53 | private $modificationTime; |
||
54 | |||
55 | /** |
||
56 | * CRC32 checksum of the uncompressed file |
||
57 | * @var int |
||
58 | */ |
||
59 | private $checksumCrc32; |
||
60 | |||
61 | /** |
||
62 | * @var string |
||
63 | */ |
||
64 | private $comment; |
||
65 | |||
66 | /** |
||
67 | * One of the COMPRESSION_METHOD_* constants |
||
68 | * @var string |
||
69 | */ |
||
70 | private $sourceCompressionMethod; |
||
71 | |||
72 | /** |
||
73 | * One of the COMPRESSION_METHOD_* constants |
||
74 | * @var string |
||
75 | */ |
||
76 | private $targetCompressionMethod; |
||
77 | |||
78 | /** |
||
79 | * Combination of DOS_ATTRIBUTE_* flags |
||
80 | * May be unsupported by target file format |
||
81 | * @var int |
||
82 | */ |
||
83 | private $dosAttributes; |
||
84 | |||
85 | /** |
||
86 | * Combination of UNIX_ATTRIBUTE_* flags |
||
87 | * May be unsupported by target file format |
||
88 | * @var int |
||
89 | */ |
||
90 | private $unixAttributes; |
||
91 | |||
92 | /** |
||
93 | * A PHP stream to the underlying file |
||
94 | * The stream should not perform any translation (like decompression) |
||
95 | * so reading from this stream should yield data in the format |
||
96 | * specified by $sourceCompressionMethod |
||
97 | * @var resource |
||
98 | */ |
||
99 | private $sourceStream; |
||
100 | |||
101 | /** |
||
102 | * Fully qualified path to the underlying uncompressed file |
||
103 | * @var string |
||
104 | */ |
||
105 | private $sourcePath; |
||
106 | |||
107 | /** |
||
108 | * Binary string containing the content of this entry |
||
109 | * @var string |
||
110 | */ |
||
111 | private $sourceString; |
||
112 | |||
113 | 2 | public function __construct(string $name) |
|
117 | |||
118 | /** |
||
119 | * Import data from the supplied $stat array. Format is the one returned by stat()/fstat() |
||
120 | * @param array $stat |
||
121 | */ |
||
122 | 3 | private function importStat(array $stat) |
|
153 | |||
154 | /** |
||
155 | * @return resource |
||
156 | */ |
||
157 | 3 | public function getSourceAsStream() |
|
174 | |||
175 | /** |
||
176 | * @return string|null |
||
177 | */ |
||
178 | 2 | public function getSourcePath() |
|
182 | |||
183 | /** |
||
184 | * Set the path of the file that will serve as source for this entry. |
||
185 | * The file must exist and be readable. |
||
186 | * You may provide a URL if a matching stream wrapper is registered with stat support. |
||
187 | * Otherwise, use withSourceStream() to add a stream directly. |
||
188 | * |
||
189 | * @param string $path |
||
190 | * @param string|null $compressionMethod |
||
191 | * @return ArchiveEntry |
||
192 | */ |
||
193 | 6 | public function withSourcePath(string $path, string $compressionMethod = null) : self |
|
220 | |||
221 | /** |
||
222 | * @return resource|null |
||
223 | */ |
||
224 | 1 | public function getSourceStream() |
|
228 | |||
229 | /** |
||
230 | * @param resource $stream |
||
231 | * @param string $compressionMethod |
||
232 | * @return ArchiveEntry |
||
233 | */ |
||
234 | 4 | public function withSourceStream($stream, string $compressionMethod = null) : self |
|
255 | |||
256 | /** |
||
257 | * @return string|null |
||
258 | */ |
||
259 | 1 | public function getSourceString() |
|
263 | |||
264 | /** |
||
265 | * @param string $content |
||
266 | * @param string $compressionMethod |
||
267 | * @return ArchiveEntry |
||
268 | */ |
||
269 | 4 | public function withSourceString(string $content, string $compressionMethod = null) : self |
|
300 | |||
301 | /** |
||
302 | * @return string|null |
||
303 | */ |
||
304 | 2 | public function getName() |
|
308 | |||
309 | /** |
||
310 | * @param string $name |
||
311 | * @return ArchiveEntry |
||
312 | */ |
||
313 | 1 | public function withName(string $name) : self |
|
319 | |||
320 | /** |
||
321 | * @return int|null |
||
322 | */ |
||
323 | 1 | public function getUncompressedSize() |
|
327 | |||
328 | /** |
||
329 | * @param int $uncompressedSize |
||
330 | * @return ArchiveEntry |
||
331 | */ |
||
332 | 1 | public function withUncompressedSize(int $uncompressedSize) : self |
|
338 | |||
339 | /** |
||
340 | * @return int|null |
||
341 | */ |
||
342 | 1 | public function getSourceSize() |
|
346 | |||
347 | /** |
||
348 | * @param int $sourceSize |
||
349 | * @return ArchiveEntry |
||
350 | */ |
||
351 | 1 | public function withSourceSize(int $sourceSize) : self |
|
357 | |||
358 | /** |
||
359 | * @return int|null |
||
360 | */ |
||
361 | 1 | public function getTargetSize() |
|
365 | |||
366 | /** |
||
367 | * @param int $targetSize |
||
368 | * @return ArchiveEntry |
||
369 | */ |
||
370 | 1 | public function withTargetSize(int $targetSize) : self |
|
376 | |||
377 | /** |
||
378 | * @return \DateTimeInterface|null |
||
379 | */ |
||
380 | 1 | public function getCreationTime() |
|
384 | |||
385 | /** |
||
386 | * @param \DateTimeInterface $creationTime |
||
387 | * @return ArchiveEntry |
||
388 | */ |
||
389 | 1 | public function withCreationTime(\DateTimeInterface $creationTime) : self |
|
395 | |||
396 | /** |
||
397 | * @return \DateTimeInterface|null |
||
398 | */ |
||
399 | 1 | public function getModificationTime() |
|
403 | |||
404 | /** |
||
405 | * @param \DateTimeInterface $modificationTime |
||
406 | * @return ArchiveEntry |
||
407 | */ |
||
408 | 1 | public function withModificationTime(\DateTimeInterface $modificationTime) : self |
|
414 | |||
415 | /** |
||
416 | * @return int|null |
||
417 | */ |
||
418 | 1 | public function getChecksumCrc32() |
|
422 | |||
423 | /** |
||
424 | * @param int $checksumCrc32 |
||
425 | * @return ArchiveEntry |
||
426 | */ |
||
427 | 1 | public function withChecksumCrc32(int $checksumCrc32) : self |
|
433 | |||
434 | /** |
||
435 | * @return string|null |
||
436 | */ |
||
437 | 1 | public function getComment() |
|
441 | |||
442 | /** |
||
443 | * @param string|null $comment |
||
444 | * @return ArchiveEntry |
||
445 | */ |
||
446 | 2 | public function withComment(string $comment = null) : self |
|
452 | |||
453 | /** |
||
454 | * @return string|null |
||
455 | */ |
||
456 | 1 | public function getSourceCompressionMethod() |
|
460 | |||
461 | /** |
||
462 | * Any of the COMPRESSION_METHOD_* constants |
||
463 | * |
||
464 | * @param string $sourceCompressionMethod |
||
465 | * @return ArchiveEntry |
||
466 | */ |
||
467 | 1 | public function withSourceCompressionMethod($sourceCompressionMethod) : self |
|
473 | |||
474 | /** |
||
475 | * @return string|null |
||
476 | */ |
||
477 | 1 | public function getTargetCompressionMethod() |
|
481 | |||
482 | /** |
||
483 | * @param string $targetCompressionMethod |
||
484 | * @return ArchiveEntry |
||
485 | */ |
||
486 | 1 | public function withTargetCompressionMethod($targetCompressionMethod) : self |
|
492 | |||
493 | /** |
||
494 | * @return int|null |
||
495 | */ |
||
496 | 1 | public function getDosAttributes() |
|
500 | |||
501 | /** |
||
502 | * @param int $dosAttributes |
||
503 | * @return ArchiveEntry |
||
504 | */ |
||
505 | 1 | public function withDosAttributes(int $dosAttributes) : self |
|
511 | |||
512 | /** |
||
513 | * @return int|null |
||
514 | */ |
||
515 | 1 | public function getUnixAttributes() |
|
519 | |||
520 | /** |
||
521 | * @param int $unixAttributes |
||
522 | * @return ArchiveEntry |
||
523 | */ |
||
524 | 1 | public function withUnixAttributes(int $unixAttributes) : self |
|
530 | } |
||
531 |
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.