1 | <?php |
||
2 | |||
3 | namespace Yaro\LogEnvelope; |
||
4 | |||
5 | use Exception; |
||
6 | use SplFileObject; |
||
7 | use Illuminate\Support\Facades\Log; |
||
8 | use Illuminate\Support\Facades\Request; |
||
9 | use Illuminate\Support\Facades\Session; |
||
10 | use Yaro\LogEnvelope\Drivers\DriverFactory; |
||
11 | |||
12 | class LogEnvelope |
||
13 | { |
||
14 | private $config = []; |
||
15 | private $cachedConfig = []; |
||
16 | |||
17 | public function __construct() |
||
18 | { |
||
19 | $this->config['censored_fields'] = config('yaro.log-envelope.censored_fields', ['password']); |
||
20 | $this->config['except'] = config('yaro.log-envelope.except', []); |
||
21 | $this->config['count'] = config('yaro.log-envelope.lines_count', 6); |
||
22 | $this->config['drivers'] = config('yaro.log-envelope.drivers', []); |
||
23 | } // end __construct |
||
24 | |||
25 | public function send($exception) |
||
26 | { |
||
27 | $this->onBefore(); |
||
28 | |||
29 | try { |
||
30 | $data = $this->getExceptionData($exception); |
||
31 | |||
32 | if ($this->isSkipException($data['class'])) { |
||
33 | return; |
||
34 | } |
||
35 | |||
36 | foreach ($this->config['drivers'] as $driver => $driverConfig) { |
||
37 | DriverFactory::create($driver, $data)->setConfig($driverConfig)->send(); |
||
38 | } |
||
39 | } catch (Exception $e) { |
||
40 | Log::error($e); |
||
41 | } |
||
42 | |||
43 | $this->onAfter(); |
||
44 | } // end send |
||
45 | |||
46 | private function onBefore() |
||
47 | { |
||
48 | $this->cachedConfig = []; |
||
49 | $forcedConfig = config('yaro.log-envelope.force_config', []); |
||
50 | foreach ($forcedConfig as $configKey => $configValue) { |
||
51 | $this->cachedConfig[$configKey] = config($configKey); |
||
52 | } |
||
53 | if ($forcedConfig) { |
||
54 | config($forcedConfig); |
||
55 | } |
||
56 | } // end onBefore |
||
57 | |||
58 | private function onAfter() |
||
59 | { |
||
60 | if ($this->cachedConfig) { |
||
0 ignored issues
–
show
|
|||
61 | config($this->cachedConfig); |
||
62 | } |
||
63 | } // end onAfter |
||
64 | |||
65 | public function isSkipException($exceptionClass) |
||
66 | { |
||
67 | return in_array($exceptionClass, $this->config['except']); |
||
68 | } // end isSkipException |
||
69 | |||
70 | private function getExceptionData($exception) |
||
71 | { |
||
72 | $data = []; |
||
73 | |||
74 | $data['host'] = Request::server('HTTP_HOST'); |
||
75 | $data['method'] = Request::method(); |
||
76 | $data['fullUrl'] = Request::fullUrl(); |
||
77 | if (php_sapi_name() === 'cli') { |
||
78 | $data['host'] = parse_url(config('app.url'), PHP_URL_HOST); |
||
79 | $data['method'] = 'CLI'; |
||
80 | } |
||
81 | $data['exception'] = $exception->getMessage(); |
||
82 | $data['error'] = $exception->getTraceAsString(); |
||
83 | $data['line'] = $exception->getLine(); |
||
84 | $data['file'] = $exception->getFile(); |
||
85 | $data['class'] = get_class($exception); |
||
86 | $data['storage'] = array( |
||
87 | 'SERVER' => Request::server(), |
||
88 | 'GET' => Request::query(), |
||
89 | 'POST' => $_POST, |
||
90 | 'FILE' => Request::file(), |
||
91 | 'OLD' => Request::hasSession() ? Request::old() : [], |
||
92 | 'COOKIE' => Request::cookie(), |
||
93 | 'SESSION' => Request::hasSession() ? Session::all() : [], |
||
94 | 'HEADERS' => Request::header(), |
||
95 | ); |
||
96 | |||
97 | // Remove empty, false and null values |
||
98 | $data['storage'] = array_filter($data['storage']); |
||
99 | |||
100 | // Censor sensitive field values |
||
101 | array_walk_recursive($data['storage'], self::censorSensitiveFields(...)); |
||
102 | |||
103 | $count = $this->config['count']; |
||
104 | |||
105 | $data['exegutor'] = []; |
||
106 | $data['file_lines'] = []; |
||
107 | |||
108 | $file = new SplFileObject($data['file']); |
||
109 | for ($i = -1 * abs($count); $i <= abs($count); $i++) { |
||
110 | list($line, $exegutorLine) = $this->getLineInfo($file, $data['line'], $i); |
||
111 | if (!$line && !$exegutorLine) { |
||
112 | continue; |
||
113 | } |
||
114 | $data['exegutor'][] = $exegutorLine; |
||
115 | $data['file_lines'][$data['line'] + $i] = $line; |
||
116 | } |
||
117 | |||
118 | // to make Symfony exception more readable |
||
119 | if ($data['class'] == 'Symfony\Component\Debug\Exception\FatalErrorException') { |
||
120 | preg_match("~^(.+)' in ~", $data['exception'], $matches); |
||
121 | if (isset($matches[1])) { |
||
122 | $data['exception'] = $matches[1]; |
||
123 | } |
||
124 | } |
||
125 | |||
126 | return $data; |
||
127 | } // end getExceptionData |
||
128 | |||
129 | /** |
||
130 | * Set the value of specified fields to ***** |
||
131 | * |
||
132 | * @param string $value |
||
133 | * @param string $key |
||
134 | * @return void |
||
135 | */ |
||
136 | public function censorSensitiveFields(&$value, $key) |
||
137 | { |
||
138 | if (in_array($key, $this->config['censored_fields'], true)) { |
||
139 | $value = '*****'; |
||
140 | } |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * @param SplFileObject $file |
||
145 | */ |
||
146 | private function getLineInfo($file, $line, $i) |
||
147 | { |
||
148 | $currentLine = $line + $i; |
||
149 | // cuz array starts with 0, when file lines start count from 1 |
||
150 | $index = $currentLine - 1; |
||
151 | if ($index < 0) { |
||
152 | return [false, false]; |
||
153 | } |
||
154 | $file->seek($index); |
||
155 | |||
156 | if ($file->eof()) { |
||
157 | return [false, false]; |
||
158 | } |
||
159 | |||
160 | return [ |
||
161 | $file->current(), |
||
162 | [ |
||
163 | 'line' => '<span style="color:#aaaaaa;">' . $currentLine . '.</span> ' . SyntaxHighlight::process($file->current()), |
||
164 | 'wrap_left' => $i ? '' : '<span style="color: #F5F5F5; background-color: #5A3E3E; width: 100%; display: block;">', |
||
165 | 'wrap_right' => $i ? '' : '</span>', |
||
166 | ] |
||
167 | ]; |
||
168 | } // end getLineInfo |
||
169 | } |
||
170 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.