'<', '>' => '>', '"' => '"', "\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;
}
}