diff options
Diffstat (limited to 'inc/errorhandler.inc.php')
-rw-r--r-- | inc/errorhandler.inc.php | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/inc/errorhandler.inc.php b/inc/errorhandler.inc.php new file mode 100644 index 00000000..ce969966 --- /dev/null +++ b/inc/errorhandler.inc.php @@ -0,0 +1,153 @@ +<?php + +declare(strict_types=1); + +use JetBrains\PhpStorm\NoReturn; + +class ErrorHandler +{ + + + /** + * Displays an error message and stops script execution. + * If CONFIG_DEBUG is true, it will also dump a stack trace + * and all globally defined variables. + * (As this might reveal sensitive data you should never enable it in production) + */ + #[NoReturn] + public static function traceError(string $message): void + { + if ((defined('API') && API) || (defined('AJAX') && AJAX) || php_sapi_name() === 'cli') { + error_log('API ERROR: ' . $message); + error_log(self::formatBacktracePlain(debug_backtrace())); + } + if (php_sapi_name() === 'cli') { + // Don't spam HTML when invoked via cli, above error_log should have gone to stdout/stderr + exit(1); + } + Header('HTTP/1.1 500 Internal Server Error'); + if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'html') === false) { + Header('Content-Type: text/plain; charset=utf-8'); + echo 'API ERROR: ', $message, "\n", self::formatBacktracePlain(debug_backtrace()); + exit(0); + } + Header('Content-Type: text/html; charset=utf-8'); + echo '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><style>', "\n", + ".arg { color: red; background: white; }\n", + "h1 a { color: inherit; text-decoration: inherit; font-weight: inherit; }\n", + '</style><title>Fatal Error</title></head><body>'; + echo '<h1>Flagrant <a href="https://www.youtube.com/watch?v=7rrZ-sA4FQc&t=2m2s" target="_blank">S</a>ystem error</h1>'; + echo "<h2>Message</h2><pre>$message</pre>"; + if (strpos($message, 'Database') !== false) { + echo '<div><a href="install.php">Try running database setup</a></div>'; + } + echo "<br><br>"; + if (defined('CONFIG_DEBUG') && CONFIG_DEBUG) { + global $SLX_ERRORS; + if (!empty($SLX_ERRORS)) { + echo '<h2>PHP Errors</h2><pre>'; + foreach ($SLX_ERRORS as $error) { + echo htmlspecialchars("{$error['errstr']} ({$error['errfile']}:{$error['errline']}\n"); + } + echo '</pre>'; + } + echo "<h2>Stack Trace</h2>"; + echo '<pre>', self::formatBacktraceHtml(debug_backtrace()), '</pre>'; + echo "<h2>Globals</h2><pre>"; + echo htmlspecialchars(print_r($GLOBALS, true)); + echo '</pre>'; + } else { + echo <<<SADFACE +<pre> +________________________¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶________ +____________________¶¶¶___________________¶¶¶¶_____ +________________¶¶¶_________________________¶¶¶¶___ +______________¶¶______________________________¶¶¶__ +___________¶¶¶_________________________________¶¶¶_ +_________¶¶_____________________________________¶¶¶ +________¶¶_________¶¶¶¶¶___________¶¶¶¶¶_________¶¶ +______¶¶__________¶¶¶¶¶¶__________¶¶¶¶¶¶_________¶¶ +_____¶¶___________¶¶¶¶____________¶¶¶¶___________¶¶ +____¶¶___________________________________________¶¶ +___¶¶___________________________________________¶¶_ +__¶¶____________________¶¶¶¶____________________¶¶_ +_¶¶_______________¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶______________¶¶__ +_¶¶____________¶¶¶¶___________¶¶¶¶¶___________¶¶___ +¶¶¶_________¶¶¶__________________¶¶__________¶¶____ +¶¶_________¶______________________¶¶________¶¶_____ +¶¶¶______¶________________________¶¶_______¶¶______ +¶¶¶_____¶_________________________¶¶_____¶¶________ +_¶¶¶___________________________________¶¶__________ +__¶¶¶________________________________¶¶____________ +___¶¶¶____________________________¶¶_______________ +____¶¶¶¶______________________¶¶¶__________________ +_______¶¶¶¶¶_____________¶¶¶¶¶_____________________ +</pre> +SADFACE; + } + echo '</body></html>'; + exit(0); + } + + public static function formatBacktraceHtml(array $trace): string + { + $output = ''; + foreach ($trace as $idx => $line) { + $args = array(); + foreach ($line['args'] as $arg) { + $arg = self::formatArgument($arg); + $args[] = '<span class="arg">' . htmlspecialchars($arg) . '</span>'; + } + $frame = str_pad('#' . $idx, 3, ' ', STR_PAD_LEFT); + $function = htmlspecialchars($line['function']); + $args = implode(', ', $args); + $file = preg_replace('~(/[^/]+)$~', '<b>$1</b>', htmlspecialchars($line['file'])); + // Add line + $output .= $frame . ' ' . $function . '<b>(</b>' + . $args . '<b>)</b>' . ' @ <i>' . $file . '</i>:' . $line['line'] . "\n"; + } + return $output; + } + + public static function formatBacktracePlain(array $trace): string + { + $output = ''; + foreach ($trace as $idx => $line) { + $args = array(); + foreach ($line['args'] as $arg) { + $args[] = self::formatArgument($arg); + } + $frame = str_pad('#' . $idx, 3, ' ', STR_PAD_LEFT); + $args = implode(', ', $args); + // Add line + $output .= "\n" . $frame . ' ' . $line['function'] . '(' + . $args . ')' . ' @ ' . $line['file'] . ':' . $line['line']; + } + return $output; + } + + private static function formatArgument($arg, bool $expandArray = true): string + { + if (is_string($arg)) { + $arg = "'$arg'"; + } elseif (is_object($arg)) { + $arg = 'instanceof ' . get_class($arg); + } elseif (is_array($arg)) { + if ($expandArray && count($arg) < 20) { + $expanded = ''; + foreach ($arg as $key => $value) { + if (!empty($expanded)) { + $expanded .= ', '; + } + $expanded .= $key . ': ' . self::formatArgument($value, false); + if (strlen($expanded) > 200) + break; + } + if (strlen($expanded) <= 200) + return '[' . $expanded . ']'; + } + $arg = 'Array(' . count($arg) . ')'; + } + return (string)$arg; + } +}
\ No newline at end of file |