Fixed the exception system up.

This commit is contained in:
flash 2016-08-05 21:13:55 +02:00
parent 5ce8c55ebf
commit f3ff6ad6c2
12 changed files with 233 additions and 178 deletions

108
app/ExceptionHandler.php Normal file
View file

@ -0,0 +1,108 @@
<?php
/**
* Holds the exception (and error) handler.
* @package Sakura
*/
namespace Sakura;
use ErrorException;
use stdClass;
use Throwable;
/**
* Exception handler.
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class ExceptionHandler
{
/**
* Disables checking if the templating engine is available
* @var bool
*/
private static $disableTemplate = false;
/**
* The entry point for set_exception_handler.
* @param Throwable $ex
*/
public static function exception(Throwable $ex)
{
try {
$report = config('dev.report_host');
} catch (Exception $ex) {
$report = null;
}
if ($report !== null) {
self::report($ex, $report);
}
self::view($ex, $report !== null);
}
/**
* The entry point for set_error_handler.
* @param int $severity
* @param string $message
* @param string $file
* @param int $line
*/
public static function error($severity, $message, $file, $line)
{
throw new ErrorException($message, 0, $severity, $file, $line);
}
/**
* Display an error message.
* @param Throwable $ex
* @param bool $reported
*/
private static function view(Throwable $ex, $reported = false)
{
http_response_code(500);
try {
$debug = config('dev.show_errorsa');
} catch (Exception $ex) {
$debug = false;
}
if ($debug) {
header('Content-Type: text/plain');
echo $ex;
} else {
if (!self::$disableTemplate && Template::available()) {
echo view('errors/500', compact('reported'));
} else {
// Disable templates permanently so we don't get stuck in an infinite loop
// if an exception is caused by the templating engine.
self::$disableTemplate = true;
echo "Something broke so badly that the error page failed to render.";
if ($reported) {
echo " The developers have been notified of the issue.";
}
}
}
}
/**
* Posts the exception as json to a remote server.
* @param Throwable $ex
* @param string $destination
*/
private static function report(Throwable $ex, $destination)
{
$send = new stdClass;
$send->Date = date('c');
$send->Message = $ex->getMessage();
$send->Code = $ex->getCode();
$send->File = $ex->getFile();
$send->Line = $ex->getLine();
$send->Trace = $ex->getTraceAsString();
Net::request($destination, 'POST', json_encode($send));
}
}

View file

@ -201,26 +201,35 @@ class Net
}
/**
* Fetch a remote file.
* Make a web request.
* @param string $url
* @return mixed
* @param string $method
* @param mixed $params
* @return string
*/
public static function fetch($url)
public static function request($url, $method = 'GET', $params = null)
{
// Create a curl instance
$curl = curl_init();
// Set options
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($curl, CURLOPT_TIMEOUT, 4);
curl_setopt($curl, CURLOPT_USERAGENT, 'Sakura/' . SAKURA_VERSION);
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 2,
CURLOPT_TIMEOUT => 4,
CURLOPT_USERAGENT => 'Sakura/' . SAKURA_VERSION,
]);
switch (strtolower($method)) {
case 'post':
curl_setopt_array($curl, [
CURLOPT_POST => is_array($params) ? count($params) : 1,
CURLOPT_POSTFIELDS => is_array($params) ? http_build_query($params) : $params,
]);
break;
}
// Execute
$curl = curl_exec($curl);
// Return the data
return $curl;
}
}

View file

@ -172,11 +172,10 @@ class Router
return self::$dispatcher->dispatch($method, $url);
} catch (HttpMethodNotAllowedException $e) {
http_response_code(403);
return view('errors/403');
} catch (HttpRouteNotFoundException $e) {
http_response_code(404);
}
// Default to the not found page
return Template::render('global/notfound');
return view('errors/404');
}
}
}

View file

