19
19
use Config \Paths ;
20
20
use ErrorException ;
21
21
use Throwable ;
22
- use function error_reporting ;
23
22
24
23
/**
25
24
* Exceptions manager
@@ -64,35 +63,25 @@ class Exceptions
64
63
*/
65
64
protected $ response ;
66
65
67
- /**
68
- * Constructor.
69
- */
70
66
public function __construct (ExceptionsConfig $ config , IncomingRequest $ request , Response $ response )
71
67
{
72
68
$ this ->ob_level = ob_get_level ();
73
-
74
69
$ this ->viewPath = rtrim ($ config ->errorViewPath , '\\/ ' ) . DIRECTORY_SEPARATOR ;
75
-
76
- $ this ->config = $ config ;
77
-
70
+ $ this ->config = $ config ;
78
71
$ this ->request = $ request ;
79
72
$ this ->response = $ response ;
80
73
}
81
74
82
75
/**
83
76
* Responsible for registering the error, exception and shutdown
84
77
* handling of our application.
78
+ *
79
+ * @codeCoverageIgnore
85
80
*/
86
81
public function initialize ()
87
82
{
88
- // Set the Exception Handler
89
83
set_exception_handler ([$ this , 'exceptionHandler ' ]);
90
-
91
- // Set the Error Handler
92
84
set_error_handler ([$ this , 'errorHandler ' ]);
93
-
94
- // Set the handler for shutdown to catch Parse errors
95
- // Do we need this in PHP7?
96
85
register_shutdown_function ([$ this , 'shutdownHandler ' ]);
97
86
}
98
87
@@ -105,12 +94,8 @@ public function initialize()
105
94
*/
106
95
public function exceptionHandler (Throwable $ exception )
107
96
{
108
- [
109
- $ statusCode ,
110
- $ exitCode ,
111
- ] = $ this ->determineCodes ($ exception );
97
+ [$ statusCode , $ exitCode ] = $ this ->determineCodes ($ exception );
112
98
113
- // Log it
114
99
if ($ this ->config ->log === true && ! in_array ($ statusCode , $ this ->config ->ignoreCodes , true )) {
115
100
log_message ('critical ' , $ exception ->getMessage () . "\n{trace} " , [
116
101
'trace ' => $ exception ->getTraceAsString (),
@@ -119,8 +104,7 @@ public function exceptionHandler(Throwable $exception)
119
104
120
105
if (! is_cli ()) {
121
106
$ this ->response ->setStatusCode ($ statusCode );
122
- $ header = "HTTP/ {$ this ->request ->getProtocolVersion ()} {$ this ->response ->getStatusCode ()} {$ this ->response ->getReason ()}" ;
123
- header ($ header , true , $ statusCode );
107
+ header (sprintf ('HTTP/%s %s %s ' , $ this ->request ->getProtocolVersion (), $ this ->response ->getStatusCode (), $ this ->response ->getReasonPhrase ()), true , $ statusCode );
124
108
125
109
if (strpos ($ this ->request ->getHeaderLine ('accept ' ), 'text/html ' ) === false ) {
126
110
$ this ->respond (ENVIRONMENT === 'development ' ? $ this ->collectVars ($ exception , $ statusCode ) : '' , $ statusCode )->send ();
@@ -142,31 +126,36 @@ public function exceptionHandler(Throwable $exception)
142
126
* This seems to be primarily when a user triggers it with trigger_error().
143
127
*
144
128
* @throws ErrorException
129
+ *
130
+ * @codeCoverageIgnore
145
131
*/
146
132
public function errorHandler (int $ severity , string $ message , ?string $ file = null , ?int $ line = null )
147
133
{
148
134
if (! (error_reporting () & $ severity )) {
149
135
return ;
150
136
}
151
137
152
- // Convert it to an exception and pass it along.
153
138
throw new ErrorException ($ message , 0 , $ severity , $ file , $ line );
154
139
}
155
140
156
141
/**
157
142
* Checks to see if any errors have happened during shutdown that
158
143
* need to be caught and handle them.
144
+ *
145
+ * @codeCoverageIgnore
159
146
*/
160
147
public function shutdownHandler ()
161
148
{
162
149
$ error = error_get_last ();
163
150
164
- // If we've got an error that hasn't been displayed, then convert
165
- // it to an Exception and use the Exception handler to display it
166
- // to the user.
167
- // Fatal Error?
168
- if ($ error !== null && in_array ($ error ['type ' ], [E_ERROR , E_CORE_ERROR , E_COMPILE_ERROR , E_PARSE ], true )) {
169
- $ this ->exceptionHandler (new ErrorException ($ error ['message ' ], $ error ['type ' ], 0 , $ error ['file ' ], $ error ['line ' ]));
151
+ if ($ error === null ) {
152
+ return ;
153
+ }
154
+
155
+ ['type ' => $ type , 'message ' => $ message , 'file ' => $ file , 'line ' => $ line ] = $ error ;
156
+
157
+ if (in_array ($ type , [E_ERROR , E_CORE_ERROR , E_COMPILE_ERROR , E_PARSE ], true )) {
158
+ $ this ->exceptionHandler (new ErrorException ($ message , $ type , 0 , $ file , $ line ));
170
159
}
171
160
}
172
161
@@ -222,20 +211,25 @@ protected function render(Throwable $exception, int $statusCode)
222
211
$ viewFile = $ altPath . $ altView ;
223
212
}
224
213
225
- // Prepare the vars
226
- $ vars = $ this ->collectVars ($ exception , $ statusCode );
227
- extract ($ vars );
214
+ if (! isset ($ viewFile )) {
215
+ echo 'The error view files were not found. Cannot render exception trace. ' ;
216
+
217
+ exit (1 );
218
+ }
228
219
229
- // Render it
230
220
if (ob_get_level () > $ this ->ob_level + 1 ) {
231
221
ob_end_clean ();
232
222
}
233
223
234
- ob_start ();
235
- include $ viewFile ; // @phpstan-ignore-line
236
- $ buffer = ob_get_contents ();
237
- ob_end_clean ();
238
- echo $ buffer ;
224
+ echo (function () use ($ exception , $ statusCode , $ viewFile ): string {
225
+ $ vars = $ this ->collectVars ($ exception , $ statusCode );
226
+ extract ($ vars , EXTR_SKIP );
227
+
228
+ ob_start ();
229
+ include $ viewFile ;
230
+
231
+ return ob_get_clean ();
232
+ })();
239
233
}
240
234
241
235
/**
@@ -244,7 +238,8 @@ protected function render(Throwable $exception, int $statusCode)
244
238
protected function collectVars (Throwable $ exception , int $ statusCode ): array
245
239
{
246
240
$ trace = $ exception ->getTrace ();
247
- if (! empty ($ this ->config ->sensitiveDataInTrace )) {
241
+
242
+ if ($ this ->config ->sensitiveDataInTrace !== []) {
248
243
$ this ->maskSensitiveData ($ trace , $ this ->config ->sensitiveDataInTrace );
249
244
}
250
245
@@ -279,11 +274,11 @@ protected function maskSensitiveData(&$trace, array $keysToMask, string $path =
279
274
}
280
275
}
281
276
282
- if (! is_iterable ( $ trace ) && is_object ($ trace )) {
277
+ if (is_object ($ trace )) {
283
278
$ trace = get_object_vars ($ trace );
284
279
}
285
280
286
- if (is_iterable ($ trace )) {
281
+ if (is_array ($ trace )) {
287
282
foreach ($ trace as $ pathKey => $ subarray ) {
288
283
$ this ->maskSensitiveData ($ subarray , $ keysToMask , $ path . '/ ' . $ pathKey );
289
284
}
@@ -298,28 +293,25 @@ protected function determineCodes(Throwable $exception): array
298
293
$ statusCode = abs ($ exception ->getCode ());
299
294
300
295
if ($ statusCode < 100 || $ statusCode > 599 ) {
301
- $ exitStatus = $ statusCode + EXIT__AUTO_MIN ; // 9 is EXIT__AUTO_MIN
302
- if ($ exitStatus > EXIT__AUTO_MAX ) { // 125 is EXIT__AUTO_MAX
303
- $ exitStatus = EXIT_ERROR ; // EXIT_ERROR
296
+ $ exitStatus = $ statusCode + EXIT__AUTO_MIN ;
297
+
298
+ if ($ exitStatus > EXIT__AUTO_MAX ) {
299
+ $ exitStatus = EXIT_ERROR ;
304
300
}
301
+
305
302
$ statusCode = 500 ;
306
303
} else {
307
- $ exitStatus = 1 ; // EXIT_ERROR
304
+ $ exitStatus = EXIT_ERROR ;
308
305
}
309
306
310
- return [
311
- $ statusCode ?: 500 ,
312
- $ exitStatus ,
313
- ];
307
+ return [$ statusCode , $ exitStatus ];
314
308
}
315
309
316
310
//--------------------------------------------------------------------
317
311
// Display Methods
318
312
//--------------------------------------------------------------------
319
313
320
314
/**
321
- * Clean Path
322
- *
323
315
* This makes nicer looking paths for the error output.
324
316
*/
325
317
public static function cleanPath (string $ file ): string
@@ -354,6 +346,7 @@ public static function describeMemory(int $bytes): string
354
346
if ($ bytes < 1024 ) {
355
347
return $ bytes . 'B ' ;
356
348
}
349
+
357
350
if ($ bytes < 1048576 ) {
358
351
return round ($ bytes / 1024 , 2 ) . 'KB ' ;
359
352
}
@@ -390,18 +383,16 @@ public static function highlightFile(string $file, int $lineNumber, int $lines =
390
383
$ source = str_replace (["\r\n" , "\r" ], "\n" , $ source );
391
384
$ source = explode ("\n" , highlight_string ($ source , true ));
392
385
$ source = str_replace ('<br /> ' , "\n" , $ source [1 ]);
393
-
394
386
$ source = explode ("\n" , str_replace ("\r\n" , "\n" , $ source ));
395
387
396
388
// Get just the part to show
397
- $ start = $ lineNumber - (int ) round ($ lines / 2 );
398
- $ start = $ start < 0 ? 0 : $ start ;
389
+ $ start = max ($ lineNumber - (int ) round ($ lines / 2 ), 0 );
399
390
400
391
// Get just the lines we need to display, while keeping line numbers...
401
392
$ source = array_splice ($ source , $ start , $ lines , true ); // @phpstan-ignore-line
402
393
403
394
// Used to format the line number in the source
404
- $ format = '% ' . strlen (sprintf ( ' %s ' , $ start + $ lines )) . 'd ' ;
395
+ $ format = '% ' . strlen (( string ) ( $ start + $ lines )) . 'd ' ;
405
396
406
397
$ out = '' ;
407
398
// Because the highlighting may have an uneven number
@@ -412,11 +403,11 @@ public static function highlightFile(string $file, int $lineNumber, int $lines =
412
403
413
404
foreach ($ source as $ n => $ row ) {
414
405
$ spans += substr_count ($ row , '<span ' ) - substr_count ($ row , '</span ' );
415
-
416
406
$ row = str_replace (["\r" , "\n" ], ['' , '' ], $ row );
417
407
418
408
if (($ n + $ start + 1 ) === $ lineNumber ) {
419
409
preg_match_all ('#<[^>]+># ' , $ row , $ tags );
410
+
420
411
$ out .= sprintf (
421
412
"<span class='line highlight'><span class='number'> {$ format }</span> %s \n</span>%s " ,
422
413
$ n + $ start + 1 ,
0 commit comments