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
Pull Request — 2.6.x (#2509)
by Nicolas
04:59
created

GenericExceptionHandler::getTemplate()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 12
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 20
rs 8.8571
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 display exceptions. Defaults to true.
17
     * @since Symphony 2.6.4
18
     *  When disabled, exception are now rendered usign the fatalerror.disabled template,
19
     *  to prevent leaking debug data.
20
     * @var boolean
21
     */
22
    public static $enabled = true;
23
24
    /**
25
     * An instance of the Symphony Log class, used to write errors to the log
26
     * @var Log
27
     */
28
    private static $_Log = null;
29
30
    /**
31
     * Whether to log errors or not.
32
     * This one is to be used temporarily, e.g., when PHP function is
33
     * supposed throw Exception and log should be kept clean.
34
     *
35
     * @since Symphony 2.6.4
36
     * @var boolean
37
     * @example
38
     *  GenericExceptionHandler::$logDisabled = true;
39
     *  DoSomethingThatEndsWithWarningsYouDoNotWantInLogs();
40
     *  GenericExceptionHandler::$logDisabled = false;
41
     */
42
    public static $logDisabled = false;
43
44
    /**
45
     * Initialise will set the error handler to be the `__CLASS__::handler` function.
46
     *
47
     * @param Log $Log
48
     *  An instance of a Symphony Log object to write errors to
49
     */
50
    public static function initialise(Log $Log = null)
51
    {
52
        if (!is_null($Log)) {
53
            self::$_Log = $Log;
54
        }
55
56
        set_exception_handler(array(__CLASS__, 'handler'));
57
        register_shutdown_function(array(__CLASS__, 'shutdown'));
58
    }
59
60
    /**
61
     * Retrieves a window of lines before and after the line where the error
62
     * occurred so that a developer can help debug the exception
63
     *
64
     * @param integer $line
65
     *  The line where the error occurred.
66
     * @param string $file
67
     *  The file that holds the logic that caused the error.
68
     * @param integer $window
69
     *  The number of lines either side of the line where the error occurred
70
     *  to show
71
     * @return array
72
     */
73
    protected static function __nearbyLines($line, $file, $window = 5)
74
    {
75
        if (!file_exists($file)) {
76
            return array();
77
        }
78
79
        return array_slice(file($file), ($line - 1) - $window, $window * 2, true);
80
    }
81
82
    /**
83
     * The handler function is given an Exception and will call it's render
84
     * function to display the Exception to a user. After calling the render
85
     * function, the output is displayed and then exited to prevent any further
86
     * logic from occurring.
87
     *
88
     * @since Symphony 2.6.4
89
     *  The method is final
90
     *
91
     * @param Exception $e
92
     *  The Exception object
93
     * @return string
94
     *  The result of the Exception's render function
95
     */
96
    final 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...
97
    {
98
        $output = '';
99
100
        try {
101
            $exception_type = get_class($e);
102
103
            if (class_exists("{$exception_type}Handler") && method_exists("{$exception_type}Handler", 'render')) {
104
                $class = "{$exception_type}Handler";
105
            } else {
106
                $class = __CLASS__;
107
            }
108
109
            // Exceptions should be logged if they are not caught.
110
            if (!self::$logDisabled && self::$_Log instanceof Log) {
111
                self::$_Log->pushExceptionToLog($e, true);
112
            }
113
114
            $output = call_user_func(array($class, 'render'), $e);
115
116
        // If an exception was raised trying to render the exception, fall back
117
        // to the generic exception handler
118
        } catch (Exception $e) {
119
            try {
120
                $output = call_user_func(array('GenericExceptionHandler', 'render'), $e);
121
122
            // If the generic exception handler couldn't do it, well we're in bad
123
            // shape, just output a plaintext response!
124
            } catch (Exception $e) {
125
                echo "<pre>";
126
                echo 'A severe error occurred whilst trying to handle an exception, check the Symphony log for more details' . PHP_EOL;
127 View Code Duplication
                if (self::$enabled === true) {
128
                    echo $e->getMessage() . ' on ' . $e->getLine() . ' of file ' . $e->getFile() . PHP_EOL;
129
                }
130
                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...
131
            }
132
        }
133
134
        // Pending nothing disasterous, we should have `$e` and `$output` values here.
135
        self::sendHeaders($e);
136
137
        echo $output;
138
        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...
139
    }
140
141
    protected static function sendHeaders(Exception $e)
142
    {
143
        if (!headers_sent()) {
144
            cleanup_session_cookies();
145
146
            // Inspect the exception to determine the best status code
147
            $httpStatus = null;
148
            if ($e instanceof SymphonyErrorPage) {
149
                $httpStatus = $e->getHttpStatusCode();
150
                if (isset($e->getAdditional()->header)) {
151
                    header($e->getAdditional()->header);
152
                }
153
            } elseif ($e instanceof FrontendPageNotFoundException) {
154
                $httpStatus = Page::HTTP_STATUS_NOT_FOUND;
155
            }
156
157
            if (!$httpStatus || $httpStatus == Page::HTTP_STATUS_OK) {
158
                $httpStatus = Page::HTTP_STATUS_ERROR;
159
            }
160
161
            Page::renderStatusCode($httpStatus);
162
            header('Content-Type: text/html; charset=utf-8');
163
        }
164
    }
165
166
    /**
167
     * Returns the path to the error-template by looking at the `WORKSPACE/template/`
168
     * directory, then at the `TEMPLATES`  directory for the convention `*.tpl`. If
169
     * the template is not found, `false` is returned
170
     *
171
     * @since Symphony 2.3
172
     * @param string $name
173
     *  Name of the template
174
     * @return mixed
175
     *  String, which is the path to the template if the template is found,
176
     *  false otherwise
177
     */
178
    public static function getTemplate($name)
179
    {
180
        $format = '%s/%s.tpl';
181
182
        if (!self::$enabled) {
183
            if (!file_exists($template = sprintf($format, TEMPLATE, 'fatalerror.disabled'))) {
184
                return false;
185
            }
186
            return $template;
187
        }
188
        else if (file_exists($template = sprintf($format, WORKSPACE . '/template', $name))) {
189
            return $template;
190
        }
191
        else if (file_exists($template = sprintf($format, TEMPLATE, $name))) {
192
            return $template;
193
        }
194
        else {
195
            return false;
196
        }
197
    }
198
199
    /**
200
     * The render function will take an Exception and output a HTML page
201
     *
202
     * @param Exception $e
203
     *  The Exception object
204
     * @return string
205
     *  An HTML string
206
     */
207
    public static function render(Exception $e)
208
    {
209
        $lines = null;
210
211
        foreach (self::__nearByLines($e->getLine(), $e->getFile()) as $line => $string) {
212
            $lines .= sprintf(
213
                '<li%s><strong>%d</strong> <code>%s</code></li>',
214
                (($line+1) == $e->getLine() ? ' class="error"' : null),
215
                ++$line,
216
                str_replace("\t", '&nbsp;&nbsp;&nbsp;&nbsp;', htmlspecialchars($string))
217
            );
218
        }
219
220
        $trace = null;
221
222
        foreach ($e->getTrace() as $t) {
223
            $trace .= sprintf(
224
                '<li><code><em>[%s:%d]</em></code></li><li><code>&#160;&#160;&#160;&#160;%s%s%s();</code></li>',
225
                (isset($t['file']) ? $t['file'] : null),
226
                (isset($t['line']) ? $t['line'] : null),
227
                (isset($t['class']) ? $t['class'] : null),
228
                (isset($t['type']) ? $t['type'] : null),
229
                $t['function']
230
            );
231
        }
232
233
        $queries = null;
234
235 View Code Duplication
        if (is_object(Symphony::Database())) {
236
            $debug = Symphony::Database()->debug();
237
238
            if (!empty($debug)) {
239
                foreach ($debug as $query) {
240
                    $queries .= sprintf(
241
                        '<li><em>[%01.4f]</em><code> %s;</code> </li>',
242
                        (isset($query['execution_time']) ? $query['execution_time'] : null),
243
                        htmlspecialchars($query['query'])
244
                    );
245
                }
246
            }
247
        }
248
249
        return self::renderHtml(
250
            'fatalerror.generic',
251
            ($e instanceof ErrorException ? GenericErrorHandler::$errorTypeStrings[$e->getSeverity()] : 'Fatal Error'),
252
            $e->getMessage(),
253
            $e->getFile(),
254
            $e->getLine(),
255
            $lines,
256
            $trace,
257
            $queries
258
        );
259
    }
260
261
    /**
262
     * The shutdown function will capture any fatal errors and format them as a
263
     * usual Symphony page.
264
     *
265
     * @since Symphony 2.4
266
     */
267
    public static function shutdown()
268
    {
269
        $last_error = error_get_last();
270
271
        if (!is_null($last_error) && $last_error['type'] === E_ERROR) {
272
            $code = $last_error['type'];
273
            $message = $last_error['message'];
274
            $file = $last_error['file'];
275
            $line = $last_error['line'];
276
277
            try {
278
                // Log the error message
279
                if (!self::$logDisabled && self::$_Log instanceof Log) {
280
                    self::$_Log->pushToLog(sprintf(
281
                        '%s %s: %s%s%s',
282
                        __CLASS__,
283
                        $code,
284
                        $message,
285
                        ($line ? " on line $line" : null),
286
                        ($file ? " of file $file" : null)
287
                    ), $code, true);
288
                }
289
290
                ob_clean();
291
292
                // Display the error message
293
                echo self::renderHtml(
294
                    'fatalerror.fatal',
295
                    'Fatal Error',
296
                    $message,
297
                    $file,
298
                    $line
299
                );
300
            } catch (Exception $e) {
301
                echo "<pre>";
302
                echo 'A severe error occurred whilst trying to handle an exception, check the Symphony log for more details' . PHP_EOL;
303 View Code Duplication
                if (self::$enabled === true) {
304
                    echo $e->getMessage() . ' on ' . $e->getLine() . ' of file ' . $e->getFile() . PHP_EOL;
305
                }
306
            }
307
        }
308
    }
309
310
    /**
311
     * This function will fetch the desired `$template`, and output the
312
     * Exception in a user friendly way.
313
     *
314
     * @since Symphony 2.4
315
     * @since Symphony 2.6.4 the method is protected
316
     * @param string $template
317
     *  The template name, which should correspond to something in the TEMPLATE
318
     *  directory, eg `fatalerror.fatal`.
319
     * @param string $heading
320
     * @param string $message
321
     * @param string $file
322
     * @param string $line
323
     * @param string $lines
324
     * @param string $trace
325
     * @param string $queries
326
     * @return string
327
     *  The HTML of the formatted error message.
328
     */
329
    protected static function renderHtml($template, $heading, $message, $file = null, $line = null, $lines = null, $trace = null, $queries = null)
330
    {
331
        $html = sprintf(
332
            file_get_contents(self::getTemplate($template)),
333
            $heading,
334
            !self::$enabled ? 'Something unexpected occurred.' : General::unwrapCDATA($message),
335
            !self::$enabled ? '' : $file,
336
            !self::$enabled ? '' : $line,
337
            !self::$enabled ? null : $lines,
338
            !self::$enabled ? null : $trace,
339
            !self::$enabled ? null : $queries
340
        );
341
342
        $html = str_replace('{ASSETS_URL}', ASSETS_URL, $html);
343
        $html = str_replace('{SYMPHONY_URL}', SYMPHONY_URL, $html);
344
        $html = str_replace('{URL}', URL, $html);
345
346
        return $html;
347
    }
348
}
349
350
/**
351
 * `GenericErrorHandler` will catch any warnings or notices thrown by PHP and
352
 * raise the errors to Exceptions so they can be dealt with by the
353
 * `GenericExceptionHandler`. The type of errors that are raised to Exceptions
354
 * depends on the `error_reporting` level. All errors raised, except
355
 * `E_STRICT` are written to the Symphony log.
356
 */
