Passed
Push — main ( 50e526...d959dc )
by Sammy
04:27 queued 02:46
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 \Psr\Log\LogLevel;
17
use HexMakina\Debugger\Debugger;
18
use HexMakina\Interfaces\StateAgentInterface;
0 ignored issues
show
The type HexMakina\Interfaces\StateAgentInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
20
class LogLaddy extends \Psr\Log\AbstractLogger
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 $hasHaltingMessages = false;
28
29
    private $state_agent = null;
30
31
32
    public function __construct(StateAgentInterface $agent)
33
    {
34
        $this->state_agent = $agent;
35
        $this->setHandlers();
36
    }
37
38
    public function setHandlers()
39
    {
40
        set_error_handler([$this, 'errorHandler']);
41
        set_exception_handler([$this, 'exceptionHandler']);
42
    }
43
44
    public function restoreHandlers()
45
    {
46
        restore_error_handler();
47
        restore_exception_handler();
48
    }
49
50
    /*
51
    * handler for errors
52
    * use set_error_handler('\HexMakina\kadro\Logger\LogLaddy::error_handler')
53
    */
54
    public function errorHandler($level, $message, $file = '', $line = 0)
55
    {
56
        $loglevel = self::mapErrorLevelToLogLevel($level);
57
        $this->$loglevel($message, ['file' => $file, 'line' => $line, 'trace' => debug_backtrace()]);
58
    }
59
60
    /*
61
    * static handlers for throwables,
62
    * use set_exception_handler('\HexMakina\kadro\Logger\LogLaddy::exception_handler');
63
    */
64
    public function exceptionHandler(\Throwable $throwable)
65
    {
66
        if ($throwable instanceof \Exception) {
67
            $this->alert(self::USER_EXCEPTION, [$throwable]);
68
        } elseif ($throwable instanceof \Error) {
69
            $this->error(self::INTERNAL_ERROR, [$throwable]);
70
        } else {
71
            $this->critical('Caught an unknown Throwable. This breaks everything.', [$throwable]);
72
        }
73
    }
74
75
    public function systemHalted($level)
76
    {
77
        switch ($level) {
78
            case LogLevel::ERROR:
79
            case LogLevel::CRITICAL:
80
            case LogLevel::ALERT:
81
            case LogLevel::EMERGENCY:
82
                return true;
83
        }
84
        return false;
85
    }
86
87
  // -- Implementation of LoggerInterface::log(), all other methods are in LoggerTrait
88
89
    public function log($level, $message, array $context = [])
90
    {
91
        $display_error = null;
92
93
      // --- Handles Throwables (exception_handler())
94
        if ($message === self::INTERNAL_ERROR || $message === self::USER_EXCEPTION) {
95
            $this->hasHaltingMessages = true;
96
            if (($context = current($context)) !== false) {
97
                $display_error = Debugger::formatThrowable($context);
98
                $display_error .= PHP_EOL . Debugger::tracesToString($context->getTrace(), false);
99
                error_log($display_error);
100
                self::HTTP500($display_error);
101
            }
102
        } elseif ($this->systemHalted($level)) { // analyses error level
103
            $display_error = sprintf(
104
                PHP_EOL . '%s in file %s:%d' . PHP_EOL . '%s',
105
                $level,
106
                Debugger::formatFilename($context['file']),
107
                $context['line'],
108
                $message
109
            );
110
111
            error_log($display_error);
112
113
            $display_error .= PHP_EOL . Debugger::tracesToString($context['trace'], true);
114
            self::HTTP500($display_error);
115
        } else {// --- Handles user messages, through SESSION storage
116
            $this->state_agent->addMessage($level, $message, $context);
117
        }
118
    }
119
120
    public static function HTTP500($display_error)
121
    {
122
        Debugger::displayErrors($display_error);
123
        http_response_code(500);
124
    }
125
126
  // -- Allows to know if script must be halted after fatal error
127
  // TODO NEH.. not good
128
129
    public function hasHaltingMessages()
130
    {
131
        return $this->hasHaltingMessages;
132
    }
133
134
135
  // -- Error level mapping from \Psr\Log\LogLevel.php & http://php.net/manual/en/errorfunc.constants.php
136
  /** Error level meaning , from \Psr\Log\LogLevel.php
137
   * const EMERGENCY = 'emergency'; // System is unusable.
138
   * const ALERT     = 'alert'; // Action must be taken immediately, Example: Entire website down, database unavailable, etc.
139
   * const CRITICAL  = 'critical';  // Application component unavailable, unexpected exception.
140
   * const ERROR     = 'error'; // Run time errors that do not require immediate action
141
   * const WARNING   = 'warning'; // Exceptional occurrences that are not errors, undesirable things that are not necessarily wrong
142
   * const NOTICE    = 'notice'; // Normal but significant events.
143
   * const INFO      = 'info'; // Interesting events. User logs in, SQL logs.
144
   * const DEBUG     = 'debug'; // Detailed debug information.
145
  */
146
    private static function mapErrorLevelToLogLevel($level): string
147
    {
148
      // http://php.net/manual/en/errorfunc.constants.php
149
        $m = [];
150
151
        $m[E_ERROR] = $m[E_PARSE] = $m[E_CORE_ERROR] = $m[E_COMPILE_ERROR] = $m[E_USER_ERROR] = $m[E_RECOVERABLE_ERROR] = LogLevel::ALERT;
152
        $m[1] = $m[4] = $m[16] = $m[64] = $m[256] = $m[4096] = LogLevel::ALERT;
153
154
        $m[E_WARNING] = $m[E_CORE_WARNING] = $m[E_COMPILE_WARNING] = $m[E_USER_WARNING] = LogLevel::CRITICAL;
155
        $m[2] = $m[32] = $m[128] = $m[512] = LogLevel::CRITICAL;
156
157
        $m[E_NOTICE] = $m[E_USER_NOTICE] = LogLevel::ERROR;
158
        $m[8] = $m[1024] = LogLevel::ERROR;
159
160
        $m[E_STRICT] = $m[E_DEPRECATED] = $m[E_USER_DEPRECATED] = $m[E_ALL] = LogLevel::DEBUG;
161
        $m[2048] = $m[8192] = $m[16384] = $m[32767] = LogLevel::DEBUG;
162
163
        if (isset($m[$level])) {
164
            return $m[$level];
165
        }
166
167
        throw new \Exception(__FUNCTION__ . "($level): $level is unknown");
168
    }
169
}
170