Completed
Push — master ( 1c54e2...48ad84 )
by Armando
04:25 queued 02:07
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)
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)
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