GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — integration ( a3ab80...7a98f7 )
by Brendan
06:22
created

GenericExceptionHandler   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 316
Duplicated Lines 7.91 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 6
Bugs 2 Features 0
Metric Value
c 6
b 2
f 0
dl 25
loc 316
rs 8.295
wmc 42
lcom 1
cbo 8

7 Methods

Rating   Name   Duplication   Size   Complexity  
A initialise() 0 9 2
D handler() 0 70 14
C render() 13 53 13
A __nearbyLines() 0 8 2
B renderHtml() 0 27 1
A getTemplate() 12 12 3
C shutdown() 0 40 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like GenericExceptionHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GenericExceptionHandler, and based on these observations, apply Extract Interface, too.

1
<?php
2
    /**
3
     * @package core
4
     */
5
6
    /**
7
     * GenericExceptionHandler will handle any uncaught exceptions thrown in
8
     * Symphony. Additionally, all errors in Symphony that are raised to Exceptions
9
     * will be handled by this class.
10
     * It is possible for Exceptions to be caught by their own `ExceptionHandler` which can
11
     * override the `render` function so that it can be displayed to the user appropriately.
12
     */
13
    class GenericExceptionHandler
14
    {
15
        /**
16
         * Whether the `GenericExceptionHandler` should handle exceptions. Defaults to true
17
         *
18
         * @var boolean
19
         */
20
        public static $enabled = true;
21
22
        /**
23
         * An instance of the Symphony Log class, used to write errors to the log
24
         *
25
         * @var Log
26
         */
27
        private static $_Log = null;
28
29
        /**
30
         * Initialise will set the error handler to be the `__CLASS__::handler` function.
31
         *
32
         * @param Log $Log
33
         *  An instance of a Symphony Log object to write errors to
34
         */
35
        public static function initialise(Log $Log = null)
36
        {
37
            if (!is_null($Log)) {
38
                self::$_Log = $Log;
39
            }
40
41
            set_exception_handler(array(__CLASS__, 'handler'));
42
            register_shutdown_function(array(__CLASS__, 'shutdown'));
43
        }
44
45
        /**
46
         * The handler function is given an Exception and will call it's render
47
         * function to display the Exception to a user. After calling the render
48
         * function, the output is displayed and then exited to prevent any further
49
         * logic from occurring.
50
         *
51
         * @param Exception $e
52
         *  The Exception object
53
         * @return string
54
         *  The result of the Exception's render function
55
         */
56
        public static function handler(Exception $e)
0 ignored issues
show
Unused Code introduced by
The parameter $e is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
57
        {
58
            $output = '';
59
60
            try {
61
                // Instead of just throwing an empty page, return a 404 page.
62
                if (self::$enabled !== true) {
63
                    $e = new FrontendPageNotFoundException();
64
                };
65
66
                $exception_type = get_class($e);
67
68
                if (class_exists("{$exception_type}Handler") && method_exists("{$exception_type}Handler", 'render')) {
69
                    $class = "{$exception_type}Handler";
70
                } else {
71
                    $class = __CLASS__;
72
                }
73
74
                // Exceptions should be logged if they are not caught.
75
                if (self::$_Log instanceof Log) {
76
                    if (method_exists($e, 'getAdditional') && null !== $e->getAdditional()->error) {
77
                        $exception = $e->getAdditional()->error;
78
                    } else {
79
                        $exception = $e;
80
                    }
81
82
                    self::$_Log->pushExceptionToLog($exception);
83
                }
84
85
                $output = call_user_func(array($class, 'render'), $e);
86
87
                // If an exception was raised trying to render the exception, fall back
88
                // to the generic exception handler
89
            } catch (Exception $e) {
90
                try {
91
                    $output = call_user_func(array('GenericExceptionHandler', 'render'), $e);
92
93
                    // If the generic exception handler couldn't do it, well we're in bad
94
                    // shape, just output a plaintext response!
95
                } catch (Exception $e) {
96
                    echo "<pre>";
97
                    echo 'A severe error occurred whilst trying to handle an exception, check the Symphony log for more details' . PHP_EOL;
98
                    echo $e->getMessage() . ' on ' . $e->getLine() . ' of file ' . $e->getFile() . PHP_EOL;
99
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method handler() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
100
                }
101
            }
102
103
            // Pending nothing disasterous, we should have `$e` and `$output` values here.
104
            if (!headers_sent()) {
105
                cleanup_session_cookies(APP_MODE);
106
107
                // Inspect the exception to determine the best status code
108
                $httpStatus = null;
109
                if ($e instanceof SymphonyErrorPage) {
110
                    $httpStatus = $e->getHttpStatusCode();
111
                } elseif ($e instanceof FrontendPageNotFoundException) {
112
                    $httpStatus = Page::HTTP_STATUS_NOT_FOUND;
113
                }
114
115
                if (!$httpStatus || $httpStatus === Page::HTTP_STATUS_OK) {
116
                    $httpStatus = Page::HTTP_STATUS_ERROR;
117
                }
118
119
                Page::renderStatusCode($httpStatus);
120
                header('Content-Type: text/html; charset=utf-8');
121
            }
122
123
            echo $output;
124
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method handler() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
125
        }
126
127
        /**
128
         * The render function will take an Exception and output a HTML page
129
         *
130
         * @param Exception $e
131
         *  The Exception object
132
         * @return string
133
         *  An HTML string
134
         */
135
        public static function render(Exception $e)
136
        {
137
            $lines = null;
138
139
            foreach (self::__nearbyLines($e->getLine(), $e->getFile()) as $line => $string) {
140
                $lines .= sprintf(
141
                    '<li%s><strong>%d</strong> <code>%s</code></li>',
142
                    (($line + 1) === $e->getLine() ? ' class="error"' : null),
143
                    ++$line,
144
                    str_replace("\t", '&nbsp;&nbsp;&nbsp;&nbsp;', htmlspecialchars($string))
145
                );
146
            }
147
148
            $trace = null;
149
150
            foreach ($e->getTrace() as $t) {
151
                $trace .= sprintf(
152
                    '<li><code><em>[%s:%d]</em></code></li><li><code>&#160;&#160;&#160;&#160;%s%s%s();</code></li>',
153
                    (isset($t['file']) ? $t['file'] : null),
154
                    (isset($t['line']) ? $t['line'] : null),
155
                    (isset($t['class']) ? $t['class'] : null),
156
                    (isset($t['type']) ? $t['type'] : null),
157
                    $t['function']
158
                );
159
            }
160
161
            $queries = null;
162
163 View Code Duplication
            if (is_object(Symphony::Database())) {
164
                $debug = Symphony::Database()->debug();
165
166
                if (!empty($debug)) {
167
                    foreach ($debug as $query) {
168
                        $queries .= sprintf(
169
                            '<li><em>[%01.4f]</em><code> %s;</code> </li>',
170
                            (isset($query['execution_time']) ? $query['execution_time'] : null),
171
                            htmlspecialchars($query['query'])
172
                        );
173
                    }
174
                }
175
            }
176
177
            return self::renderHtml(
178
                'fatalerror.generic',
179
                ($e instanceof ErrorException ? GenericErrorHandler::$errorTypeStrings[$e->getSeverity()] : 'Fatal Error'),
180
                $e->getMessage(),
181
                $e->getFile(),
182
                $e->getLine(),
183
                $lines,
184
                $trace,
185
                $queries
186
            );
187
        }
188
189
        /**
190
         * Retrieves a window of lines before and after the line where the error
191
         * occurred so that a developer can help debug the exception
192
         *
193
         * @param integer $line
194
         *  The line where the error occurred.
195
         * @param string $file
196
         *  The file that holds the logic that caused the error.
197
         * @param integer $window
198
         *  The number of lines either side of the line where the error occurred
199
         *  to show
200
         * @return array
201
         */
202
        protected static function __nearbyLines($line, $file, $window = 5)
203
        {
204
            if (!file_exists($file)) {
205
                return array();
206
            }
207
208
            return array_slice(file($file), ($line - 1) - $window, $window * 2, true);
209
        }
210
211
        /**
212
         * This function will fetch the desired `$template`, and output the
213
         * Exception in a user friendly way.
214
         *
215
         * @since Symphony 2.4
216
         * @param string $template
217
         *  The template name, which should correspond to something in the TEMPLATE
218
         *  directory, eg `fatalerror.fatal`.
219
         * @param string $heading
220
         * @param string $message
221
         * @param string $file
222
         * @param string $line
223
         * @param string $lines
224
         * @param string $trace
225
         * @param string $queries
226
         * @return string
227
         *  The HTML of the formatted error message.
228
         */
229
        public static function renderHtml(
230
            $template,
231
            $heading,
232
            $message,
233
            $file = null,
234
            $line = null,
235
            $lines = null,
236
            $trace = null,
237
            $queries = null
238
        ) {
239
            $html = sprintf(
240
                file_get_contents(self::getTemplate($template)),
241
                $heading,
242
                General::unwrapCDATA($message),
243
                $file,
244
                $line,
245
                $lines,
246
                $trace,
247
                $queries
248
            );
249
250
            $html = str_replace('{ASSETS_URL}', ASSETS_URL, $html);
251
            $html = str_replace('{SYMPHONY_URL}', SYMPHONY_URL, $html);
252
            $html = str_replace('{URL}', URL, $html);
253
254
            return $html;
255
        }
256
257
        /**
258
         * Returns the path to the error-template by looking at the `WORKSPACE/template/`
259
         * directory, then at the `TEMPLATES`  directory for the convention `*.tpl`. If
260
         * the template is not found, `false` is returned
261
         *
262
         * @since Symphony 2.3
263
         * @param string $name
264
         *  Name of the template
265
         * @return mixed
266
         *  String, which is the path to the template if the template is found,
267
         *  false otherwise
268
         */
269 View Code Duplication
        public static function getTemplate($name)
270
        {
271
            $format = '%s/%s.tpl';
272
273
            if (file_exists($template = sprintf($format, WORKSPACE . '/template', $name))) {
274
                return $template;
275
            } elseif (file_exists($template = sprintf($format, TEMPLATE, $name))) {
276
                return $template;
277
            } else {
278
                return false;
279
            }
280
        }
281
282
        /**
283
         * The shutdown function will capture any fatal errors and format them as a
284
         * usual Symphony page.
285
         *
286
         * @since Symphony 2.4
287
         */
288
        public static function shutdown()
289
        {
290
            $last_error = error_get_last();
291
292
            if (!is_null($last_error) && $last_error['type'] === E_ERROR) {
293
                $code = $last_error['type'];
294
                $message = $last_error['message'];
295
                $file = $last_error['file'];
296
                $line = $last_error['line'];
297
298
                try {
299
                    // Log the error message
300
                    if (self::$_Log instanceof Log) {
301
                        self::$_Log->pushToLog(sprintf(
302
                            '%s %s: %s%s%s',
303
                            __CLASS__,
304
                            $code,
305
                            $message,
306
                            ($line ? " on line $line" : null),
307
                            ($file ? " of file $file" : null)
308
                        ), $code);
309
                    }
310
311
                    ob_clean();
312
313
                    // Display the error message
314
                    echo self::renderHtml(
315
                        'fatalerror.fatal',
316
                        'Fatal Error',
317
                        $message,
318
                        $file,
319
                        $line
320
                    );
321
                } catch (Exception $e) {
322
                    echo "<pre>";
323
                    echo 'A severe error occurred whilst trying to handle an exception, check the Symphony log for more details' . PHP_EOL;
324
                    echo $e->getMessage() . ' on ' . $e->getLine() . ' of file ' . $e->getFile() . PHP_EOL;
325
                }
326
            }
327
        }
328
    }
