Passed
Push — master ( c1b6a8...2f41ed )
by Alexander
02:09
created

SimpleLogger::getMessages()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Test\Support\Log;
6
7
use JsonException;
8
use Psr\Log\InvalidArgumentException;
9
use Psr\Log\LoggerInterface;
10
use Psr\Log\LoggerTrait;
11
use Psr\Log\LogLevel;
12
use Throwable;
13
14
use function get_class;
15
use function gettype;
16
use function implode;
17
use function is_string;
18
use function json_encode;
19
use function method_exists;
20
use function preg_replace_callback;
21
use function sprintf;
22
23
use const JSON_THROW_ON_ERROR;
24
use const JSON_UNESCAPED_SLASHES;
25
use const JSON_UNESCAPED_UNICODE;
26
27
final class SimpleLogger implements LoggerInterface
28
{
29
    use LoggerTrait;
30
31
    /**
32
     * The list of log message levels. See {@see LogLevel} constants for valid level names.
33
     */
34
    private const LEVELS = [
35
        LogLevel::EMERGENCY,
36
        LogLevel::ALERT,
37
        LogLevel::CRITICAL,
38
        LogLevel::ERROR,
39
        LogLevel::WARNING,
40
        LogLevel::NOTICE,
41
        LogLevel::INFO,
42
        LogLevel::DEBUG,
43
    ];
44
45
    /**
46
     * @var array[] The log messages.
47
     */
48
    private array $messages = [];
49
50
    /**
51
     * Logs a message in an array {@see $messages}.
52
     *
53
     * To get all the log messages, use the {@see getMessages()} method.
54
     *
55
     * @param mixed $level The log message level.
56
     * @param mixed $message The log message.
57
     * @param array $context The log message context.
58
     */
59 26
    public function log($level, $message, array $context = []): void
60
    {
61 26
        if (!is_string($level)) {
62 7
            throw new InvalidArgumentException(sprintf(
63 7
                'The log message level must be a string, %s provided.',
64 7
                gettype($level)
65
            ));
66
        }
67
68 19
        if (!in_array($level, self::LEVELS, true)) {
69 1
            throw new InvalidArgumentException(sprintf(
70 1
                'Invalid log message level "%s" provided. The following values are supported: "%s".',
71 1
                $level,
72 1
                implode('", "', self::LEVELS)
73
            ));
74
        }
75
76 18
        if (($message instanceof Throwable) && !isset($context['exception'])) {
77 1
            $context['exception'] = $message;
78
        }
79
80 18
        $message = $this->convertMessageToString($message);
81 18
        $message = $this->parseMessage($message, $context);
82
83 18
        $this->messages[] = ['level' => $level, 'message' => $message, 'context' => $context];
84 18
    }
85
86
    /**
87
     * Returns all log messages.
88
     *
89
     * @return array[] All log messages.
90
     */
91 18
    public function getMessages(): array
92
    {
93 18
        return $this->messages;
94
    }
95
96
    /**
97
     * Converts a message to a string.
98
     *
99
     * @param mixed $message The log message to be converted.
100
     *
101
     * @return string The log message in the string representation.
102
     */
103 18
    private function convertMessageToString($message): string
104
    {
105 18
        switch (gettype($message)) {
106 18
            case 'NULL':
107 1
                return 'null';
108 17
            case 'boolean':
109 2
                return $message ? 'true' : 'false';
110 15
            case 'object':
111 4
                return method_exists($message, '__toString') ? (string) $message : get_class($message);
112 11
            case 'array':
113
                try {
114 4
                    return json_encode($message, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
115 1
                } catch (JsonException $e) {
116 1
                    return '[]';
117
                }
118
            default:
119 8
                return (string) $message;
120
        }
121
    }
122
123
    /**
124
     * Parses log message resolving placeholders in the form: "{foo}",
125
     * where foo will be replaced by the context data in key "foo".
126
     *
127
     * @param string $message Raw log message.
128
     * @param array $context Message context.
129
     *
130
     * @return string Parsed message.
131
     *
132
     * @psalm-suppress MixedArgumentTypeCoercion
133
     * @psalm-suppress MixedArrayOffset
134
     * @psalm-suppress MixedAssignment
135
     */
136 18
    private function parseMessage(string $message, array $context): string
137
    {
138 18
        return preg_replace_callback('/{([\w.]+)}/', static function (array $matches) use ($context) {
139 3
            $placeholderName = $matches[1];
140
141 3
            if (isset($context[$placeholderName])) {
142 1
                return (string) $context[$placeholderName];
143
            }
144
145 2
            return $matches[0];
146 18
        }, $message);
147
    }
148
}
149