Completed
Pull Request — master (#462)
by Richard
15:21
created

Logger::info()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1
Metric Value
dl 0
loc 4
rs 10
ccs 3
cts 3
cp 1
cc 1
eloc 2
nc 1
nop 2
crap 1
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
namespace Xoops\Core;
13
14
use Psr\Log\LogLevel;
15
use Psr\Log\LoggerInterface;
16
17
/**
18
 * Xoops\Core\Logger - dispatch log requests to any registered loggers.
19
 *
20
 * No logging is done in this class, but any logger, implemented as a
21
 * module or extension, can register as a logger using the addLogger()
22
 * method. Multiple loggers can be registered, and each will be
23
 * invoked in turn for each log() call.
24
 *
25
 * Such loggers are expected to implement the PSR-3 LoggerInterface.
26
 * In addition, any logger that generates output as part of the XOOPS
27
 * delivered page should implement the quiet() method, to disable output.
28
 *
29
 * Loggers are managed this way so that any routine may easily add a
30
 * log entry without needing to know any details of the implementation.
31
 *
32
 * Not all events are published through this mechanism, only specific requests
33
 * to log() or related methods. Individual loggers may listen for events (i.e.
34
 * preloads) or other sources and gain access to detailed debugging information.
35
 *
36
 * @category  Xoops\Core\Logger
37
 * @package   Logger
38
 * @author    Richard Griffith <[email protected]>
39
 * @copyright 2013 XOOPS Project (http://xoops.org)
40
 * @license   GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
41
 * @version   Release: 2.6.0
42
 * @link      http://xoops.org
43
 * @since     2.6.0
44
 */
45
class Logger implements LoggerInterface
46
{
47
    /**
48
     * @var LoggerInterface[] chain of PSR-3 compatible loggers to call
49
     */
50
    private $loggers = array();
51
52
    /**
53
     * @var boolean do we have active loggers?
54
     */
55
    private $logging_active = false;
56
57
    /**
58
     * @var boolean just to prevent fatal legacy errors. Does nothing. Stop it!
59
     */
60
    //public $activated = false;
61
62
    /**
63
     * Get the Xoops\Core\Logger instance
64
     *
65
     * @return Logger object
66
     */
67 18
    public static function getInstance()
68
    {
69 18
        static $instance;
70 18
        if (!isset($instance)) {
71
            $class = __CLASS__;
72
            $instance = new $class();
73
            // Always catch errors, for security reasons
74
            set_error_handler(array($instance, 'handleError'));
75
            // grab any uncaught exception
76
            set_exception_handler(array($instance, 'handleException'));
77
        }
78
79 18
        return $instance;
80
    }
81
82
    /**
83
     * Error handling callback.
84
     *
85
     * This will
86
     *
87
     * @param integer $errorNumber error number
88
     * @param string  $errorString error message
89
     * @param string  $errorFile   file
90
     * @param integer $errorLine   line number
91
     *
92
     * @return void
93
     */
94 53
    public function handleError($errorNumber, $errorString, $errorFile, $errorLine)
95
    {
96 53
        if ($this->logging_active && ($errorNumber & error_reporting())) {
97
98
            // if an error occurs before a locale is established,
99
            // we still need messages, so check and deal with it
100
101 5
            $msg = ': ' . sprintf(
102 5
                (class_exists('\XoopsLocale', false) ? \XoopsLocale::EF_LOGGER_FILELINE : "%s in file %s line %s"),
103 5
                $this->sanitizePath($errorString),
104 5
                $this->sanitizePath($errorFile),
105
                $errorLine
106 5
            );
107
108
            switch ($errorNumber) {
109 5 View Code Duplication
                case E_USER_NOTICE:
110 1
                    $msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_ERROR : '*Error:') . $msg;
111 1
                    $this->log(LogLevel::NOTICE, $msg);
112 1
                    break;
113 4 View Code Duplication
                case E_NOTICE:
114 1
                    $msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_NOTICE : '*Notice:') . $msg;
115 1
                    $this->log(LogLevel::NOTICE, $msg);
116 1
                    break;
117 3 View Code Duplication
                case E_WARNING:
118 1
                    $msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_WARNING : '*Warning:') . $msg;
119 1
                    $this->log(LogLevel::WARNING, $msg);
120 1
                    break;
121 2 View Code Duplication
                case E_STRICT:
122 1
                    $msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_STRICT : '*Strict:') . $msg;
123 1
                    $this->log(LogLevel::WARNING, $msg);
124 1
                    break;
125 1 View Code Duplication
                case E_USER_ERROR:
126
                    $msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_ERROR : '*Error:') . $msg;
127
                    @$this->log(LogLevel::CRITICAL, $msg);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
128
                    break;
129 1 View Code Duplication
                default:
130 1
                    $msg = (class_exists('\XoopsLocale', false) ? \XoopsLocale::E_LOGGER_UNKNOWN : '*Unknown:') . $msg;
131 1
                    $this->log(LogLevel::ERROR, $msg);
132 1
                    break;
133 1
            }
134 5
        }