@ -120,6 +120,15 @@ class Template
}
}
/**
* Checks if twig is available.
* @return bool
*/
public static function available()
{
return self::$engine !== null && self::$name !== null;
}
/**
* Merge the parse variables.
* @param array $vars

View file

@ -87,6 +87,9 @@ show_changelog = false
; Host for the mahou serve command
host = localhost:8000
; Host for dumping exceptions, sends the exception as a json object in a raw post request. Leave empty to disable.
report_host =
; Mailing settings
[mail]
contact_address = sakura@localhost

View file

@ -0,0 +1 @@
{% include 'errors/404.twig' %}

View file

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Cannot find page</title>
<link rel="stylesheet" type="text/css" href="/error.css">
</head>
<body>
<div id="wrap">
<h1>
<img src="/images/404-info.gif">
The page cannot be found
</h1>
<p>
The page you are looking for might have been removed, had its
name changed, or is temporarily unavailable.
</p>
<hr>
<h2>
Please try the following:
</h2>
<ul>
<li>
If you typed the page address in the Address bar, make
sure that it is spelled correctly.
</li>
<li>
Open the <a href="{{ route('main.index') }}">flashii.net</a>
home page, and then look for links to the information you want.
</li>
<li>
Click the <img src="/images/404-back.gif"><a href="javascript:void(0);" onclick="history.go(-1);">Back</a>
button to try another link.
</li>
<li>
Click <img src="/images/404-search.gif"><a href="{{ route('main.search') }}">Search</a>
to look for information on the Internet.
</li>
</ul>
<h3>
HTTP 404 - File not found
<br>
Internet Explorer
</h3>
</div>
</body>
</html>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Internal Server Error</title>
<link rel="stylesheet" type="text/css" href="/error.css">
</head>
<body>
<div id="wrap">
<h1>
<img src="/images/404-info.gif">
The page is currently unavailable
</h1>
<p>
The page you are looking for experienced a severe explosion.
</p>
{% if reported %}
<p>
The developers have been notified of this issue, sorry for the inconvenience!
</p>
{% endif %}
<hr>
{% if message %}
<h2>
{{ message }}
</h2>
{% endif %}
<h3>
HTTP 500 - Internal Server Error
<br>
Internet Explorer
</h3>
</div>
</body>
</html>

View file

@ -1,47 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Cannot find page</title>
<link rel="stylesheet" type="text/css" href="/error.css">
</head>
<body>
<div id="wrap">
<h1>
<img src="/images/404-info.gif">
The page cannot be found
</h1>
<p>
The page you are looking for might have been removed, had its
name changed, or is temporarily unavailable.
</p>
<hr>
<h2>
Please try the following:
</h2>
<ul>
<li>
If you typed the page address in the Address bar, make
sure that it is spelled correctly.
</li>
<li>
Open the <a href="{{ route('main.index') }}">flashii.net</a>
home page, and then look for links to the information you want.
</li>
<li>
Click the <img src="/images/404-back.gif"><a href="javascript:void(0);" onclick="history.go(-1);">Back</a>
button to try another link.
</li>
<li>
Click <img src="/images/404-search.gif"><a href="{{ route('main.search') }}">Search</a>
to look for information on the Internet.
</li>
</ul>
<h3>
HTTP 404 - File not found
<br>
Internet Explorer
</h3>
</div>
</body>
</html>

View file

@ -15,6 +15,9 @@ if (php_sapi_name() === 'cli') {
define('IN_CLI', true);
}
// Turn error reporting on regardless of anything
error_reporting(-1);
// Override expiration variables
ignore_user_abort(true);
set_time_limit(0);
@ -35,12 +38,13 @@ if (!file_exists(ROOT . 'vendor/autoload.php')) {
// Include the autoloader
require_once ROOT . 'vendor/autoload.php';
// Register the handlers
set_exception_handler(['Sakura\ExceptionHandler', 'exception']);
set_error_handler(['Sakura\ExceptionHandler', 'error']);
// Load the configuration
Config::init(ROOT . 'config/config.ini');
set_error_handler('error_handler');
error_reporting(config('dev.show_errors') ? -1 : 0);
// Start the database module
$capsule = new DB;
$capsule->addConnection(config('database'));

View file

@ -169,116 +169,3 @@ function send_mail($to, $subject, $body)
return $mailer->send($message);
}
function error_handler($errno, $errstr, $errfile, $errline)
{
// Remove ROOT path from the error string and file location
$errstr = str_replace(ROOT, '', $errstr);
$errfile = str_replace(ROOT, '', $errfile);
switch ($errno) {
case E_ERROR:
case E_USER_ERROR:
$error = "<b>FATAL ERROR</b>";
break;
case E_WARNING:
case E_USER_WARNING:
$error = "<b>WARNING</b>";
break;
case E_NOTICE:
case E_USER_NOTICE:
$error = "<b>NOTICE</b>";
break;
default:
$error = "<b>Unknown error type</b> [{$errno}]";
}
$error .= ": {$errstr} on line {$errline} in {$errfile}";
// Truncate all previous outputs
ob_clean();
ob_end_clean();
// Check for dev mode
$detailed = config('dev.show_errors');
// Build page (not even going to bother cleaning this one up)
$errorPage = '<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sakura Internal Error</title>
<style type="text/css">
body { margin: 0; padding: 0; background: #EEE; color: #000;
font: 12px/20px Verdana, Arial, Helvetica, sans-serif; }
h1, h2 { font-weight: 100; background: #CAA; padding: 8px 5px 10px;
margin: 0; font-style: italic; font-family: serif; }
h1 { border-radius: 8px 8px 0 0; }
h2 { margin: 0 -10px; }
.container { border: 1px solid #CAA; margin: 10px auto; background: #FFF;
box-shadow: 2px 2px 1em #888; max-width: 1024px; border-radius: 10px; }
.container .inner { padding: 0 10px; }
.container .inner .error { background: #555; color: #EEE; border-left: 5px solid #C22;
padding: 4px 6px; text-shadow: 0 1px 1px #888; white-space: pre-wrap;
word-wrap: break-word; margin: 12px 0; border-radius: 5px; box-shadow: inset 0 0 1em #333; }
.container .footer { border-top: 1px solid #CAA; font-size: x-small; padding: 0 5px 1px; }
a { color: #77E; text-decoration: none; }
a:hover { text-decoration: underline; }
a:active { color: #E77; }
</style>
</head>
<body>
<div class="container">
<h1>An error occurred while executing the script.</h1>
<div class="inner">
<p>To prevent potential security risks or data loss Sakura has stopped execution of the script.</p>';
if (isset($errid)) {
$errorPage .= '<p>The error and surrounding data has been logged.</p>
<h2>' . (!$detailed ? 'Report the following text to a staff member' : 'Logged as') . '</h2>
<pre class="error">' . $errid . '</pre>';
} else {
$errorPage .= '<p>Sakura was not able to log this error which could mean that there was an error
with the database connection. If you\'re the system administrator check the database credentials
and make sure the server is running and if you\'re not please let the system administrator
know about this error if it occurs again.</p>';
}
if ($detailed) {
$errorPage .= ' <h2>Summary</h2>
<pre class="error">' . $error . '</pre>
<h2>Backtraces</h2>';
foreach (debug_backtrace() as $num => $trace) {
$errorPage .= '<h3>#' . $num . '</h3><pre class="error">';
foreach ($trace as $key => $val) {
$errorPage .=
str_pad(
'[' . $key . ']',
12
) . '=> ' . (
is_array($val) || is_object($val) ?
json_encode($val) :
$val
) . "\r\n";
}
$errorPage .= '</pre>';
}
}
$errorPage .= '</div>
<div class="footer">
Sakura r' . SAKURA_VERSION . '.
</div>
</div>
</body>
</html>';
// Die and display error message
die($errorPage);
}