Passed
Pull Request — master (#914)
by Asmir
02:33
created

ConsoleLogger::interpolate()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 10.0244

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 16
c 1
b 0
f 0
nc 10
nop 2
dl 0
loc 26
ccs 15
cts 16
cp 0.9375
crap 10.0244
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Migrations\Tools\Console;
6
7
use DateTime;
8
use DateTimeInterface;
9
use Psr\Log\AbstractLogger;
10
use Psr\Log\InvalidArgumentException;
11
use Psr\Log\LogLevel;
12
use Symfony\Component\Console\Output\ConsoleOutputInterface;
13
use Symfony\Component\Console\Output\OutputInterface;
14
use function get_class;
15
use function gettype;
16
use function is_object;
17
use function is_scalar;
18
use function method_exists;
19
use function sprintf;
20
use function strpos;
21
use function strtr;
22
23
/**
24
 * PSR-3 compliant console logger.
25
 *
26
 * @internal
27
 *
28
 * @see https://www.php-fig.org/psr/psr-3/
29
 */
30
final class ConsoleLogger extends AbstractLogger
31
{
32
    public const INFO  = 'info';
33
    public const ERROR = 'error';
34
35
    /** @var OutputInterface */
36
    private $output;
37
38
    /** @var array<string, int> */
39
    private $verbosityLevelMap = [
40
        LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
41
        LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
42
        LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
43
        LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
44
        LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
45
        LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL,
46
        LogLevel::INFO => OutputInterface::VERBOSITY_VERBOSE,
47
        LogLevel::DEBUG => OutputInterface::VERBOSITY_VERY_VERBOSE,
48
    ];
49
    /** @var array<string, string> */
50
    private $formatLevelMap = [
51
        LogLevel::EMERGENCY => self::ERROR,
52
        LogLevel::ALERT => self::ERROR,
53
        LogLevel::CRITICAL => self::ERROR,
54
        LogLevel::ERROR => self::ERROR,
55
        LogLevel::WARNING => self::INFO,
56
        LogLevel::NOTICE => self::INFO,
57
        LogLevel::INFO => self::INFO,
58
        LogLevel::DEBUG => self::INFO,
59
    ];
60
61
    /**
62
     * @param array<string, int>    $verbosityLevelMap
63
     * @param array<string, string> $formatLevelMap
64
     */
65 45
    public function __construct(OutputInterface $output, array $verbosityLevelMap = [], array $formatLevelMap = [])
66
    {
67 45
        $this->output            = $output;
68 45
        $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
69 45
        $this->formatLevelMap    = $formatLevelMap + $this->formatLevelMap;
70 45
    }
71
72
    /**
73
     * {@inheritdoc}
74
     */
75 4
    public function log($level, $message, array $context = [])
76
    {
77 4
        if (! isset($this->verbosityLevelMap[$level])) {
78
            throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
79
        }
80
81 4
        $output = $this->output;
82
83
        // Write to the error output if necessary and available
84 4
        if ($this->formatLevelMap[$level] === self::ERROR) {
85 1
            if ($this->output instanceof ConsoleOutputInterface) {
86
                $output = $output->getErrorOutput();
0 ignored issues
show
Bug introduced by
The method getErrorOutput() does not exist on Symfony\Component\Console\Output\OutputInterface. It seems like you code against a sub-type of Symfony\Component\Console\Output\OutputInterface such as Symfony\Component\Consol...\ConsoleOutputInterface or Symfony\Component\Console\Style\OutputStyle or Symfony\Component\Console\Output\ConsoleOutput. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

86
                /** @scrutinizer ignore-call */ 
87
                $output = $output->getErrorOutput();
Loading history...
87
            }
88
        }
89
90
        // the if condition check isn't necessary -- it's the same one that $output will do internally anyway.
91
        // We only do it for efficiency here as the message formatting is relatively expensive.
92 4
        if ($output->getVerbosity() < $this->verbosityLevelMap[$level]) {
93 2
            return;
94
        }
95
96 3
        $output->writeln(sprintf('<%1$s>[%2$s] %3$s</%1$s>', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]);
97 3
    }
98
99
    /**
100
     * Interpolates context values into the message placeholders.
101
     *
102
     * @param mixed[] $context
103
     */
104 3
    private function interpolate(string $message, array $context) : string
105
    {
106 3
        if (strpos($message, '{') === false) {
107 2
            return $message;
108
        }
109
110 1
        $replacements = [];
111 1
        foreach ($context as $key => $val) {
112 1
            if ($val === null || is_scalar($val) || (is_object($val) && method_exists($val, '__toString'))) {
113 1
                $replacements["{{$key}}"] = $val;
114 1
            } elseif ($val instanceof DateTimeInterface) {
115 1
                $replacements["{{$key}}"] = $val->format(DateTime::RFC3339);
116 1
            } elseif (is_object($val)) {
117 1
                $replacements["{{$key}}"] = '[object ' . get_class($val) . ']';
118
            } else {
119 1
                $replacements["{{$key}}"] = '[' . gettype($val) . ']';
120
            }
121
122 1
            if (! isset($replacements["{{$key}}"])) {
123
                continue;
124
            }
125
126 1
            $replacements["{{$key}}"] = '<comment>' . $replacements["{{$key}}"] . '</comment>';
127
        }
128
129 1
        return strtr($message, $replacements);
130
    }
131
}
132