Passed
Push — main ( 786b75...fcb1f8 )
by Sammy
01:57
created

LogLaddy.php (1 issue)

Labels
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
    public function set_handlers()
44
    {
45
        set_error_handler([$this, 'error_handler']);
46
        set_exception_handler([$this, 'exception_handler']);
47
    }
48
49
    public function restore_handlers()
50
    {
51
        restore_error_handler();
52
        restore_exception_handler();
53
    }
54
55
    /*
56
    * handler for errors
57
    * use set_error_handler('\HexMakina\kadro\Logger\LogLaddy::error_handler')
58
    */
59
    public function error_handler($level, $message, $file = '', $line = 0)
60
    {
61
        $loglevel = self::map_error_level_to_log_level($level);
62
        $this->$loglevel($message, ['file' => $file, 'line' => $line, 'trace' => debug_backtrace()]);
63
    }
64
65
    /*
66
    * static handlers for throwables,
67
    * use set_exception_handler('\HexMakina\kadro\Logger\LogLaddy::exception_handler');
68
    */
69
    public function exception_handler(\Throwable $throwable)
70
    {
71
        if ($throwable instanceof \Exception) {
72
            $this->alert(self::USER_EXCEPTION, [$throwable]);
73
        } elseif ($throwable instanceof \Error) {
74
            $this->notice(self::INTERNAL_ERROR, [$throwable]);
75
        }
76
        else {
77
            $this->critical('Caught an unknown Throwable. This breaks everything.', [$throwable]);
78
        }
79
    }
80
81
    public function system_halted($level)
82
    {
83
        switch ($level) {
84
            case LogLevel::ERROR:
85
            case LogLevel::CRITICAL:
86
            case LogLevel::ALERT:
87
            case LogLevel::EMERGENCY:
88
                return true;
89
        }
90
        return false;
91
    }
92
93
  // -- Implementation of LoggerInterface::log(), all other methods are in LoggerTrait
94
95
    public function log($level, $message, array $context = [])
96
    {
97
        $display_error = null;
98
99
      // --- Handles Throwables (exception_handler())
100
        if ($message === self::INTERNAL_ERROR || $message === self::USER_EXCEPTION) {
101
            $this->has_halting_messages = true;
102
            if (($context = current($context)) !== false) {
103
                $display_error = Debugger::formatThrowable($context);
104
                $display_error .= PHP_EOL.Debugger::tracesToString($context->getTrace(), false);
105
                error_log($display_error);
106
                self::HTTP_500($display_error);
107
            }
108
        } elseif ($this->system_halted($level)) { // analyses error level
109
            $display_error = sprintf(
110
                PHP_EOL . '%s in file %s:%d' . PHP_EOL . '%s',
111
                $level,
112
                Debugger::formatFilename($context['file']),
113
                $context['line'],
114
                $message
115
            );
116
117
            error_log($display_error);
118
119
            $display_error .= PHP_EOL.Debugger::tracesToString($context['trace'], true);
120
            self::HTTP_500($display_error);
121
        } else {// --- Handles user messages, through SESSION storage
122
            $this->report_to_user($level, $message, $context);
123
        }
124
    }
125
126
    public static function HTTP_500($display_error)
127
    {
128
        Debugger::displayErrors($display_error);
0 ignored issues
show
The method displayErrors() does not exist on HexMakina\Debugger\Debugger. Did you maybe mean display_errors()? ( Ignorable by Annotation )

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

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