Passed
Push — logger ( ec2b41...fe50cc )
by Arnaud
02:58
created

ConsoleLogger::log()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 7
eloc 18
c 3
b 0
f 0
nc 6
nop 3
dl 0
loc 32
rs 8.8333
1
<?php
2
/**
3
 * This file is part of the Cecil/Cecil package.
4
 *
5
 * Copyright (c) Arnaud Ligny <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Cecil\Logger;
12
13
use Psr\Log\InvalidArgumentException;
14
use Psr\Log\LogLevel;
15
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
16
use Symfony\Component\Console\Helper\ProgressBar;
17
use Symfony\Component\Console\Logger\ConsoleLogger as SfConsoleLogger;
18
use Symfony\Component\Console\Output\OutputInterface;
19
20
class ConsoleLogger extends SfConsoleLogger
21
{
22
    const ERROR = 'error';
23
    const WARNING = 'comment';
24
    const NOTICE = 'info';
25
    const INFO = 'text';
26
27
    private $output;
28
    private $verbosityLevelMap = [
29
        LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
30
        LogLevel::ALERT     => OutputInterface::VERBOSITY_NORMAL,
31
        LogLevel::CRITICAL  => OutputInterface::VERBOSITY_NORMAL,
32
        LogLevel::ERROR     => OutputInterface::VERBOSITY_NORMAL,
33
        LogLevel::WARNING   => OutputInterface::VERBOSITY_NORMAL,
34
        LogLevel::NOTICE    => OutputInterface::VERBOSITY_NORMAL,
35
        LogLevel::INFO      => OutputInterface::VERBOSITY_VERBOSE,
36
        LogLevel::DEBUG     => OutputInterface::VERBOSITY_DEBUG,
37
    ];
38
    private $formatLevelMap = [
39
        LogLevel::EMERGENCY => self::ERROR,
40
        LogLevel::ALERT     => self::ERROR,
41
        LogLevel::CRITICAL  => self::ERROR,
42
        LogLevel::ERROR     => self::ERROR,
43
        LogLevel::WARNING   => self::INFO,
44
        LogLevel::NOTICE    => self::NOTICE,
45
        LogLevel::INFO      => self::INFO,
46
        LogLevel::DEBUG     => self::INFO,
47
    ];
48
49
    /** @var ProgressBar */
50
    protected $progressBar = null;
51
    /** @var int */
52
    private $progressBarMax;
53
54
    public function __construct(OutputInterface $output, array $verbosityLevelMap = [], array $formatLevelMap = [])
55
    {
56
        $this->output = $output;
57
        $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
58
        $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap;
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     *
64
     * @return void
65
     */
66
    public function log($level, $message, array $context = [])
67
    {
68
        if (!isset($this->verbosityLevelMap[$level])) {
69
            throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
70
        }
71
72
        $output = $this->output;
73
74
        if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) {
75
            $outputStyle = new OutputFormatterStyle('white');
76
            $output->getFormatter()->setStyle('text', $outputStyle);
77
78
            if ($output->getVerbosity() == OutputInterface::VERBOSITY_NORMAL && array_key_exists('step', $context)) {
79
                $this->printProgressBar($context['step'][0], $context['step'][1]);
80
81
                return;
82
            }
83
84
            $pattern = '<%1$s>%3$s</%1$s>';
85
            if (array_key_exists('progress', $context)) {
86
                $pattern = '<%1$s>('.$context['progress'][0].'/'.$context['progress'][1].') %3$s</%1$s>';
87
88
                if ($output->getVerbosity() == OutputInterface::VERBOSITY_VERBOSE) {
89
                    $this->printProgressBar($context['progress'][0], $context['progress'][1]);
90
91
                    return;
92
                }
93
            }
94
95
            $output->writeln(
96
                sprintf($pattern, $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)),
97
                $this->verbosityLevelMap[$level]
98
            );
99
        }
100
    }
101
102
    /**
103
     * Interpolates context values into the message placeholders.
104
     *
105
     * @author PHP Framework Interoperability Group
106
     */
107
    private function interpolate(string $message, array $context): string
108
    {
109
        if (false === strpos($message, '{')) {
110
            return $message;
111
        }
112
113
        $replacements = [];
114
        foreach ($context as $key => $val) {
115
            if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
116
                $replacements["{{$key}}"] = $val;
117
            } elseif ($val instanceof \DateTimeInterface) {
118
                $replacements["{{$key}}"] = $val->format(\DateTime::RFC3339);
119
            } elseif (\is_object($val)) {
120
                $replacements["{{$key}}"] = '[object '.\get_class($val).']';
121
            } else {
122
                $replacements["{{$key}}"] = '['.\gettype($val).']';
123
            }
124
        }
125
126
        return strtr($message, $replacements);
127
    }
128
129
    /**
130
     * Prints the Progress Bar.
131
     *
132
     * @param int $itemsCount
133
     * @param int $itemsMax
134
     *
135
     * @return void
136
     */
137
    protected function printProgressBar(int $itemsCount, int $itemsMax): void
138
    {
139
        $this->createProgressBar($itemsMax);
140
        $this->progressBar->clear();
141
        $this->progressBar->setProgress($itemsCount);
142
        $this->progressBar->display();
143
        if ($itemsCount == $itemsMax) {
144
            $this->progressBar->finish();
145
            $this->output->writeln('');
146
        }
147
    }
148
149
    /**
150
     * Creates the Progress bar.
151
     *
152
     * @param int $max
153
     *
154
     * @return void
155
     */
156
    private function createProgressBar(int $max): void
157
    {
158
        if ($this->progressBar === null || $max != $this->progressBarMax) {
159
            $this->progressBarMax = $max;
160
            $this->progressBar = new ProgressBar($this->output, $max);
161
            $this->progressBar->setOverwrite(true);
162
            $this->progressBar->setFormat('%percent:3s%% [%bar%] %current%/%max%');
163
            $this->progressBar->setBarCharacter('#');
164
            $this->progressBar->setEmptyBarCharacter(' ');
165
            $this->progressBar->setProgressCharacter('#');
166
            $this->progressBar->setRedrawFrequency(1);
167
            $this->progressBar->start();
168
        }
169
    }
170
}
171