Completed
Push — master ( 1c54e2...48ad84 )
by Armando
02:22
created

TelegramLog::getLogText()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 3
cts 3
cp 1
rs 9.4285
cc 2
eloc 3
nc 2
nop 2
crap 2
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 Longman\TelegramBot\Exception\TelegramLogException;
14
use Monolog\Formatter\LineFormatter;
15
use Monolog\Handler\StreamHandler;
16
use Monolog\Logger;
17
18
class TelegramLog
19
{
20
    /**
21
     * Monolog instance
22
     *
23
     * @var \Monolog\Logger
24
     */
25
    static protected $monolog;
26
27
    /**
28
     * Monolog instance for update
29
     *
30
     * @var \Monolog\Logger
31
     */
32
    static protected $monolog_update;
33
34
    /**
35
     * Path for error log
36
     *
37
     * @var string
38
     */
39
    static protected $error_log_path;
40
41
    /**
42
     * Path for debug log
43
     *
44
     * @var string
45
     */
46
    static protected $debug_log_path;
47
48
    /**
49
     * Path for update log
50
     *
51
     * @var string
52
     */
53
    static protected $update_log_path;
54
55
    /**
56
     * Temporary stream handle for debug log
57
     *
58
     * @var resource|null
59
     */
60
    static protected $debug_log_temp_stream_handle;
61
62
    /**
63
     * Initialize
64
     *
65
     * Initilize monolog instance. Singleton
66
     * Is possbile provide an external monolog instance
67
     *
68
     * @param \Monolog\Logger
69
     *
70
     * @return \Monolog\Logger
71
     */
72 3
    public static function initialize(Logger $external_monolog = null)
73
    {
74 3
        if (self::$monolog === null) {
75 3
            if ($external_monolog !== null) {
76 1
                self::$monolog = $external_monolog;
77
78 1
                foreach (self::$monolog->getHandlers() as $handler) {
79 1
                    if ($handler->getLevel() === 400) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Monolog\Handler\HandlerInterface as the method getLevel() does only exist in the following implementations of said interface: Monolog\Handler\AbstractHandler, Monolog\Handler\AbstractProcessingHandler, Monolog\Handler\AbstractSyslogHandler, Monolog\Handler\AmqpHandler, Monolog\Handler\BrowserConsoleHandler, Monolog\Handler\BufferHandler, Monolog\Handler\ChromePHPHandler, Monolog\Handler\CouchDBHandler, Monolog\Handler\CubeHandler, Monolog\Handler\DeduplicationHandler, Monolog\Handler\DoctrineCouchDBHandler, Monolog\Handler\DynamoDbHandler, Monolog\Handler\ElasticSearchHandler, Monolog\Handler\ErrorLogHandler, Monolog\Handler\ExceptionTestHandler, Monolog\Handler\FilterHandler, Monolog\Handler\FingersCrossedHandler, Monolog\Handler\FirePHPHandler, Monolog\Handler\FleepHookHandler, Monolog\Handler\FlowdockHandler, Monolog\Handler\GelfHandler, Monolog\Handler\GroupHandler, Monolog\Handler\HipChatHandler, Monolog\Handler\IFTTTHandler, Monolog\Handler\LogEntriesHandler, Monolog\Handler\LogglyHandler, Monolog\Handler\MailHandler, Monolog\Handler\MandrillHandler, Monolog\Handler\MongoDBHandler, Monolog\Handler\NativeMailerHandler, Monolog\Handler\NewRelicHandler, Monolog\Handler\NullHandler, Monolog\Handler\PHPConsoleHandler, Monolog\Handler\PsrHandler, Monolog\Handler\PushoverHandler, Monolog\Handler\RavenHandler, Monolog\Handler\RedisHandler, Monolog\Handler\RollbarHandler, Monolog\Handler\RotatingFileHandler, Monolog\Handler\SamplingHandler, Monolog\Handler\SlackHandler, Monolog\Handler\SocketHandler, Monolog\Handler\StreamHandler, Monolog\Handler\StubNewRelicHandler, Monolog\Handler\StubNewR...HandlerWithoutExtension, Monolog\Handler\SwiftMailerHandler, Monolog\Handler\SyslogHandler, Monolog\Handler\SyslogUdpHandler, Monolog\Handler\TestChromePHPHandler, Monolog\Handler\TestFirePHPHandler, Monolog\Handler\TestHandler, Monolog\Handler\WhatFailureGroupHandler, Monolog\Handler\ZendMonitorHandler.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
80 1
                        self::$error_log_path = 'true';
81
                    }
82 1
                    if ($handler->getLevel() === 100) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Monolog\Handler\HandlerInterface as the method getLevel() does only exist in the following implementations of said interface: Monolog\Handler\AbstractHandler, Monolog\Handler\AbstractProcessingHandler, Monolog\Handler\AbstractSyslogHandler, Monolog\Handler\AmqpHandler, Monolog\Handler\BrowserConsoleHandler, Monolog\Handler\BufferHandler, Monolog\Handler\ChromePHPHandler, Monolog\Handler\CouchDBHandler, Monolog\Handler\CubeHandler, Monolog\Handler\DeduplicationHandler, Monolog\Handler\DoctrineCouchDBHandler, Monolog\Handler\DynamoDbHandler, Monolog\Handler\ElasticSearchHandler, Monolog\Handler\ErrorLogHandler, Monolog\Handler\ExceptionTestHandler, Monolog\Handler\FilterHandler, Monolog\Handler\FingersCrossedHandler, Monolog\Handler\FirePHPHandler, Monolog\Handler\FleepHookHandler, Monolog\Handler\FlowdockHandler, Monolog\Handler\GelfHandler, Monolog\Handler\GroupHandler, Monolog\Handler\HipChatHandler, Monolog\Handler\IFTTTHandler, Monolog\Handler\LogEntriesHandler, Monolog\Handler\LogglyHandler, Monolog\Handler\MailHandler, Monolog\Handler\MandrillHandler, Monolog\Handler\MongoDBHandler, Monolog\Handler\NativeMailerHandler, Monolog\Handler\NewRelicHandler, Monolog\Handler\NullHandler, Monolog\Handler\PHPConsoleHandler, Monolog\Handler\PsrHandler, Monolog\Handler\PushoverHandler, Monolog\Handler\RavenHandler, Monolog\Handler\RedisHandler, Monolog\Handler\RollbarHandler, Monolog\Handler\RotatingFileHandler, Monolog\Handler\SamplingHandler, Monolog\Handler\SlackHandler, Monolog\Handler\SocketHandler, Monolog\Handler\StreamHandler, Monolog\Handler\StubNewRelicHandler, Monolog\Handler\StubNewR...HandlerWithoutExtension, Monolog\Handler\SwiftMailerHandler, Monolog\Handler\SyslogHandler, Monolog\Handler\SyslogUdpHandler, Monolog\Handler\TestChromePHPHandler, Monolog\Handler\TestFirePHPHandler, Monolog\Handler\TestHandler, Monolog\Handler\WhatFailureGroupHandler, Monolog\Handler\ZendMonitorHandler.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
83 1
                        self::$debug_log_path = 'true';
84
                    }
85
                }
86
            } else {
87 2
                self::$monolog = new Logger('bot_log');
88
            }
89
        }
