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.