Passed
Push — feature/upgrade-phpunit ( 397e91 )
by Avtandil
02:47
created

TelegramLog::interpolate()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 13
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 5
nc 3
nop 2
dl 0
loc 13
ccs 6
cts 6
cp 1
crap 5
rs 9.6111
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the TelegramBot package.
4
 *
5
 * (c) Avtandil Kikabidze aka LONGMAN <[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 Longman\TelegramBot;
12
13
use Exception;
14
use Longman\TelegramBot\Exception\TelegramLogException;
15
use Monolog\Formatter\LineFormatter;
16
use Monolog\Handler\StreamHandler;
17
use Monolog\Logger;
18
use Psr\Log\LoggerInterface;
19
use Psr\Log\NullLogger;
20
21
/**
22
 * Class TelegramLog
23
 *
24
 * @todo Clean out all deprecated code in the near future!
25
 *
26
 * @method static void emergency(string $message, array $context = [])
27
 * @method static void alert(string $message, array $context = [])
28
 * @method static void critical(string $message, array $context = [])
29
 * @method static void error(string $message, array $context = [])
30
 * @method static void warning(string $message, array $context = [])
31
 * @method static void notice(string $message, array $context = [])
32
 * @method static void info(string $message, array $context = [])
33
 * @method static void debug(string $message, array $context = [])
34
 * @method static void update(string $message, array $context = [])
35
 */
36
class TelegramLog
37
{
38
    /**
39
     * Logger instance
40
     *
41
     * @var LoggerInterface|Logger
42
     */
43
    protected static $logger;
44
45
    /**
46
     * Logger instance for update
47
     *
48
     * @var LoggerInterface|Logger
49
     */
50
    protected static $update_logger;
51
52
    /**
53
     * Path for error log
54
     *
55
     * @var string
56
     * @deprecated
57
     */
58
    protected static $error_log_path;
59
60
    /**
61
     * Path for debug log
62
     *
63
     * @var string
64
     * @deprecated
65
     */
66
    protected static $debug_log_path;
67
68
    /**
69
     * Path for update log
70
     *
71
     * @var string
72
     * @deprecated
73
     */
74
    protected static $update_log_path;
75
76
    /**
77
     * Temporary stream handle for debug log
78
     *
79
     * @var resource|null
80
     */
81
    protected static $debug_log_temp_stream_handle;
82
83
    /**
84
     * Initialise Logger instance, optionally passing an existing one.
85
     *
86
     * @param LoggerInterface|null $logger
87
     * @param LoggerInterface|null $update_logger
88
     */
89 3
    public static function initialize(LoggerInterface $logger = null, LoggerInterface $update_logger = null)
90
    {
91 3
        if ($logger === null && $update_logger === null) {
92
            // Clearly deprecated code still being executed.
93 2
            (defined('PHPUNIT_TESTSUITE') && PHPUNIT_TESTSUITE) || trigger_error('A PSR-3 compatible LoggerInterface object must be provided. Initialise with a preconfigured logger instance.', E_USER_DEPRECATED);
94 2
            $logger = new Logger('bot_log');
95 1
        } elseif ($logger instanceof Logger) {
96 1
            foreach ($logger->getHandlers() as $handler) {
97 1
                if (method_exists($handler, 'getLevel') && $handler->getLevel() === Logger::ERROR) {
98 1
                    self::$error_log_path = 'true';
99
                }
100 1
                if (method_exists($handler, 'getLevel') && $handler->getLevel() === Logger::DEBUG) {
101 1
                    self::$debug_log_path = 'true';
102
                }
103
            }
104
        }
105
106
        // Fallback to NullLogger.
107 3
        self::$logger        = $logger ?: new NullLogger();
108 3
        self::$update_logger = $update_logger ?: new NullLogger();
109 3
    }
110
111
    /**
112
     * Initialise error log (deprecated)
113
     *
114
     * @param string $path
115
     *
116
     * @return LoggerInterface
117
     * @throws Exception
118
     *
119
     * @deprecated Initialise a preconfigured logger instance instead.
120
     */
121 2
    public static function initErrorLog($path)
122
    {
123 2
        (defined('PHPUNIT_TESTSUITE') && PHPUNIT_TESTSUITE) || trigger_error(__METHOD__ . ' is deprecated and will be removed soon. Initialise with a preconfigured logger instance instead using "TelegramLog::initialize($logger)".', E_USER_DEPRECATED);
124
125 2
        if ($path === null || $path === '') {
126 1
            throw new TelegramLogException('Empty path for error log');
127
        }
128 1
        self::initialize();
129
130
        // Deprecated code used as fallback.
131 1
        if (self::$logger instanceof Logger) {
132 1
            self::$error_log_path = $path;
133
134 1
            self::$logger->pushHandler(
135 1
                (new StreamHandler(self::$error_log_path, Logger::ERROR))
136 1
                    ->setFormatter(new LineFormatter(null, null, true))
137
            );
138
        }
139
140 1
        return self::$logger;
141
    }
142
143
    /**
144
     * Initialise debug log (deprecated)
145
     *
146
     * @param string $path
147
     *
148
     * @return LoggerInterface
149
     * @throws Exception
150
     *
151
     * @deprecated Initialise a preconfigured logger instance instead.
152
     */
153 2
    public static function initDebugLog($path)
154
    {
155 2
        (defined('PHPUNIT_TESTSUITE') && PHPUNIT_TESTSUITE) || trigger_error(__METHOD__ . ' is deprecated and will be removed soon. Initialise with a preconfigured logger instance instead using "TelegramLog::initialize($logger)".', E_USER_DEPRECATED);
156
157 2
        if ($path === null || $path === '') {
158 1
            throw new TelegramLogException('Empty path for debug log');
159
        }
160 1
        self::initialize();
161
162
        // Deprecated code used as fallback.
163 1
        if (self::$logger instanceof Logger) {
164 1
            self::$debug_log_path = $path;
165
166 1
            self::$logger->pushHandler(
167 1
                (new StreamHandler(self::$debug_log_path, Logger::DEBUG))
168 1
                    ->setFormatter(new LineFormatter(null, null, true))
169
            );
170
        }
171
172 1
        return self::$logger;
173
    }
174
175
    /**
176
     * Initialise update log (deprecated)
177
     *
178
     * @param string $path
179
     *
180
     * @return LoggerInterface
181
     * @throws Exception
182
     *
183
     * @deprecated Initialise a preconfigured logger instance instead.
184
     */
185 2
    public static function initUpdateLog($path)
186
    {
187 2
        (defined('PHPUNIT_TESTSUITE') && PHPUNIT_TESTSUITE) || trigger_error(__METHOD__ . ' is deprecated and will be removed soon. Initialise with a preconfigured logger instance instead using "TelegramLog::initialize($logger)".', E_USER_DEPRECATED);
188
189 2
        if ($path === null || $path === '') {
190 1
            throw new TelegramLogException('Empty path for update log');
191
        }
192 1
        self::$update_log_path = $path;
193
194 1
        if (self::$update_logger === null || self::$update_logger instanceof NullLogger) {
195 1
            self::$update_logger = new Logger('bot_update_log');
196
197 1
            self::$update_logger->pushHandler(
198 1
                (new StreamHandler(self::$update_log_path, Logger::INFO))
199 1
                    ->setFormatter(new LineFormatter('%message%' . PHP_EOL))
200
            );
201
        }
202
203 1
        return self::$update_logger;
204
    }
205
206
    /**
207
     * Get the stream handle of the temporary debug output
208
     *
209
     * @return mixed The stream if debug is active, else false
210
     */
211
    public static function getDebugLogTempStream()
212
    {
213
        if ((self::$debug_log_temp_stream_handle === null) && $temp_stream_handle = fopen('php://temp', 'wb+')) {
214
            self::$debug_log_temp_stream_handle = $temp_stream_handle;
215
        }
216
217
        return self::$debug_log_temp_stream_handle;
218
    }
219
220
    /**
221
     * Write the temporary debug stream to log and close the stream handle
222
     *
223
     * @param string $message Message (with placeholder) to write to the debug log
224
     */
225
    public static function endDebugLogTempStream($message = '%s')
226
    {
227
        if (is_resource(self::$debug_log_temp_stream_handle)) {
228
            rewind(self::$debug_log_temp_stream_handle);
229
            self::debug(sprintf($message, stream_get_contents(self::$debug_log_temp_stream_handle)));
230
            fclose(self::$debug_log_temp_stream_handle);
231
            self::$debug_log_temp_stream_handle = null;
232
        }
233
    }
234
235
    /**
236
     * Is error log active
237
     *
238
     * @return bool
239
     *
240
     * @deprecated Initialise a preconfigured logger instance instead.
241
     */
242
    public static function isErrorLogActive()
243
    {
244
        (defined('PHPUNIT_TESTSUITE') && PHPUNIT_TESTSUITE) || trigger_error(__METHOD__ . ' is deprecated and will be removed soon. Initialise with a preconfigured logger instance instead using "TelegramLog::initialize($logger)".', E_USER_DEPRECATED);
245
        return self::$error_log_path !== null;
246
    }
247
248
    /**
249
     * Is debug log active
250
     *
251
     * @return bool
252
     *
253
     * @deprecated Initialise a preconfigured logger instance instead.
254
     */
255
    public static function isDebugLogActive()
256
    {
257
        (defined('PHPUNIT_TESTSUITE') && PHPUNIT_TESTSUITE) || trigger_error(__METHOD__ . ' is deprecated and will be removed soon. Initialise with a preconfigured logger instance instead using "TelegramLog::initialize($logger)".', E_USER_DEPRECATED);
258
        return self::$debug_log_path !== null;
259
    }
260
261
    /**
262
     * Is update log active
263
     *
264
     * @return bool
265
     *
266
     * @deprecated Initialise a preconfigured logger instance instead.
267
     */
268
    public static function isUpdateLogActive()
269
    {
270
        (defined('PHPUNIT_TESTSUITE') && PHPUNIT_TESTSUITE) || trigger_error(__METHOD__ . ' is deprecated and will be removed soon. Initialise with a preconfigured logger instance instead using "TelegramLog::initialize($logger)".', E_USER_DEPRECATED);
271
        return self::$update_log_path !== null;
272
    }
273
274
    /**
275
     * Handle any logging method call.
276
     *
277
     * @param string $name
278
     * @param array  $arguments
279
     */
280 6
    public static function __callStatic($name, array $arguments)
281
    {
282
        // Get the correct logger instance.
283 6
        $logger = null;
284 6
        if (in_array($name, ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug',], true)) {
285 5
            $logger = self::$logger;
286 2
        } elseif ($name === 'update') {
287 2
            $logger = self::$update_logger;
288 2
            $name   = 'info';
289
        }
290
291
        // Clearly we have no logging enabled.
292 6
        if ($logger === null) {
293
            return;
294
        }
295
296
        // Replace any placeholders from the passed context.
297 6
        if (count($arguments) >= 2) {
298 4
            if (is_array($arguments[1])) {
299 4
                $arguments[0] = self::interpolate($arguments[0], $arguments[1]);
300
            } else {
301
                // @todo Old parameter passing active, should be removed in the near future.
302 4
                $arguments[0] = vsprintf($arguments[0], array_splice($arguments, 1));
303
            }
304
        }
305
306 6
        call_user_func_array([$logger, $name], $arguments);
307 6
    }
308
309
    /**
310
     * Interpolates context values into the message placeholders.
311
     *
312
     * @see https://www.php-fig.org/psr/psr-3/#12-message
313
     *
314
     * @param string $message
315
     * @param array  $context
316
     *
317
     * @return string
318
     */
319 4
    protected static function interpolate($message, array $context = [])
320
    {
321
        // build a replacement array with braces around the context keys
322 4
        $replace = [];
323 4
        foreach ($context as $key => $val) {
324
            // check that the value can be casted to string
325 4
            if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
326 4
                $replace['{' . $key . '}'] = $val;
327
            }
328
        }
329
330
        // interpolate replacement values into the message and return
331 4
        return strtr($message, $replace);
332
    }
333
}
334