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:
1 | <?php declare(strict_types=1); |
||
385 | class Logger |
||
386 | { |
||
387 | const EMERGENCY = 'emergency'; |
||
388 | const ALERT = 'alert'; |
||
389 | const CRITICAL = 'critical'; |
||
390 | const ERROR = 'error'; |
||
391 | const WARNING = 'warning'; |
||
392 | const NOTICE = 'notice'; |
||
393 | const INFO = 'info'; |
||
394 | const DEBUG = 'debug'; |
||
395 | |||
396 | const BLACK = 'black'; |
||
397 | const DARK_GRAY = 'dark_gray'; |
||
398 | const BLUE = 'blue'; |
||
399 | const LIGHT_BLUE = 'light_blue'; |
||
400 | const GREEN = 'green'; |
||
401 | const LIGHT_GREEN = 'light_green'; |
||
402 | const CYAN = 'cyan'; |
||
403 | const LIGHT_CYAN = 'light_cyan'; |
||
404 | const RED = 'red'; |
||
405 | const LIGHT_RED = 'light_red'; |
||
406 | const PURPLE = 'purple'; |
||
407 | const LIGHT_PURPLE = 'light_purple'; |
||
408 | const BROWN = 'brown'; |
||
409 | const YELLOW = 'yellow'; |
||
410 | const MAGENTA = 'magenta'; |
||
411 | const LIGHT_GRAY = 'light_gray'; |
||
412 | const WHITE = 'white'; |
||
413 | const DEFAULT = 'default'; |
||
414 | const BOLD = 'bold'; |
||
415 | |||
416 | /** @var resource $resource The file handle */ |
||
417 | protected $resource = null; |
||
418 | |||
419 | /** @var string $level */ |
||
420 | protected $level = self::INFO; |
||
421 | |||
422 | /** @var bool $closeLocally */ |
||
423 | protected $closeLocally = false; |
||
424 | |||
425 | /** @var bool */ |
||
426 | protected $addDate = true; |
||
427 | |||
428 | /** @var string */ |
||
429 | protected $separator = ' | '; |
||
430 | |||
431 | /** @var \Closure */ |
||
432 | protected $formatter = null; |
||
433 | |||
434 | /** @var string */ |
||
435 | protected $lastLogEntry = ''; |
||
436 | |||
437 | /** @var bool|null */ |
||
438 | protected $gzipFile = null; |
||
439 | |||
440 | /** @var bool */ |
||
441 | protected $useLocking = false; |
||
442 | |||
443 | /** |
||
444 | * @var array $logLevels List of supported levels |
||
445 | */ |
||
446 | static protected $logLevels = [ |
||
447 | self::EMERGENCY => [1, self::WHITE, self::RED, self::DEFAULT, 'EMERG'], |
||
448 | self::ALERT => [2, self::WHITE, self::YELLOW, self::DEFAULT, 'ALERT'], |
||
449 | self::CRITICAL => [3, self::RED, self::DEFAULT, self::BOLD , 'CRIT'], |
||
450 | self::ERROR => [4, self::RED, self::DEFAULT, self::DEFAULT, 'ERROR'], |
||
451 | self::WARNING => [5, self::YELLOW, self::DEFAULT, self::DEFAULT, 'WARN'], |
||
452 | self::NOTICE => [6, self::CYAN, self::DEFAULT, self::DEFAULT, 'NOTE'], |
||
453 | self::INFO => [7, self::GREEN, self::DEFAULT, self::DEFAULT, 'INFO'], |
||
454 | self::DEBUG => [8, self::LIGHT_GRAY, self::DEFAULT, self::DEFAULT, 'DEBUG'], |
||
455 | ]; |
||
456 | |||
457 | /** |
||
458 | * @var array |
||
459 | */ |
||
460 | static protected $colours = [ |
||
461 | 'fore' => [ |
||
462 | self::BLACK => '0;30', |
||
463 | self::DARK_GRAY => '1;30', |
||
464 | self::BLUE => '0;34', |
||
465 | self::LIGHT_BLUE => '1;34', |
||
466 | self::GREEN => '0;32', |
||
467 | self::LIGHT_GREEN => '1;32', |
||
468 | self::CYAN => '0;36', |
||
469 | self::LIGHT_CYAN => '1;36', |
||
470 | self::RED => '0;31', |
||
471 | self::LIGHT_RED => '1;31', |
||
472 | self::PURPLE => '0;35', |
||
473 | self::LIGHT_PURPLE => '1;35', |
||
474 | self::BROWN => '0;33', |
||
475 | self::YELLOW => '1;33', |
||
476 | self::MAGENTA => '0;35', |
||
477 | self::LIGHT_GRAY => '0;37', |
||
478 | self::WHITE => '1;37', |
||
479 | ], |
||
480 | 'back' => [ |
||
481 | self::DEFAULT => '49', |
||
482 | self::BLACK => '40', |
||
483 | self::RED => '41', |
||
484 | self::GREEN => '42', |
||
485 | self::YELLOW => '43', |
||
486 | self::BLUE => '44', |
||
487 | self::MAGENTA => '45', |
||
488 | self::CYAN => '46', |
||
489 | self::LIGHT_GRAY => '47', |
||
490 | ], |
||
491 | self::BOLD => [], |
||
492 | ]; |
||
493 | |||
494 | /** |
||
495 | * @param mixed $resource |
||
496 | * @param string $level |
||
497 | * @param bool $useLocking |
||
498 | * @param bool $gzipFile |
||
499 | * @param bool $addDate |
||
500 | */ |
||
501 | public function __construct($resource=STDOUT, string $level=self::INFO, bool $useLocking=false, bool $gzipFile=false, bool $addDate=true) |
||
509 | |||
510 | /** |
||
511 | * System is unusable. |
||
512 | * |
||
513 | * @param string $message |
||
514 | * @param array $context |
||
515 | */ |
||
516 | public function emergency(string $message, array $context=[]) |
||
520 | |||
521 | /** |
||
522 | * Action must be taken immediately. |
||
523 | * |
||
524 | * Example: Entire website down, database unavailable, etc. This should |
||
525 | * trigger the SMS alerts and wake you up. |
||
526 | * |
||
527 | * @param string $message |
||
528 | * @param array $context |
||
529 | */ |
||
530 | public function alert(string $message, array $context=[]) |
||
534 | |||
535 | /** |
||
536 | * Critical conditions. |
||
537 | * |
||
538 | * Example: Application component unavailable, unexpected exception. |
||
539 | * |
||
540 | * @param string $message |
||
541 | * @param array $context |
||
542 | */ |
||
543 | public function critical(string $message, array $context=[]) |
||
547 | |||
548 | /** |
||
549 | * Runtime errors that do not require immediate action but should typically |
||
550 | * be logged and monitored. |
||
551 | * |
||
552 | * @param string $message |
||
553 | * @param array $context |
||
554 | */ |
||
555 | public function error(string $message, array $context=[]) |
||
559 | |||
560 | /** |
||
561 | * Exceptional occurrences that are not errors. |
||
562 | * |
||
563 | * Example: Use of deprecated APIs, poor use of an API, undesirable things |
||
564 | * that are not necessarily wrong. |
||
565 | * |
||
566 | * @param string $message |
||
567 | * @param array $context |
||
568 | */ |
||
569 | public function warning(string $message, array $context=[]) |
||
573 | |||
574 | /** |
||
575 | * Normal but significant events. |
||
576 | * |
||
577 | * @param string $message |
||
578 | * @param array $context |
||
579 | */ |
||
580 | public function notice(string $message, array $context=[]) |
||
584 | |||
585 | /** |
||
586 | * Interesting events. |
||
587 | * |
||
588 | * Example: User logs in, SQL logs. |
||
589 | * |
||
590 | * @param string $message |
||
591 | * @param array $context |
||
592 | */ |
||
593 | public function info(string $message, array $context=[]) |
||
597 | |||
598 | /** |
||
599 | * Detailed debug information. |
||
600 | * |
||
601 | * @param string $message |
||
602 | * @param array $context |
||
603 | */ |
||
604 | public function debug(string $message, array $context=[]) |
||
608 | |||
609 | /** |
||
610 | * @param $resource |
||
611 | * @return Logger |
||
612 | */ |
||
613 | public function setLogFile($resource) : Logger |
||
619 | |||
620 | /** |
||
621 | * @param string $string |
||
622 | * @param string $foregroundColor |
||
623 | * @param string $backgroundColor |
||
624 | * @param bool $bold |
||
625 | * @return string |
||
626 | */ |
||
627 | public static function addColour(string $string, string $foregroundColor='', string $backgroundColor='', bool $bold=false) : string |
||
647 | |||
648 | /** |
||
649 | * @param string $string |
||
650 | * @param string $foregroundColor |
||
651 | * @param string $backgroundColor |
||
652 | * @param bool $bold |
||
653 | * @return string |
||
654 | */ |
||
655 | public function colourize(string $string, string $foregroundColor='', string $backgroundColor='', bool $bold=false) : string |
||
659 | |||
660 | /** |
||
661 | * @param string $level Ignore logging attempts at a level less the $level |
||
662 | * @return Logger |
||
663 | */ |
||
664 | public function setLogLevel(string $level) : Logger |
||
674 | |||
675 | /** |
||
676 | * @return Logger |
||
677 | */ |
||
678 | public function lock() : Logger |
||
684 | |||
685 | /** |
||
686 | * @return Logger |
||
687 | */ |
||
688 | public function gzipped() : Logger |
||
694 | |||
695 | /** |
||
696 | * @param callable $fnFormatter |
||
697 | * |
||
698 | * @return Logger |
||
699 | */ |
||
700 | public function formatter(callable $fnFormatter) : Logger |
||
706 | |||
707 | /** |
||
708 | * Log messages to resource |
||
709 | * |
||
710 | * @param mixed $level The level of the log message |
||
711 | * @param string|object $message If an object is passed it must implement __toString() |
||
712 | * @param array $context Placeholders to be substituted in the message |
||
713 | */ |
||
714 | public function log($level, $message, array $context=[]) |
||
734 | |||
735 | /** |
||
736 | * @param string $style |
||
737 | * @param string $message |
||
738 | * @return string |
||
739 | */ |
||
740 | public static function style(string $style, string $message) : string |
||
748 | |||
749 | /** |
||
750 | * @param string $level |
||
751 | * @param string $message |
||
752 | * @param array $context |
||
753 | * @return string |
||
754 | */ |
||
755 | protected function formatMessage(string $level, string $message, array $context=[]) : string |
||
766 | |||
767 | /** |
||
768 | * Write the content to the stream |
||
769 | * |
||
770 | * @param string $content |
||
771 | */ |
||
772 | public function write(string $content) |
||
785 | |||
786 | /** |
||
787 | * @return mixed|resource |
||
788 | * @throws \Exception |
||
789 | */ |
||
790 | protected function getResource() |
||
806 | |||
807 | /** |
||
808 | * @return string |
||
809 | */ |
||
810 | public function getLastLogEntry() : string |
||
814 | |||
815 | /** |
||
816 | * @return resource |
||
817 | */ |
||
818 | protected function openResource() |
||
827 | |||
828 | public function __destruct() |
||
835 | } |
||
836 |
Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.