90
91 3
        return self::$monolog;
92
    }
93
94
    /**
95
     * Initialize error log
96
     *
97
     * @param string $path
98
     *
99
     * @return \Monolog\Logger
100
     * @throws \Longman\TelegramBot\Exception\TelegramLogException
101
     * @throws \InvalidArgumentException
102
     * @throws \Exception
103
     */
104 2 View Code Duplication
    public static function initErrorLog($path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
105
    {
106 2
        if ($path === null || $path === '') {
107 1
            throw new TelegramLogException('Empty path for error log');
108
        }
109 1
        self::initialize();
110 1
        self::$error_log_path = $path;
111
112 1
        return self::$monolog->pushHandler(
113 1
            (new StreamHandler(self::$error_log_path, Logger::ERROR))
114 1
                ->setFormatter(new LineFormatter(null, null, true))
115
        );
116
    }
117
118
    /**
119
     * Initialize debug log
120
     *
121
     * @param string $path
122
     *
123
     * @return \Monolog\Logger
124
     * @throws \Longman\TelegramBot\Exception\TelegramLogException
125
     * @throws \InvalidArgumentException
126
     * @throws \Exception
127
     */
128 2 View Code Duplication
    public static function initDebugLog($path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
129
    {
130 2
        if ($path === null || $path === '') {
131 1
            throw new TelegramLogException('Empty path for debug log');
132
        }
133 1
        self::initialize();
134 1
        self::$debug_log_path = $path;
135
136 1
        return self::$monolog->pushHandler(
137 1
            (new StreamHandler(self::$debug_log_path, Logger::DEBUG))
138 1
                ->setFormatter(new LineFormatter(null, null, true))
139
        );
140
    }
141
142
    /**
143
     * Get the stream handle of the temporary debug output
144
     *
145
     * @return mixed The stream if debug is active, else false
146
     */
147
    public static function getDebugLogTempStream()
148
    {
149
        if (self::$debug_log_temp_stream_handle === null) {
150
            if (!self::isDebugLogActive()) {
151
                return false;
152
            }
153
            self::$debug_log_temp_stream_handle = fopen('php://temp', 'w+b');
154
        }
155
156
        return self::$debug_log_temp_stream_handle;
157
    }
158
159
    /**
160
     * Write the temporary debug stream to log and close the stream handle
161
     *
162
     * @param string $message Message (with placeholder) to write to the debug log
163
     */
164
    public static function endDebugLogTempStream($message = '%s')
165
    {
166
        if (is_resource(self::$debug_log_temp_stream_handle)) {
167
            rewind(self::$debug_log_temp_stream_handle);
168
            self::debug($message, stream_get_contents(self::$debug_log_temp_stream_handle));
169
            fclose(self::$debug_log_temp_stream_handle);
170
            self::$debug_log_temp_stream_handle = null;
171
        }
172
    }
173
174
    /**
175
     * Initialize update log
176
     *
177
     * Initilize monolog instance. Singleton
178
     * Is possbile provide an external monolog instance
179
     *
180
     * @param string $path
181
     *
182
     * @return \Monolog\Logger
183
     * @throws \Longman\TelegramBot\Exception\TelegramLogException
184
     * @throws \InvalidArgumentException
185
     * @throws \Exception
186
     */
187 2
    public static function initUpdateLog($path)
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 1
        if (self::$monolog_update === null) {
194 1
            self::$monolog_update = new Logger('bot_update_log');
195
            // Create a formatter
196 1
            $output = '%message%' . PHP_EOL;
197 1
            $formatter = new LineFormatter($output);
198
199
            // Update handler
200 1
            $update_handler = new StreamHandler(self::$update_log_path, Logger::INFO);
201 1
            $update_handler->setFormatter($formatter);
202
203 1
            self::$monolog_update->pushHandler($update_handler);
204
        }
205
206 1
        return self::$monolog;
207
    }
208
209
    /**
210
     * Is error log active
211
     *
212
     * @return bool
213
     */
214 4
    public static function isErrorLogActive()
215
    {
216 4
        return self::$error_log_path !== null;
217
    }
218
219
    /**
220
     * Is debug log active
221
     *
222
     * @return bool
223
     */
224 2
    public static function isDebugLogActive()
225
    {
226 2
        return self::$debug_log_path !== null;
227
    }
228
229
    /**
230
     * Is update log active
231
     *
232
     * @return bool
233
     */
234 1
    public static function isUpdateLogActive()
235
    {
236 1
        return self::$update_log_path !== null;
237
    }
238
239
    /**
240
     * Report error log
241
     *
242
     * @param string $text
243
     */
244 4
    public static function error($text)
245
    {
246 4
        if (self::isErrorLogActive()) {
247 4
            $text = self::getLogText($text, func_get_args());
248 4
            self::$monolog->error($text);
249
        }
250 4
    }
251
252
    /**
253
     * Report debug log
254
     *
255
     * @param string $text
256
     */
257 2
    public static function debug($text)
258
    {
259 2
        if (self::isDebugLogActive()) {
260 2
            $text = self::getLogText($text, func_get_args());
261 2
            self::$monolog->debug($text);
262
        }
263 2
    }
264
265
    /**
266
     * Report update log
267
     *
268
     * @param string $text
269
     */
270 1
    public static function update($text)
271
    {
272 1
        if (self::isUpdateLogActive()) {
273 1
            $text = self::getLogText($text, func_get_args());
274 1
            self::$monolog_update->info($text);
275
        }
276 1
    }
277
278
    /**
279
     * Applies vsprintf to the text if placeholder replacements are passed along.
280
     *
281
     * @param string $text
282
     * @param array  $args
283
     *
284
     * @return string
285
     */
286 6
    protected static function getLogText($text, array $args = [])
287
    {
288
        // Pop the $text off the array, as it gets passed via func_get_args().
289 6
        array_shift($args);
290
291
        // Suppress warning if placeholders don't match out.
292 6
        return @vsprintf($text, $args) ?: $text;
293
    }
294
}
295