1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace ColinODell\PsrTestLogger; |
||||
6 | |||||
7 | use Psr\Log\AbstractLogger; |
||||
8 | use Psr\Log\InvalidArgumentException; |
||||
9 | |||||
10 | /** |
||||
11 | * Used for testing purposes. |
||||
12 | * |
||||
13 | * It records all records and gives you access to them for verification. |
||||
14 | * |
||||
15 | * @method bool hasEmergency(string|array $record) |
||||
16 | * @method bool hasAlert(string|array $record) |
||||
17 | * @method bool hasCritical(string|array $record) |
||||
18 | * @method bool hasError(string|array $record) |
||||
19 | * @method bool hasWarning(string|array $record) |
||||
20 | * @method bool hasNotice(string|array $record) |
||||
21 | * @method bool hasInfo(string|array $record) |
||||
22 | * @method bool hasDebug(string|array $record) |
||||
23 | * @method bool hasEmergencyRecords() |
||||
24 | * @method bool hasAlertRecords() |
||||
25 | * @method bool hasCriticalRecords() |
||||
26 | * @method bool hasErrorRecords() |
||||
27 | * @method bool hasWarningRecords() |
||||
28 | * @method bool hasNoticeRecords() |
||||
29 | * @method bool hasInfoRecords() |
||||
30 | * @method bool hasDebugRecords() |
||||
31 | * @method bool hasEmergencyThatContains(string $message) |
||||
32 | * @method bool hasAlertThatContains(string $message) |
||||
33 | * @method bool hasCriticalThatContains(string $message) |
||||
34 | * @method bool hasErrorThatContains(string $message) |
||||
35 | * @method bool hasWarningThatContains(string $message) |
||||
36 | * @method bool hasNoticeThatContains(string $message) |
||||
37 | * @method bool hasInfoThatContains(string $message) |
||||
38 | * @method bool hasDebugThatContains(string $message) |
||||
39 | * @method bool hasEmergencyThatMatches(string $regex) |
||||
40 | * @method bool hasAlertThatMatches(string $regex) |
||||
41 | * @method bool hasCriticalThatMatches(string $regex) |
||||
42 | * @method bool hasErrorThatMatches(string $regex) |
||||
43 | * @method bool hasWarningThatMatches(string $regex) |
||||
44 | * @method bool hasNoticeThatMatches(string $regex) |
||||
45 | * @method bool hasInfoThatMatches(string $regex) |
||||
46 | * @method bool hasDebugThatMatches(string $regex) |
||||
47 | * @method bool hasEmergencyThatPasses(callable $predicate) |
||||
48 | * @method bool hasAlertThatPasses(callable $predicate) |
||||
49 | * @method bool hasCriticalThatPasses(callable $predicate) |
||||
50 | * @method bool hasErrorThatPasses(callable $predicate) |
||||
51 | * @method bool hasWarningThatPasses(callable $predicate) |
||||
52 | * @method bool hasNoticeThatPasses(callable $predicate) |
||||
53 | * @method bool hasInfoThatPasses(callable $predicate) |
||||
54 | * @method bool hasDebugThatPasses(callable $predicate) |
||||
55 | * |
||||
56 | * Adapted from psr/log, |
||||
57 | * Copyright (c) 2012 PHP Framework Interoperability Group |
||||
58 | * Used under the MIT license |
||||
59 | */ |
||||
60 | final class TestLogger extends AbstractLogger |
||||
61 | { |
||||
62 | /** @var array<int, array<string, mixed>> */ |
||||
63 | public array $records = []; |
||||
64 | |||||
65 | /** @var array<string|int, array<int, array<string, mixed>>> */ |
||||
66 | public array $recordsByLevel = []; |
||||
67 | |||||
68 | /** |
||||
69 | * {@inheritDoc} |
||||
70 | * |
||||
71 | * @param array<array-key, mixed> $context |
||||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||||
72 | */ |
||||
73 | 108 | public function log($level, $message, array $context = []): void |
|||
74 | { |
||||
75 | 108 | if (! (\is_string($level) || \is_int($level))) { |
|||
76 | 14 | throw new InvalidArgumentException('Unsupported log level. The psr-testlogger library only supports string and integer log levels; passed ' . \print_r($level, true)); |
|||
0 ignored issues
–
show
Are you sure
print_r($level, true) of type string|true can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
77 | } |
||||
78 | |||||
79 | 94 | $record = [ |
|||
80 | 94 | 'level' => $level, |
|||
81 | 94 | 'message' => $message, |
|||
82 | 94 | 'context' => $context, |
|||
83 | ]; |
||||
84 | |||||
85 | 94 | $this->recordsByLevel[$record['level']][] = $record; |
|||
86 | 94 | $this->records[] = $record; |
|||
87 | 94 | } |
|||
88 | |||||
89 | 94 | public function hasRecords(string|int|null $level = null): bool |
|||
90 | { |
||||
91 | 94 | if ($level === null) { |
|||
92 | 10 | return \count($this->records) !== 0; |
|||
93 | } |
||||
94 | |||||
95 | 84 | return isset($this->recordsByLevel[$level]); |
|||
96 | } |
||||
97 | |||||
98 | /** |
||||
99 | * @param string|array<string, mixed> $record |
||||
100 | */ |
||||
101 | 20 | public function hasRecord(string|array $record, string|int|null $level = null): bool |
|||
102 | { |
||||
103 | 20 | if (\is_string($record)) { |
|||
0 ignored issues
–
show
|
|||||
104 | 20 | $record = ['message' => $record]; |
|||
105 | } |
||||
106 | |||||
107 | 20 | return $this->hasRecordThatPasses(static function (array $rec) use ($record) { |
|||
108 | 20 | if ((string) $rec['message'] !== (string) $record['message']) { |
|||
109 | 18 | return false; |
|||
110 | } |
||||
111 | |||||
112 | 20 | return ! isset($record['context']) || $rec['context'] === $record['context']; |
|||
113 | 20 | }, $level); |
|||
114 | } |
||||
115 | |||||
116 | 18 | public function hasRecordThatContains(string $message, string|int|null $level = null): bool |
|||
117 | { |
||||
118 | 18 | return $this->hasRecordThatPasses(static function (array $rec) use ($message) { |
|||
119 | 18 | return \str_contains((string) $rec['message'], $message); |
|||
120 | 18 | }, $level); |
|||
121 | } |
||||
122 | |||||
123 | 18 | public function hasRecordThatMatches(string $regex, string|int|null $level = null): bool |
|||
124 | { |
||||
125 | 18 | return $this->hasRecordThatPasses(static function ($rec) use ($regex) { |
|||
126 | 18 | return \preg_match($regex, (string) $rec['message']) > 0; |
|||
127 | 18 | }, $level); |
|||
128 | } |
||||
129 | |||||
130 | /** |
||||
131 | * @param callable(array<string, mixed>, int): bool $predicate |
||||
132 | */ |
||||
133 | 74 | public function hasRecordThatPasses(callable $predicate, string|int|null $level = null): bool |
|||
134 | { |
||||
135 | 74 | if (! $this->hasRecords($level)) { |
|||
136 | 72 | return false; |
|||
137 | } |
||||
138 | |||||
139 | 74 | foreach ($level === null ? $this->records : $this->recordsByLevel[$level] as $i => $rec) { |
|||
140 | 74 | if (\call_user_func($predicate, $rec, $i)) { |
|||
141 | 74 | return true; |
|||
142 | } |
||||
143 | } |
||||
144 | |||||
145 | 36 | return false; |
|||
146 | } |
||||
147 | |||||
148 | /** |
||||
149 | * @param array<int, mixed> $args |
||||
150 | */ |
||||
151 | 84 | public function __call(string $method, array $args): bool |
|||
152 | { |
||||
153 | 84 | if (\preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { |
|||
154 | 82 | $genericMethod = $matches[1] . ($matches[3] !== 'Records' ? 'Record' : '') . $matches[3]; |
|||
155 | 82 | $callable = [$this, $genericMethod]; |
|||
156 | 82 | $level = \strtolower($matches[2]); |
|||
157 | 82 | if (\is_callable($callable)) { |
|||
158 | 82 | $args[] = $level; |
|||
159 | |||||
160 | 82 | return \call_user_func_array($callable, $args); |
|||
161 | } |
||||
162 | } |
||||
163 | |||||
164 | 2 | throw new \BadMethodCallException('Call to undefined method ' . self::class . '::' . $method . '()'); |
|||
165 | } |
||||
166 | |||||
167 | 2 | public function reset(): void |
|||
168 | { |
||||
169 | 2 | $this->records = []; |
|||
170 | 2 | $this->recordsByLevel = []; |
|||
171 | 2 | } |
|||
172 | } |
||||
173 |