329
330
    /**
331
     * `GenericErrorHandler` will catch any warnings or notices thrown by PHP and
332
     * raise the errors to Exceptions so they can be dealt with by the
333
     * `GenericExceptionHandler`. The type of errors that are raised to Exceptions
334
     * depends on the `error_reporting` level. All errors raised, except
335
     * `E_NOTICE` and `E_STRICT` are written to the Symphony log.
336
     */
337
    class GenericErrorHandler
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
338
    {
339
        /**
340
         * Whether the error handler is enabled or not, defaults to true.
341
         * Setting to false will prevent any Symphony error handling from occurring
342
         *
343
         * @var boolean
344
         */
345
        public static $enabled = true;
346
        /**
347
         * Whether to log errors or not.
348
         * This one is to be used temporarily, e.g., when PHP function is
349
         * supposed to error out and throw warning and log should be kept clean.
350
         *
351
         * @since Symphony 2.2.2
352
         * @var boolean
353
         * @example
354
         *  GenericErrorHandler::$logDisabled = true;
355
         *  DoSomethingThatEndsWithWarningsYouDoNotWantInLogs();
356
         *  GenericErrorHandler::$logDisabled = false;
357
         */
358
        public static $logDisabled = false;
359
        /**
360
         * An associative array with the PHP error constant as a key, and
361
         * a string describing that constant as the value
362
         *
363
         * @var array
364
         */
365
        public static $errorTypeStrings = array(
366
            E_ERROR => 'Fatal Error',
367
            E_WARNING => 'Warning',
368
            E_PARSE => 'Parsing Error',
369
            E_NOTICE => 'Notice',
370
371
            E_CORE_ERROR => 'Core Error',
372
            E_CORE_WARNING => 'Core Warning',
373
            E_COMPILE_ERROR => 'Compile Error',
374
            E_COMPILE_WARNING => 'Compile Warning',
375
376
            E_USER_NOTICE => 'User Notice',
377
            E_USER_WARNING => 'User Warning',
378
            E_USER_ERROR => 'User Error',
379
380
            E_STRICT => 'Strict Notice',
381
            E_RECOVERABLE_ERROR => 'Recoverable Error',
382
            E_DEPRECATED => 'Deprecated Warning'
383
        );
384
        /**
385
         * An instance of the Log class, used to write errors to the log
386
         *
387
         * @var Log
388
         */
389
        private static $_Log = null;
390
391
        /**
392
         * Initialise will set the error handler to be the `__CLASS__::handler`
393
         * function.
394
         *
395
         * @param Log|null $Log (optional)
396
         *  An instance of a Symphony Log object to write errors to
397
         */
398
        public static function initialise(Log $Log = null)
399
        {
400
            if (!is_null($Log)) {
401
                self::$_Log = $Log;
402
            }
403
404
            set_error_handler(array(__CLASS__, 'handler'), error_reporting());
405
        }
406
407
        /**
408
         * The handler function will write the error to the `$Log` if it is not `E_NOTICE`
409
         * or `E_STRICT` before raising the error as an Exception. This allows all `E_WARNING`
410
         * to actually be captured by an Exception handler.
411
         *
412
         * @param integer $code
413
         *  The error code, one of the PHP error constants
414
         * @param string $message
415
         *  The message of the error, this will be written to the log and
416
         *  displayed as the exception message
417
         * @param string $file
418
         *  The file that holds the logic that caused the error. Defaults to null
419
         * @param integer $line
420
         *  The line where the error occurred.
421
         * @throws ErrorException
422
         * @return string
423
         *  Usually a string of HTML that will displayed to a user
424
         */
425
        public static function handler($code, $message, $file = null, $line = null)
426
        {
427
            // Only log if the error won't be raised to an exception and the error is not `E_NOTICE` or `E_STRICT`
428
            if (!self::$logDisabled && !in_array($code, array(E_NOTICE, E_STRICT)) && self::$_Log instanceof Log) {
429
                self::$_Log->pushToLog(sprintf(
430
                    '%s %s: %s%s%s',
431
                    __CLASS__,
432
                    $code,
433
                    $message,
434
                    ($line ? " on line $line" : null),
435
                    ($file ? " of file $file" : null)
436
                ), $code);
437
            }
438
439
            if (self::isEnabled()) {
440
                throw new ErrorException($message, 0, $code, $file, $line);
441
            }
442
        }
443
444
        /**
445
         * Determines if the error handler is enabled by checking that error_reporting
446
         * is set in the php config and that $enabled is true
447
         *
448
         * @return boolean
449
         */
450
        public static function isEnabled()
451
        {
452
            return (bool)error_reporting() && self::$enabled;
453
        }
454
    }
455