135
136 53
        if ($errorNumber == E_USER_ERROR) {
137
            $trace = true;
138
            if (substr($errorString, 0, '8') === 'notrace:') {
139
                $trace = false;
140
                $errorString = substr($errorString, 8);
141
            }
142
            $this->reportFatalError($errorString);
143
            if ($trace) {
144
                $trace = debug_backtrace();
145
                array_shift($trace);
146
                if ('cli' === php_sapi_name()) {
147
                    foreach ($trace as $step) {
148
                        if (isset($step['file'])) {
149
                            fprintf(STDERR, "%s (%d)\n", $this->sanitizePath($step['file']), $step['line']);
150
                        }
151
                    }
152
                } else {
153
                    echo "<div style='color:#f0f0f0;background-color:#f0f0f0'>" . _XOOPS_FATAL_BACKTRACE . ":<br />";
154
                    foreach ($trace as $step) {
155
                        if (isset($step['file'])) {
156
                            printf("%s (%d)\n<br />", $this->sanitizePath($step['file']), $step['line']);
157
                        }
158
                    }
159
                    echo '</div>';
160
                }
161
            }
162
            exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method handleError() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
163
        }
164 53
    }
165
166
    /**
167
     * Exception handling callback.
168
     *
169
     * This will
170
     *
171
     * @param \Exception|\Throwable $e uncaught Exception or Error
172
     *
173
     * @return void
174
     */
175
    public function handleException($e)
176
    {
177
        $msg = $e->getMessage();
178
        $this->reportFatalError($msg);
179
    }
180
181
    /**
182
     * Announce fatal error, attempt to log
183
     *
184
     * @param string $msg error message to report
185
     *
186
     * @return void
187
     */
188
    private function reportFatalError($msg)
189
    {
190
        $msg=$this->sanitizePath($msg);
191
        if ('cli' === php_sapi_name()) {
192
            fprintf(STDERR, "\nError : %s\n", $msg);
193
        } else {
194
            printf(_XOOPS_FATAL_MESSAGE, XOOPS_URL, $msg);
195
        }
196
        @$this->log(LogLevel::CRITICAL, $msg);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
197
    }
198
199
    /**
200
     * clean a path to remove sensitive details
201
     *
202
     * @param string $message text to sanitize
203
     *
204
     * @return string sanitized message
205
     */
206 21
    public function sanitizePath($message)
207
    {
208
        $stringsToClean = array(
209 21
            '\\',
210 21
            \XoopsBaseConfig::get('var-path'),
211 21
            str_replace('\\', '/', realpath(\XoopsBaseConfig::get('var-path'))),
212 21
            \XoopsBaseConfig::get('lib-path'),
213 21
            str_replace('\\', '/', realpath(\XoopsBaseConfig::get('lib-path'))),
214 21
            \XoopsBaseConfig::get('root-path'),
215 21
            str_replace('\\', '/', realpath(\XoopsBaseConfig::get('root-path'))),
216 21
            \XoopsBaseConfig::get('db-name') . '.',
217 21
            \XoopsBaseConfig::get('db-name'),
218 21
            \XoopsBaseConfig::get('db-prefix') . '_',
219 21
            \XoopsBaseConfig::get('db-user'),
220 21
            \XoopsBaseConfig::get('db-pass'),
221 21
        );
222
223
        $replacementStings = array(
224 21
            '/',
225 21
            'VAR',
226 21
            'VAR',
227 21
            'LIB',
228 21
            'LIB',
229 21
            'ROOT',
230 21
            'ROOT',
231 21
            '',
232 21
            '',
233 21
            '',
234 21
            '***',
235 21
            '***',
236 21
        );
237
238 21
        $message = str_replace($stringsToClean, $replacementStings, $message);
239
240 21
        return $message;
241
    }
242
243
    /**
244
     * add a PSR-3 compatible logger to the chain
245
     *
246
     * @param object $logger a PSR-3 compatible logger object
247
     *
248
     * @return void
249
     */
250 22
    public function addLogger($logger)
251
    {
252 22
        if (is_object($logger) && method_exists($logger, 'log')) {
253 22
                $this->loggers[] = $logger;
254 22
                $this->logging_active = true;
255 22
        }
256 22
    }
257
258
    /**
259
     * System is unusable.
260
     *
261
     * @param string $message message
262
     * @param array  $context array of context data for this log entry
263
     *
264
     * @return void
265
     */
266 1
    public function emergency($message, array $context = array())
267
    {
268 1
        $this->log(LogLevel::EMERGENCY, $message, $context);
269 1
    }
270
271
    /**
272
     * Action must be taken immediately.
273
     *
274
     * Example: Entire website down, database unavailable, etc. This should
275
     * trigger the SMS alerts and wake you up.
276
     *
277
     * @param string $message message
278
     * @param array  $context array of context data for this log entry
279
     *
280
     * @return void
281
     */
282 1
    public function alert($message, array $context = array())
283
    {
284 1
        $this->log(LogLevel::ALERT, $message, $context);
285 1
    }
286
287
    /**
288
     * Critical conditions.
289
     *
290
     * Example: Application component unavailable, unexpected exception.
291
     *
292
     * @param string $message message
293
     * @param array  $context array of context data for this log entry
294
     *
295
     * @return void
296
     */
