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 SymmetricCrypt 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 SymmetricCrypt, and based on these observations, apply Extract Interface, too.
1 | <?php namespace Limoncello\Crypt; |
||
28 | class SymmetricCrypt extends BaseCrypt implements EncryptInterface, DecryptInterface |
||
29 | { |
||
30 | /** |
||
31 | * @var string |
||
32 | */ |
||
33 | private $method; |
||
34 | |||
35 | /** |
||
36 | * @var string |
||
37 | */ |
||
38 | private $password; |
||
39 | |||
40 | /** |
||
41 | * Such as OPENSSL_RAW_DATA, OPENSSL_ZERO_PADDING. |
||
42 | * |
||
43 | * @var int |
||
44 | */ |
||
45 | private $options = 0; |
||
46 | |||
47 | /** |
||
48 | * @var string |
||
49 | */ |
||
50 | private $initializationVector = ''; |
||
51 | |||
52 | // Authenticated Encryption with Associated Data options (since PHP 7.1) |
||
53 | 3 | ||
54 | /** |
||
55 | 3 | * Use Authenticated Encryption with Associated Data (since PHP 7.1) |
|
56 | 3 | * |
|
57 | * @var bool |
||
58 | */ |
||
59 | private $useAuthentication = false; |
||
60 | |||
61 | 1 | /** |
|
62 | * Additional authentication data. |
||
63 | 1 | * |
|
64 | * @var string |
||
65 | 1 | */ |
|
66 | private $aad = ''; |
||
67 | 1 | ||
68 | /** |
||
69 | 1 | * The length of the authentication tag. Its value can be between 4 and 16 for GCM (Galois/Counter Mode) mode. |
|
70 | 1 | * |
|
71 | 1 | * @var int |
|
72 | 1 | */ |
|
73 | private $tagLength = 16; |
||
74 | |||
75 | 1 | /** |
|
76 | * @param string $method |
||
77 | 1 | * @param string $password |
|
78 | */ |
||
79 | public function __construct(string $method, string $password) |
||
83 | 3 | ||
84 | /** |
||
85 | 3 | * @inheritdoc |
|
86 | * |
||
87 | 3 | * @SuppressWarnings(PHPMD.ElseExpression) |
|
88 | */ |
||
89 | 3 | public function decrypt(string $data): string |
|
126 | |||
127 | 3 | /** |
|
128 | * @inheritdoc |
||
129 | 3 | * |
|
130 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
131 | */ |
||
132 | public function encrypt(string $data): string |
||
185 | 1 | ||
186 | /** |
||
187 | * @return string |
||
188 | */ |
||
189 | public function getPassword(): string |
||
193 | 1 | ||
194 | /** |
||
195 | * @return string |
||
196 | */ |
||
197 | public function getMethod(): string |
||
201 | 3 | ||
202 | /** |
||
203 | * @param string $method |
||
204 | * |
||
205 | * @return self |
||
206 | */ |
||
207 | 3 | public function setMethod(string $method): self |
|
218 | |||
219 | 3 | /** |
|
220 | * @param string $password |
||
221 | 3 | * |
|
222 | * @return self |
||
223 | 3 | */ |
|
224 | public function setPassword(string $password): self |
||
232 | |||
233 | 2 | /** |
|
234 | * @param string $value |
||
235 | * |
||
236 | * @return self |
||
237 | */ |
||
238 | public function setIV(string $value): self |
||
244 | |||
245 | 3 | /** |
|
246 | * @return string |
||
247 | 3 | */ |
|
248 | public function getIV(): string |
||
252 | |||
253 | /** |
||
254 | * @return self |
||
255 | 3 | */ |
|
256 | public function withZeroPadding(): self |
||
260 | |||
261 | 3 | /** |
|
262 | * @return self |
||
263 | */ |
||
264 | public function withoutZeroPadding(): self |
||
268 | |||
269 | /** |
||
270 | * @return bool |
||
271 | */ |
||
272 | public function isUseAuthentication(): bool |
||
276 | |||
277 | 1 | /** |
|
278 | * Authenticated Encryption with Associated Data available for certain methods since PHP 7.1. |
||
279 | * |
||
280 | * @return self |
||
281 | */ |
||
282 | public function enableAuthentication(): self |
||
288 | |||
289 | /** |
||
290 | * Authenticated Encryption with Associated Data available for certain methods since PHP 7.1. |
||
291 | 2 | * |
|
292 | * @return self |
||
293 | 2 | */ |
|
294 | public function disableAuthentication(): self |
||
300 | |||
301 | /** |
||
302 | * @return string |
||
303 | */ |
||
304 | public function getAdditionalAuthenticationData(): string |
||
308 | |||
309 | /** |
||
310 | * @param string $data |
||
311 | * |
||
312 | * @return self |
||
313 | */ |
||
314 | public function setAdditionalAuthenticationData(string $data): self |
||
320 | |||
321 | /** |
||
322 | * @return int |
||
323 | */ |
||
324 | public function getTagLength(): int |
||
328 | |||
329 | /** |
||
330 | * @param int $length |
||
331 | * |
||
332 | * @return self |
||
333 | */ |
||
334 | public function setTagLength(int $length): self |
||
342 | |||
343 | /** |
||
344 | * @return self |
||
345 | */ |
||
346 | protected function asRaw(): self |
||
350 | |||
351 | /** |
||
352 | * @return int |
||
353 | */ |
||
354 | protected function getOptions(): int |
||
358 | |||
359 | /** |
||
360 | * @param int $options |
||
361 | * |
||
362 | * @return self |
||
363 | */ |
||
364 | protected function setOptions(int $options): self |
||
370 | |||
371 | /** |
||
372 | * @return string |
||
373 | */ |
||
374 | protected function generateIV(): string |
||
383 | |||
384 | /** |
||
385 | * @param int $option |
||
386 | * |
||
387 | * @return self |
||
388 | */ |
||
389 | protected function setOption(int $option): self |
||
395 | |||
396 | /** |
||
397 | * @param int $option |
||
398 | * |
||
399 | * @return self |
||
400 | */ |
||
401 | protected function clearOption(int $option): self |
||
407 | |||
408 | /** |
||
409 | * @param string $data |
||
410 | * @param int $ivLength |
||
411 | * |
||
412 | * @return string |
||
413 | */ |
||
414 | View Code Duplication | protected function readIV(string $data, int $ivLength): string |
|
423 | |||
424 | /** |
||
425 | * @param string $data |
||
426 | * @param int $tagLength |
||
427 | * |
||
428 | * @return string |
||
429 | */ |
||
430 | protected function readTag(string $data, int $tagLength): string |
||
439 | |||
440 | /** |
||
441 | * @param string $data |
||
442 | * @param int $ivLength |
||
443 | * |
||
444 | * @return string |
||
445 | */ |
||
446 | View Code Duplication | protected function extractData(string $data, int $ivLength): string |
|
455 | |||
456 | /** |
||
457 | * @param string $data |
||
458 | * @param string $method |
||
459 | * @param string $password |
||
460 | * @param int $options |
||
461 | * @param string $initializationVector |
||
462 | * |
||
463 | * @return string |
||
464 | */ |
||
465 | View Code Duplication | protected function openSslEncrypt( |
|
479 | |||
480 | /** |
||
481 | * @param string $data |
||
482 | * @param string $method |
||
483 | * @param string $password |
||
484 | * @param int $options |
||
485 | * @param string $initializationVector |
||
486 | * |
||
487 | * @return string |
||
488 | */ |
||
489 | View Code Duplication | protected function openSslDecrypt( |
|
502 | |||
503 | /** @noinspection PhpTooManyParametersInspection |
||
504 | * @param string $data |
||
505 | * @param string $method |
||
506 | * @param string $password |
||
507 | * @param int $options |
||
508 | * @param string $initializationVector |
||
509 | * @param string $aad |
||
510 | * @param string|null &$tag |
||
511 | * @param int $tagLength |
||
512 | * |
||
513 | * @return string |
||
514 | */ |
||
515 | protected function openSslEncryptAuthenticated( |
||
541 | |||
542 | /** |
||
543 | * @param string $data |
||
544 | * @param string $method |
||
545 | * @param string $password |
||
546 | * @param int $options |
||
547 | * @param string $initializationVector |
||
548 | * @param string $aad |
||
549 | * @param string $tag |
||
550 | * |
||
551 | * @return string |
||
552 | */ |
||
553 | View Code Duplication | protected function openSslDecryptAuthenticated( |
|
569 | |||
570 | /** |
||
571 | * @param string $method |
||
572 | * |
||
573 | * @return int |
||
574 | */ |
||
575 | protected function openSslIvLength(string $method): int |
||
583 | |||
584 | /** |
||
585 | * @return string |
||
586 | */ |
||
587 | protected function getReadVectorErrorMessage(): string |
||
591 | |||
592 | /** |
||
593 | * @return string |
||
594 | */ |
||
595 | protected function getReadTagErrorMessage(): string |
||
599 | |||
600 | /** |
||
601 | * @return string |
||
602 | */ |
||
603 | protected function getExtractDataErrorMessage(): string |
||
607 | |||
608 | /** |
||
609 | * We need this wrapper for testing purposes so we can mock system call to Open SSL. |
||
610 | * |
||
611 | * @param string $data |
||
612 | * @param string $method |
||
613 | * @param string $password |
||
614 | * @param int $options |
||
615 | * @param string $initializationVector |
||
616 | * |
||
617 | * @return string|false |
||
618 | */ |
||
619 | protected function openSslEncryptImpl( |
||
628 | |||
629 | /** |
||
630 | * We need this wrapper for testing purposes so we can mock system call to Open SSL. |
||
631 | * |
||
632 | * @param string $data |
||
633 | * @param string $method |
||
634 | * @param string $password |
||
635 | * @param int $options |
||
636 | * @param string $initializationVector |
||
637 | * |
||
638 | * @return string|false |
||
639 | */ |
||
640 | protected function openSslDecryptImpl( |
||
649 | |||
650 | /** @noinspection PhpTooManyParametersInspection |
||
651 | * We need this wrapper for testing purposes so we can mock system call to Open SSL. |
||
652 | * |
||
653 | * @param string $data |
||
654 | * @param string $method |
||
655 | * @param string $password |
||
656 | * @param int $options |
||
657 | * @param string $initializationVector |
||
658 | * @param string $aad |
||
659 | * @param string|null &$tag |
||
660 | * @param int $tagLength |
||
661 | * |
||
662 | * @return false|string |
||
663 | */ |
||
664 | protected function openSslEncryptAuthenticatedImpl( |
||
681 | |||
682 | /** |
||
683 | * We need this wrapper for testing purposes so we can mock system call to Open SSL. |
||
684 | * |
||
685 | * @param string $data |
||
686 | * @param string $method |
||
687 | * @param string $password |
||
688 | * @param int $options |
||
689 | * @param string $initializationVector |
||
690 | * @param string $aad |
||
691 | * @param string $tag |
||
692 | * |
||
693 | * @return false|string |
||
694 | */ |
||
695 | protected function openSslDecryptAuthenticatedImpl( |
||
708 | |||
709 | /** |
||
710 | * We need this wrapper for testing purposes so we can mock system call to Open SSL. |
||
711 | * |
||
712 | * @param string $method |
||
713 | * |
||
714 | * @return int|false |
||
715 | */ |
||
716 | protected function openSslIvLengthImpl(string $method) |
||
720 | |||
721 | /** |
||
722 | * @param int $length |
||
723 | * |
||
724 | * @return bool |
||
725 | */ |
||
726 | private function isTagLengthMightBeValid(int $length): bool |
||
732 | } |
||
733 |
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.