Passed
Push — main ( 5be53a...df551c )
by Sammy
01:31
created

LogLaddy.php (2 issues)

Severity
1
<?php
2
3
/*
4
 * LogLaddy
5
 *
6
 * I carry a log – yes. Is it funny to you? It is not to me.
7
 * Behind all things are reasons. Reasons can even explain the absurd.
8
 *
9
 * LogLaddy manages error reporting
10
 * PSR-3 Compliant, with a NICE bonus
11
 */
12
13
namespace HexMakina\LogLaddy;
14
15
// Debugger
16
use HexMakina\Debugger\Debugger;
17
18
class LogLaddy implements LoggerInterface
19
{
20
    use \Psr\Log\LoggerTrait;           // PSR implementation
21
22
    public const REPORTING_USER = 'user_messages';
23
    public const INTERNAL_ERROR = 'error';
24
    public const USER_EXCEPTION = 'exception';
25
    public const LOG_LEVEL_SUCCESS = 'ok';
26
27
    private $has_halting_messages = false;
28
29
  /**
30
   * Everything went fine, which is always nice.
31
   * LogLaddy is a bit more optimistic than PSRLog
32
   * @param string $message
33
   * @param array  $context
34
   *
35
   * @return void
36
   */
37
    public function nice($message, array $context = array())
38
    {
39
        $this->log(LogLevel::NICE, $message, $context);
40
    }
41
42
    /*
43
    * static handlers for error,
44
    * use set_error_handler('\HexMakina\kadro\Logger\LogLaddy::error_handler')
45
    */
46
    public static function error_handler($level, $message, $file = '', $line = 0)
47
    {
48
        $loglevel = self::map_error_level_to_log_level($level);
49
50
        (new LogLaddy())->$loglevel($message, ['file' => $file, 'line' => $line, 'trace' => debug_backtrace()]);
51
    }
52
53
    /*
54
    * static handlers for throwables,
55
    * use set_exception_handler('\HexMakina\kadro\Logger\LogLaddy::exception_handler');
56
    */
57
    public static function exception_handler(\Throwable $throwable)
58
    {
59
        $lad = new LogLaddy();
60
61
        if ($throwable instanceof \Exception) {
62
            $lad->alert(self::USER_EXCEPTION, [$throwable]);
63
        } elseif ($throwable instanceof \Error) {
64
            $lad->notice(self::INTERNAL_ERROR, [$throwable]);
65
        }
66
        else {
67
            $lad->critical('Caught an unknown Throwable. This breaks everything.', [$throwable]);
68
        }
69
    }
70
71
    public function system_halted($level)
72
    {
73
        switch ($level) {
74
            case LogLevel::ERROR:
75
            case LogLevel::CRITICAL:
76
            case LogLevel::ALERT:
77
            case LogLevel::EMERGENCY:
78
                return true;
79
        }
80
        return false;
81
    }
82
83
  // -- Implementation of LoggerInterface::log(), all other methods are in LoggerTrait
84
85
    public function log($level, $message, array $context = [])
86
    {
87
        $display_error = null;
88
89
      // --- Handles Throwables (exception_handler())
90
        if ($message === self::INTERNAL_ERROR || $message === self::USER_EXCEPTION) {
91
            $this->has_halting_messages = true;
92
            if (($context = current($context)) !== false) {
93
                $display_error = Debugger::formatThrowable($context);
94
            }
95
            error_log($display_error);
96
            $display_error .= Debugger::tracesToString($context['trace'], false);
97
            self::HTTP_500($display_error);
98
        } elseif ($this->system_halted($level)) { // analyses error level
99
            $display_error = sprintf(
100
                PHP_EOL . '%s in file %s:%d' . PHP_EOL . '%s',
101
                $level,
102
                Debugger::formatFilename($context['file']),
103
                $context['line'],
104
                $message
105
            );
106
107
            error_log($display_error);
108
            $display_error .= Debugger::tracesToString($context['trace'], true);
109
            self::HTTP_500($display_error);
110
        } else {// --- Handles user messages, through SESSION storage
111
            $this->report_to_user($level, $message, $context);
112
        }
113
    }
114
115
    public static function HTTP_500($display_error)
116
    {
117
        self::displayErrors($display_error);
0 ignored issues
show
The method displayErrors() does not exist on HexMakina\LogLaddy\LogLaddy. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

117
        self::/** @scrutinizer ignore-call */ 
118
              displayErrors($display_error);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
118
        http_response_code(500);
119
        die;
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
120
    }
121
  // -- Allows to know if script must be halted after fatal error
122
  // TODO NEH.. not good
123
    public function has_halting_messages()
124
    {
125
        return $this->has_halting_messages === true;
126
    }
127
128
  // -- User messages
129
130
  // -- User messages:add one
131
    public function report_to_user($level, $message, $context = [])
132
    {
133
        if (!isset($_SESSION[self::REPORTING_USER])) {
134
            $_SESSION[self::REPORTING_USER] = [];
135
        }
136
137
        if (!isset($_SESSION[self::REPORTING_USER][$level])) {
138
            $_SESSION[self::REPORTING_USER][$level] = [];
139
        }
140
141
        $_SESSION[self::REPORTING_USER][$level][] = [$message, $context];
142
    }
143
144
  // -- User messages:get all
145
    public function get_user_report()
146
    {
147
        return $_SESSION[self::REPORTING_USER] ?? [];
148
    }
149
150
  // -- User messages:reset all
151
    public function clean_user_report()
152
    {
153
        unset($_SESSION[self::REPORTING_USER]);
154
    }
155
156
  // -- Error level mapping from \Psr\Log\LogLevel.php & http://php.net/manual/en/errorfunc.constants.php
157
  /** Error level meaning , from \Psr\Log\LogLevel.php
158
   * const EMERGENCY = 'emergency'; // System is unusable.
159
   * const ALERT     = 'alert'; // Action must be taken immediately, Example: Entire website down, database unavailable, etc.
160
   * const CRITICAL  = 'critical';  // Application component unavailable, unexpected exception.
161
   * const ERROR     = 'error'; // Run time errors that do not require immediate action
162
   * const WARNING   = 'warning'; // Exceptional occurrences that are not errors, undesirable things that are not necessarily wrong
163
   * const NOTICE    = 'notice'; // Normal but significant events.
164
   * const INFO      = 'info'; // Interesting events. User logs in, SQL logs.
165
   * const DEBUG     = 'debug'; // Detailed debug information.
166
  */
167
    private static function map_error_level_to_log_level($level): string
168
    {
169
      // http://php.net/manual/en/errorfunc.constants.php
170
        $m = [];
171
172
        $m[E_ERROR] = $m[E_PARSE] = $m[E_CORE_ERROR] = $m[E_COMPILE_ERROR] = $m[E_USER_ERROR] = $m[E_RECOVERABLE_ERROR] = LogLevel::ALERT;
173
        $m[1] = $m[4] = $m[16] = $m[64] = $m[256] = $m[4096] = LogLevel::ALERT;
174
175
        $m[E_WARNING] = $m[E_CORE_WARNING] = $m[E_COMPILE_WARNING] = $m[E_USER_WARNING] = LogLevel::CRITICAL;
176
        $m[2] = $m[32] = $m[128] = $m[512] = LogLevel::CRITICAL;
177
178
        $m[E_NOTICE] = $m[E_USER_NOTICE] = LogLevel::ERROR;
179
        $m[8] = $m[1024] = LogLevel::ERROR;
180
181
        $m[E_STRICT] = $m[E_DEPRECATED] = $m[E_USER_DEPRECATED] = $m[E_ALL] = LogLevel::DEBUG;
182
        $m[2048] = $m[8192] = $m[16384] = $m[32767] = LogLevel::DEBUG;
183
184
        if (isset($m[$level])) {
185
            return $m[$level];
186
        }
187
188
        throw new \Exception(__FUNCTION__ . "($level): $level is unknown");
189
    }
190
}
191