'<', '>' => '>', '"' => '"', "\t" => ' ']); $error_message = strtr($error_message, ['<br>' => "
", '<br />' => "
", '<b>' => '', '</b>' => '', '<pre>' => '
', '</pre>' => '
']); // Generate a bunch of useful information to ease debugging later. $debug_info = self::getDebugInfo(debug_backtrace()); // Log the error in the database. self::logError($error_message, $debug_info, $file, $line); // Are we considering this fatal? Then display and exit. // !!! TODO: should we consider warnings fatal? if (true) // DEBUG || (!DEBUG && $error_level === E_WARNING || $error_level === E_USER_WARNING)) self::display($file . ' (' . $line . ')
' . $error_message, $debug_info); // If it wasn't a fatal error, well... self::$handling_error = false; } public static function getDebugInfo(array $trace) { $debug_info = "Backtrace:\n"; $debug_info .= self::formatBacktrace($trace); // Include info on the contents of superglobals. if (!empty($_SESSION)) $debug_info .= "\nSESSION: " . print_r($_SESSION, true); if (!empty($_POST)) $debug_info .= "\nPOST: " . print_r($_POST, true); if (!empty($_GET)) $debug_info .= "\nGET: " . print_r($_GET, true); return $debug_info; } private static function formatBacktrace(array $trace) { $buffer = ''; $skipping = true; foreach ($trace as $i => $call) { if (isset($call['class']) && ($call['class'] === 'ErrorHandler' || $call['class'] === 'Database') || isset($call['function']) && $call['function'] === 'preg_replace_callback') { if (!$skipping) { $buffer .= "[...]\n"; $skipping = true; } continue; } else $skipping = false; $file = isset($call['file']) ? str_replace(BASEDIR, '', $call['file']) : 'Unknown'; $object = isset($call['class']) ? $call['class'] . $call['type'] : ''; $args = []; foreach ($call['args'] as $j => $arg) { if (is_array($arg)) $args[$j] = print_r($arg, true); elseif (is_object($arg)) $args[$j] = var_dump($arg); } $buffer .= '#' . str_pad($i, 3, ' ') . $object . $call['function'] . '(' . implode(', ', $args) . ')' . ' called at [' . $file . ':' . $call['line'] . "]\n"; } return $buffer; } // Logs an error into the database. private static function logError($error_message = '', $debug_info = '', $file = '', $line = 0) { if (!ErrorLog::log([ 'message' => $error_message, 'debug_info' => $debug_info, 'file' => str_replace(BASEDIR, '', $file), 'line' => $line, 'id_user' => Registry::has('user') ? Registry::get('user')->getUserId() : 0, 'ip_address' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '', 'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '', ])) { header('HTTP/1.1 503 Service Temporarily Unavailable'); echo '

An Error Occured

Our software could not connect to the database. We apologise for any inconvenience and ask you to check back later.

'; exit; } return $error_message; } public static function display($message, $debug_info) { // Just show the message if we're running in a console. if (empty($_SERVER['HTTP_HOST'])) { echo $message; exit; } // Initialise the main template to present a nice message to the user. $page = new MainTemplate('An error occured!'); // Show the error. $is_admin = Registry::has('user') && Registry::get('user')->isAdmin(); if (DEBUG || $is_admin) { $page->adopt(new DummyBox('An error occured!', '

' . $message . '

' . $debug_info . '
')); // Let's provide the admin navigation despite it all! if ($is_admin) { $page->appendStylesheet(BASEURL . '/css/admin.css'); $page->adopt(new AdminBar()); } } else $page->adopt(new DummyBox('An error occured!', '

Our apologies, an error occured while we were processing your request. Please try again later, or contact us if the problem persists.

')); // If we got this far, make sure we're not showing stuff twice. ob_end_clean(); // Render the page. $page->html_main(); exit; } }