PluginProbe
DecaLog / 4.4.0
DecaLog v4.4.0
3.0.2 3.1.0 3.10.0 3.2.0 3.3.0 3.4.0 3.4.1 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.6.3 3.7.0 3.7.1 3.8.0 3.9.0 3.9.1 4.0.0 4.1.0 4.2.0 4.3.0 4.3.1 4.4.0 4.5.0 All 75 releases
decalog / assets / fatal-error-handler.php

fatal-error-handler.php in DecaLog 4.4.0, at assets/fatal-error-handler.php

279 lines 7.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: DecaLog Error Handler
4 * Plugin URI: https://perfops.one/decalog
5 * Description: Capture and log events, metrics and traces on your site. Make WordPress observable – finally!
6 * Version: 4.x
7 * Author: Pierre Lannoy / PerfOps One
8 * Author URI: https://perfops.one
9 * License: GPLv3
10 * License URI: https://www.gnu.org/licenses/gpl-3.0.html
11 */
12
13
14 if ( ! defined( 'DECALOG_BOOTSTRAPPED' ) ) {
15 define( 'DECALOG_BOOTSTRAPPED', true );
16 }
17
18 $dclg_btsrp = [];
19
20
21 /**
22 * Bootstrap handler for DecaLog.
23 *
24 * Defines methods and properties for bootstrap handling.
25 *
26 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
27 * @since 2.4.0
28 */
29 class Decalog_Error_Handler extends \WP_Fatal_Error_Handler {
30
31 /**
32 * The previous error handler, to restore if needed.
33 *
34 * @since 2.4.0
35 * @var callable $previous_error_handler The previous error handler.
36 */
37 private $previous_error_handler;
38
39 /**
40 * The error level mapping.
41 *
42 * @since 2.4.0
43 * @var array $error_level_map List of mappings.
44 */
45 private $error_level_map = [
46 E_ERROR => 600,
47 E_PARSE => 600,
48 E_CORE_ERROR => 600,
49 E_COMPILE_ERROR => 600,
50 E_USER_ERROR => 400,
51 E_RECOVERABLE_ERROR => 400,
52 E_CORE_WARNING => 300,
53 E_WARNING => 300,
54 E_COMPILE_WARNING => 300,
55 E_USER_WARNING => 300,
56 E_NOTICE => 250,
57 E_USER_NOTICE => 250,
58 E_STRICT => 250,
59 E_DEPRECATED => 200,
60 E_USER_DEPRECATED => 200,
61 ];
62
63 /**
64 * The error string mapping.
65 *
66 * @since 2.4.0
67 * @var array $error_string_map List of mappings.
68 */
69 private $error_string_map = [
70 E_ERROR => 'E_ERROR',
71 E_PARSE => 'E_PARSE',
72 E_CORE_ERROR => 'E_CORE_ERROR',
73 E_COMPILE_ERROR => 'E_COMPILE_ERROR',
74 E_USER_ERROR => 'E_USER_ERROR',
75 E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
76 E_CORE_WARNING => 'E_CORE_WARNING',
77 E_WARNING => 'E_WARNING',
78 E_COMPILE_WARNING => 'E_COMPILE_WARNING',
79 E_USER_WARNING => 'E_USER_WARNING',
80 E_NOTICE => 'E_NOTICE',
81 E_USER_NOTICE => 'E_USER_NOTICE',
82 E_STRICT => 'E_STRICT',
83 E_DEPRECATED => 'E_DEPRECATED',
84 E_USER_DEPRECATED => 'E_USER_DEPRECATED',
85 ];
86
87 /**
88 * Initialize the class and set its properties.
89 *
90 * @since 2.4.0
91 */
92 public function __construct() {
93 if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) {
94 // phpcs:ignore
95 $this->previous_error_handler = \set_error_handler( [ $this, 'handle_error' ] );
96 }
97 if ( ! defined( 'DECALOG_TRACEID' ) ) {
98 define( 'DECALOG_TRACEID', $this->generate_unique_id() );
99 }
100 if ( ! defined( 'POWP_START_TIMESTAMP' ) ) {
101 define( 'POWP_START_TIMESTAMP', microtime( true ) );
102 }
103 if ( ! defined( 'POWS_START_TIMESTAMP' ) ) {
104 if ( array_key_exists( 'REQUEST_TIME_FLOAT', $_SERVER ) ) {
105 define( 'POWS_START_TIMESTAMP', (float) filter_var( $_SERVER['REQUEST_TIME_FLOAT'], FILTER_VALIDATE_FLOAT ) );
106 } else {
107 define( 'POWS_START_TIMESTAMP', POWP_START_TIMESTAMP );
108 }
109 }
110 if ( ! defined( 'SAVEQUERIES' ) ) {
111 define( 'SAVEQUERIES', true );
112 }
113 if ( ! defined( 'DECALOG_MAX_SHUTDOWN_PRIORITY' ) ) {
114 define( 'DECALOG_MAX_SHUTDOWN_PRIORITY', PHP_INT_MAX - 1000 );
115 }
116 }
117
118 /**
119 * Handles errors.
120 *
121 * @param integer $code The error code.
122 * @param string $message The error message.
123 * @param string $file The file where the error was raised.
124 * @param integer $line The line where the error was raised.
125 * @param array $context The context of the error.
126 * @return mixed|false The result of the previous handler if any, or false.
127 * @since 2.4.0
128 */
129 public function handle_error( $code, $message, $file = '', $line = 0, $context = [] ) {
130 $level = $this->error_level_map[ $code ] ?? 500;
131 $file = $this->normalized_file_line( $file, $line );
132 $message = \sprintf( 'Error (%s): "%s" at `%s`.', $this->error_string_map[ $code ] ?? 'Unknown PHP error', $message, $file );
133 $this->prelog( $level, $message, (int) $code );
134 if ( $this->previous_error_handler && \is_callable( $this->previous_error_handler ) ) {
135 return \call_user_func( $this->previous_error_handler, $code, $message, $file, $line, $context );
136 }
137 return false;
138 }
139
140 /**
141 * In-memory logging.
142 *
143 * @param integer $level The log level.
144 * @param string $message The log message.
145 * @param integer $code Optional. The log code.
146 * @since 2.4.0
147 */
148 private function prelog( $level, $message, int $code = 0 ) {
149 global $dclg_btsrp;
150 if ( ! is_array( $dclg_btsrp ) ) {
151 $dclg_btsrp = [];
152 }
153 $dclg_btsrp[] = [
154 'level' => $level,
155 'message' => $message,
156 'code' => $code,
157 ];
158 }
159
160 /**
161 * Normalizes a path+file.
162 *
163 * @param string $file The raw file name.
164 * @return string The normalized file name.
165 * @since 2.4.0
166 */
167 private function normalized_file( $file ) {
168 if ( 'unknown' === $file || '' === $file ) {
169 return 'PHP kernel';
170 }
171 if ( false !== \strpos( $file, 'phar://' ) ) {
172 return \str_replace( 'phar://', '', $this->normalized_path( $file ) );
173 }
174 return './' . \str_replace( $this->normalized_path( ABSPATH ), '', $this->normalized_path( $file ) );
175 }
176
177 /**
178 * Normalizes a path+file.
179 *
180 * @param string $file The raw file name.
181 * @param string $line Optional. The file line.
182 * @return string The normalized file & line.
183 * @since 2.4.0
184 */
185 private function normalized_file_line( $file, $line = '' ) {
186 if ( '' === (string) $line || '0' === (string) $line ) {
187 return $this->normalized_file( $file );
188 }
189 return $this->normalized_file( $file ) . ':' . (string) $line;
190 }
191
192 /**
193 * Test if a given path is a stream URL
194 *
195 * @param string $path The resource path or URL.
196 * @return bool True if the path is a stream URL.
197 * @since 2.4.0
198 */
199 private function is_stream( $path ) {
200 $scheme_separator = \strpos( $path, '://' );
201 if ( false === $scheme_separator ) {
202 return false;
203 }
204 $stream = \substr( $path, 0, $scheme_separator );
205 return \in_array( $stream, \stream_get_wrappers(), true );
206 }
207
208 /**
209 * Normalize a filesystem path.
210 *
211 * On windows systems, replaces backslashes with forward slashes
212 * and forces upper-case drive letters.
213 * Allows for two leading slashes for Windows network shares, but
214 * ensures that all other duplicate slashes are reduced to a single.
215 *
216 * @param string $path Path to normalize.
217 * @return string Normalized path.
218 * @since 2.4.0
219 */
220 private function normalized_path( $path ) {
221 $wrapper = '';
222
223 if ( $this->is_stream( $path ) ) {
224 [ $wrapper, $path ] = \explode( '://', $path, 2 );
225 $wrapper .= '://';
226 }
227 $path = \str_replace( '\\', '/', $path );
228 $path = \preg_replace( '|(?<=.)/+|', '/', $path );
229 if ( ':' === \substr( $path, 1, 1 ) ) {
230 $path = \ucfirst( $path );
231 }
232 return $wrapper . $path;
233 }
234
235 /**
236 * Generates a v4 UUID.
237 *
238 * @since 1.0.0
239 * @return string A v4 UUID.
240 */
241 private function generate_v4() {
242 return sprintf(
243 '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
244 // phpcs:disable
245 mt_rand( 0, 0xffff ),
246 mt_rand( 0, 0xffff ),
247 mt_rand( 0, 0xffff ),
248 mt_rand( 0, 0x0fff ) | 0x4000,
249 mt_rand( 0, 0x3fff ) | 0x8000,
250 mt_rand( 0, 0xffff ),
251 mt_rand( 0, 0xffff ),
252 mt_rand( 0, 0xffff )
253 // phpcs:enabled
254 );
255 }
256
257 /**
258 * Generates a (pseudo) unique ID.
259 * This function does not generate cryptographically secure values, and should not be used for cryptographic purposes.
260 *
261 * @param integer $length Optional. The length of the ID.
262 * @return string The unique ID.
263 * @since 1.0.0
264 */
265 private function generate_unique_id( $length = 32 ) {
266 $result = '';
267 $date = new \DateTime();
268 do {
269 $s = $this->generate_v4();
270 $s = str_replace( '-', (string) ( $date->format( 'u' ) ), $s );
271 $result .= $s;
272 $l = strlen( $result );
273 } while ( $l < $length );
274 return substr( str_shuffle( $result ), 0, $length );
275 }
276 }
277
278 return new Decalog_Error_Handler();
279