357
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...
358
{
359
    /**
360
     * Whether the error handler is enabled or not, defaults to true.
361
     * Setting to false will prevent any Symphony error handling from occurring
362
     * @var boolean
363
     */
364
    public static $enabled = true;
365
366
    /**
367
     * An instance of the Log class, used to write errors to the log
368
     * @var Log
369
     */
370
    private static $_Log = null;
371
372
    /**
373
     * Whether to log errors or not.
374
     * This one is to be used temporarily, e.g., when PHP function is
375
     * supposed to error out and throw warning and log should be kept clean.
376
     *
377
     * @since Symphony 2.2.2
378
     * @var boolean
379
     * @example
380
     *  GenericErrorHandler::$logDisabled = true;
381
     *  DoSomethingThatEndsWithWarningsYouDoNotWantInLogs();
382
     *  GenericErrorHandler::$logDisabled = false;
383
     */
384
    public static $logDisabled = false;
385
386
    /**
387
     * An associative array with the PHP error constant as a key, and
388
     * a string describing that constant as the value
389
     * @var array
390
     */
391
    public static $errorTypeStrings = array(
392
        E_ERROR                 => 'Fatal Error',
393
        E_WARNING               => 'Warning',
394
        E_PARSE                 => 'Parsing Error',
395
        E_NOTICE                => 'Notice',
396
397
        E_CORE_ERROR            => 'Core Error',
398
        E_CORE_WARNING          => 'Core Warning',
399
        E_COMPILE_ERROR         => 'Compile Error',
400
        E_COMPILE_WARNING       => 'Compile Warning',
401
402
        E_USER_NOTICE           => 'User Notice',
403
        E_USER_WARNING          => 'User Warning',
404
        E_USER_ERROR            => 'User Error',
405
406
        E_STRICT                => 'Strict Notice',
407
        E_RECOVERABLE_ERROR     => 'Recoverable Error',
408
        E_DEPRECATED            => 'Deprecated Warning'
409
    );
410
411
    /**
412
     * Initialise will set the error handler to be the `__CLASS__::handler`
413
     * function.
414
     *
415
     * @param Log|null $Log (optional)
416
     *  An instance of a Symphony Log object to write errors to
417
     */
418
    public static function initialise(Log $Log = null)
419
    {
420
        if (!is_null($Log)) {
421
            self::$_Log = $Log;
422
        }
423
424
        set_error_handler(array(__CLASS__, 'handler'), error_reporting());
425
    }
426
427
    /**
428
     * Determines if the error handler is enabled by checking that error_reporting
429
     * is set in the php config and that $enabled is true
430
     *
431
     * @return boolean
432
     */
433
    public static function isEnabled()
434
    {
435
        return (bool)error_reporting() && self::$enabled;
436
    }
437
438
    /**
439
     * The handler function will write the error to the `$Log` if it is not
440
     * `E_STRICT` before raising the error as an Exception. This allows all `E_WARNING`
441
     * to actually be captured by an Exception handler.
442
     *
443
     * @since Symphony 2.6.4
444
     *  The method is final
445
     *
446
     * @param integer $code
447
     *  The error code, one of the PHP error constants
448
     * @param string $message
449
     *  The message of the error, this will be written to the log and
450
     *  displayed as the exception message
451
     * @param string $file
452
     *  The file that holds the logic that caused the error. Defaults to null
453
     * @param integer $line
454
     *  The line where the error occurred.
455
     * @throws ErrorException
456
     * @return string
457
     *  Usually a string of HTML that will displayed to a user
458
     */
459
    final public static function handler($code, $message, $file = null, $line = null)
460
    {
461
        // Only log if the error won't be raised to an exception and the error is not `E_STRICT`
462
        if (!self::$logDisabled && !in_array($code, array(E_STRICT)) && self::$_Log instanceof Log) {
463
            self::$_Log->pushToLog(sprintf(
464
                '%s %s: %s%s%s',
465
                __CLASS__,
466
                $code,
467
                $message,
468
                ($line ? " on line $line" : null),
469
                ($file ? " of file $file" : null)
470
            ), $code, true);
471
        }
472
473
        if (self::isEnabled()) {
474
            // prevent double logging
475
            GenericExceptionHandler::$logDisabled = true;
476
            // throw Error
477
            throw new ErrorException($message, 0, $code, $file, $line);
478
        }
479
    }
480
}
481