Passed
Push — main ( c0116a...5be53a )
by Sammy
01:35
created

LogLaddy::HTTP_500()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 3
c 1
b 0
f 1
dl 0
loc 5
rs 10
cc 1
nc 1
nop 1
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
0 ignored issues
show
Bug introduced by
The type HexMakina\LogLaddy\Psr\Log\LoggerTrait was not found. Did you mean Psr\Log\LoggerTrait? If so, make sure to prefix the type with \.
Loading history...
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);
118
        http_response_code(500);
119
        die;
0 ignored issues
show
Best Practice introduced by
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