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