Passed
Push — master ( a53086...ada629 )
by Carlos
08:33
created

MiniLog::disable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
/**
3
 * This file is part of FacturaScripts
4
 * Copyright (C) 2017-2024 Carlos Garcia Gomez <[email protected]>
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as
8
 * published by the Free Software Foundation, either version 3 of the
9
 * License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18
 */
19
20
namespace FacturaScripts\Core\Base;
21
22
use FacturaScripts\Core\Base\Contract\MiniLogStorageInterface;
23
24
/**
25
 * Manage all log message information types.
26
 *
27
 * @author Carlos García Gómez <[email protected]>
28
 */
29
final class MiniLog
30
{
31
    const DEFAULT_CHANNEL = 'master';
32
    const LIMIT = 5000;
33
34
    /** @var string */
35
    private $channel;
36
37
    /** @var array */
38
    private static $context = [];
39
40
    /** @var array */
41
    private static $data = [];
42
43
    /** @var bool */
44
    private static $disabled = false;
45
46
    /** @var MiniLogStorageInterface */
47
    private static $storage;
48
49
    /** @var Translator|null */
50
    private $translator;
51
52
    public function __construct(string $channel = '', $translator = null)
53
    {
54
        $this->channel = empty($channel) ? self::DEFAULT_CHANNEL : $channel;
55
        $this->translator = $translator;
56
    }
57
58
    /**
59
     * Clears all data for one or all channels.
60
     *
61
     * @param string $channel
62
     */
63
    public static function clear(string $channel = ''): void
64
    {
65
        if (empty($channel)) {
66
            self::$data = [];
67
            return;
68
        }
69
70
        foreach (self::$data as $key => $item) {
71
            if ($item['channel'] === $channel) {
72
                unset(self::$data[$key]);
73
            }
74
        }
75
    }
76
77
    /**
78
     * Critical conditions.
79
     *
80
     * Example: Application component unavailable, unexpected exception.
81
     *
82
     * @param string $message
83
     * @param array $context
84
     */
85
    public function critical(string $message, array $context = []): void
86
    {
87
        $this->log('critical', $message, $context);
88
    }
89
90
    /**
91
     * Detailed debug information.
92
     *
93
     * @param string $message
94
     * @param array $context
95
     */
96
    public function debug(string $message, array $context = []): void
97
    {
98
        if (FS_DEBUG) {
99
            $this->log('debug', $message, $context);
100
        }
101
    }
102
103
    public static function disable(bool $value = true): void
104
    {
105
        self::$disabled = $value;
106
    }
107
108
    /**
109
     * Runtime errors that do not require immediate action but should typically
110
     * be logged and monitored.
111
     *
112
     * @param string $message
113
     * @param array $context
114
     */
115
    public function error(string $message, array $context = []): void
116
    {
117
        $this->log('error', $message, $context);
118
    }
119
120
    /**
121
     * Gets the stored context value for a given key.
122
     *
123
     * @param string $key
124
     *
125
     * @return string
126
     */
127
    public static function getContext(string $key): string
128
    {
129
        return self::$context[$key] ?? '';
130
    }
131
132
    /**
133
     * Interesting information, advices.
134
     *
135
     * @param string $message
136
     * @param array $context
137
     */
138
    public function info(string $message, array $context = []): void
139
    {
140
        $this->log('info', $message, $context);
141
    }
142
143
    /**
144
     * Normal but significant events.
145
     *
146
     * @param string $message
147
     * @param array $context
148
     */
149
    public function notice(string $message, array $context = []): void
150
    {
151
        $this->log('notice', $message, $context);
152
    }
153
154
    /**
155
     * Returns all messages for a given channel (or all channels) and some levels.
156
     *
157
     * @param string $channel
158
     * @param array $levels
159
     *
160
     * @return array
161
     */
162
    public static function read(string $channel = '', array $levels = []): array
163
    {
164
        $messages = [];
165
        foreach (self::$data as $data) {
166
            if ($channel && $data['channel'] != $channel) {
167
                continue;
168
            }
169
170
            if ($levels && false === in_array($data['level'], $levels)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $levels of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
171
                continue;
172
            }
173
174
            $messages[] = $data;
175
        }
176
177
        return $messages;
178
    }
179
180
    /**
181
     * Stores all messages on the default storage.
182
     *
183
     * @param string $channel
184
     *
185
     * @return bool
186
     */
187
    public static function save(string $channel = ''): bool
188
    {
189
        if (!isset(self::$storage)) {
190
            self::$storage = new MiniLogStorage();
191
        }
192
193
        $data = empty($channel) ? self::$data : self::read($channel);
194
        self::clear($channel);
195
196
        self::disable();
197
        $return = self::$storage->save($data);
198
        self::disable(false);
199
200
        return $return;
201
    }
202
203
    /**
204
     * Sets the context value for a given key.
205
     *
206
     * @param string $key
207
     * @param string $value
208
     */
209
    public static function setContext(string $key, string $value): void
210
    {
211
        self::$context[$key] = $value;
212
    }
213
214
    /**
215
     * Sets a new storage.
216
     *
217
     * @param MiniLogStorageInterface $storage
218
     */
219
    public static function setStorage(MiniLogStorageInterface $storage): void
220
    {
221
        self::$storage = $storage;
222
    }
223
224
    /**
225
     * Exceptional occurrences that are not errors.
226
     *
227
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
228
     * that are not necessarily wrong.
229
     *
230
     * @param string $message
231
     * @param array $context
232
     */
233
    public function warning(string $message, array $context = []): void
234
    {
235
        $this->log('warning', $message, $context);
236
    }
237
238
    /**
239
     * Logs with an arbitrary level.
240
     *
241
     * @param string $level
242
     * @param string $message
243
     * @param array $context
244
     */
245
    private function log(string $level, string $message, array $context = []): void
246
    {
247
        if (empty($message) || self::$disabled) {
248
            return;
249
        }
250
251
        // if we find this message in the log, we increase the counter
252
        $finalContext = array_merge($context, self::$context);
253
        $transMessage = is_null($this->translator) ? $message : $this->translator->trans($message, $context);
254
        foreach (self::$data as $key => $value) {
255
            if ($value['channel'] === $this->channel && $value['level'] === $level &&
256
                $value['message'] === $transMessage && $value['context'] === $finalContext) {
257
                self::$data[$key]['count']++;
258
                return;
259
            }
260
        }
261
262
        // add message
263
        self::$data[] = [
264
            'channel' => $this->channel,
265
            'context' => $finalContext,
266
            'count' => 1,
267
            'level' => $level,
268
            'message' => $transMessage,
269
            'original' => $message,
270
            'time' => $context['time'] ?? microtime(true),
271
        ];
272
        $this->reduce();
273
    }
274
275
    /**
276
     * Saves on the default storage and clear all data.
277
     */
278
    protected function reduce(): void
279
    {
280
        if (count(self::$data) > self::LIMIT) {
281
            self::save($this->channel);
282
        }
283
    }
284
}
285