PrintLogger::format()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of Cecil.
5
 *
6
 * (c) Arnaud Ligny <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Cecil\Logger;
15
16
use Cecil\Builder;
17
use Psr\Log\AbstractLogger;
18
use Psr\Log\InvalidArgumentException;
19
use Psr\Log\LogLevel;
20
21
/**
22
 * PrintLogger class.
23
 *
24
 * This logger prints messages to the standard output.
25
 * It supports different log levels and can be configured to print only messages
26
 * up to a certain verbosity level.
27
 */
28
class PrintLogger extends AbstractLogger
29
{
30
    /** @var int */
31
    protected $printLevelMax = null;
32
33
    /** @var array */
34
    protected $verbosityLevelMap = [
35
        LogLevel::EMERGENCY => Builder::VERBOSITY_NORMAL,
36
        LogLevel::ALERT     => Builder::VERBOSITY_NORMAL,
37
        LogLevel::CRITICAL  => Builder::VERBOSITY_NORMAL,
38
        LogLevel::ERROR     => Builder::VERBOSITY_NORMAL,
39
        LogLevel::WARNING   => Builder::VERBOSITY_NORMAL,
40
        LogLevel::NOTICE    => Builder::VERBOSITY_NORMAL,
41
        LogLevel::INFO      => Builder::VERBOSITY_VERBOSE,
42
        LogLevel::DEBUG     => Builder::VERBOSITY_DEBUG,
43
    ];
44
45
    /**
46
     * Print only the $printLevelMax.
47
     */
48
    public function __construct(?int $printLevelMax = null)
49
    {
50
        $this->printLevelMax = $printLevelMax;
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    public function log($level, string|\Stringable $message, array $context = []): void
57
    {
58
        if (!isset($this->verbosityLevelMap[$level])) {
59
            throw new InvalidArgumentException(\sprintf('The log level "%s" does not exist.', $level));
60
        }
61
62
        if ($this->printLevelMax !== null && $this->verbosityLevelMap[$level] > $this->printLevelMax) {
63
            return;
64
        }
65
66
        $level = $level != LogLevel::INFO ? "[$level] " : '';
67
68
        if (isset($context['progress'])) {
69
            printf(
70
                "%s%s (%s/%s)\n",
71
                $level,
72
                $this->interpolate($message, $context),
73
                $context['progress'][0],
74
                $context['progress'][1]
75
            );
76
77
            return;
78
        }
79
80
        printf("%s%s\n", $level, $this->interpolate($message, $context));
81
    }
82
83
    /**
84
     * Interpolates context values into the message placeholders.
85
     *
86
     * @author PHP Framework Interoperability Group
87
     */
88
    protected function interpolate(string $message, array $context): string
89
    {
90
        if (false === strpos($message, '{')) {
91
            return $message;
92
        }
93
94
        $replacements = [];
95
        foreach ($context as $key => $val) {
96
            if (null === $val || \is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
97
                $replacements["{{$key}}"] = $val;
98
            } elseif ($val instanceof \DateTimeInterface) {
99
                $replacements["{{$key}}"] = $val->format(\DateTime::RFC3339);
100
            } elseif (\is_object($val)) {
101
                $replacements["{{$key}}"] = '[object ' . \get_class($val) . ']';
102
            } else {
103
                $replacements["{{$key}}"] = '[' . \gettype($val) . ']';
104
            }
105
        }
106
107
        return strtr($message, $replacements);
108
    }
109
110
    /**
111
     * Format expression to string.
112
     */
113
    public static function format($expression): string
114
    {
115
        return str_replace(["\n", ' '], '', var_export($expression, true));
116
    }
117
}
118