297 1
    public function critical($message, array $context = array())
298
    {
299 1
        $this->log(LogLevel::CRITICAL, $message, $context);
300 1
    }
301
302
    /**
303
     * Runtime errors that do not require immediate action but should typically
304
     * be logged and monitored.
305
     *
306
     * @param string $message message
307
     * @param array  $context array of context data for this log entry
308
     *
309
     * @return void
310
     */
311 1
    public function error($message, array $context = array())
312
    {
313 1
        $this->log(LogLevel::ERROR, $message, $context);
314 1
    }
315
316
    /**
317
     * Exceptional occurrences that are not errors.
318
     *
319
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
320
     * that are not necessarily wrong.
321
     *
322
     * @param string $message message
323
     * @param array  $context array of context data for this log entry
324
     *
325
     * @return void
326
     */
327 1
    public function warning($message, array $context = array())
328
    {
329 1
        $this->log(LogLevel::WARNING, $message, $context);
330 1
    }
331
332
    /**
333
     * Normal but significant events.
334
     *
335
     * @param string $message message
336
     * @param array  $context array of context data for this log entry
337
     *
338
     * @return void
339
     */
340 1
    public function notice($message, array $context = array())
341
    {
342 1
        $this->log(LogLevel::NOTICE, $message, $context);
343 1
    }
344
345
    /**
346
     * Interesting events.
347
     *
348
     * Example: User logs in, SQL logs.
349
     *
350
     * @param string $message message
351
     * @param array  $context array of context data for this log entry
352
     *
353
     * @return void
354
     */
355 1
    public function info($message, array $context = array())
356
    {
357 1
        $this->log(LogLevel::INFO, $message, $context);
358 1
    }
359
360
    /**
361
     * Detailed debug information.
362
     *
363
     * @param string $message message
364
     * @param array  $context array of context data for this log entry
365
     *
366
     * @return void
367
     */
368 1
    public function debug($message, array $context = array())
369
    {
370 1
        $this->log(LogLevel::DEBUG, $message, $context);
371 1
    }
372
373
    /**
374
     * Logs with an arbitrary level.
375
     *
376
     * @param mixed  $level   PSR-3 LogLevel constant
377
     * @param string $message message
378
     * @param array  $context array of context data for this log entry
379
     *
380
     * @return void
381
     */
382 14
    public function log($level, $message, array $context = array())
383
    {
384 14
        if (!empty($this->loggers)) {
385 13
            foreach ($this->loggers as $logger) {
386 13
                if (is_object($logger)) {
387
                    try {
388 13
                        $logger->log($level, $message, $context);
389 13
                    } catch (\Exception $e) {
390
                        // just ignore, as we can't do anything, not even log it.
391
                    }
392 13
                }
393 13
            }
394 13
        }
395 14
    }
396
397
    /**
398
     * quiet - turn off output if output is rendered in XOOPS page output.
399
     * This is intended to assist ajax code that may fail with any extra
400
     * content the logger may introduce.
401
     *
402
     * It should have no effect on loggers using other methods, such a write
403
     * to file.
404
     *
405
     * @return void
406
     */
407 2
    public function quiet()
408
    {
409 2
        if (!empty($this->loggers)) {
410 2
            foreach ($this->loggers as $logger) {
411 2
                if (is_object($logger) && method_exists($logger, 'quiet')) {
412
                    try {
413 2
                        $logger->quiet();
414 2
                    } catch (\Exception $e) {
415
                        // just ignore, as we can't do anything, not even log it.
416
                    }
417 2
                }
418 2
            }
419 2
        }
420 2
    }
421
422
    // Deprecated uses
423
424
    /**
425
     * Keep deprecated calls from failing
426
     *
427
     * @param string $var property
428
     * @param string $val value
429
     *
430
     * @return void
431
     *
432
     * @deprecated
433
     */
434 1
    public function __set($var, $val)
435
    {
436 1
        $this->deprecatedMessage();
437
        // legacy compatibility: turn off logger display for $xoopsLogger->activated = false; usage
438 1
        if ($var==='activated' && !$val) {
439 1
            $this->quiet();
440 1
        }
441
442 1
    }
443
444
    /**
445
     * Keep deprecated calls from failing
446
     *
447
     * @param string $var property
448
     *
449
     * @return void
450
     *
451
     * @deprecated
452
     */
453 1
    public function __get($var)
454
    {
455 1
        $this->deprecatedMessage();
456 1
    }
457
458
    /**
459
     * Keep deprecated calls from failing
460
     *
461
     * @param string $method method
462
     * @param string $args   arguments
463
     *
464
     * @return void
465
     *
466
     * @deprecated
467
    */
468 1
    public function __call($method, $args)
469
    {
470 1
        $this->deprecatedMessage();
471 1
    }
472
473
    /**
474
     * issue a deprecated warning
475
     *
476
     * @return void
477
     */
478 3
    private function deprecatedMessage()
479
    {
480 3
        $xoops = \Xoops::getInstance();
481 3
        $xoops->deprecated('This use of XoopsLogger is deprecated since 2.6.0.');
482 3
    }
483
}
484