summaryrefslogtreecommitdiffstats
path: root/application
diff options
context:
space:
mode:
authorFlorian Pritz <bluewind@xinu.at>2015-06-04 22:09:05 +0200
committerFlorian Pritz <bluewind@xinu.at>2015-06-04 22:09:05 +0200
commit048410164d2a17bbb588e43351e44eeb81610960 (patch)
tree0331c096d68d78496342accbd92b341113fe72b3 /application
parent8b3525c2b79f74089b623ca6870ddb9b06270e5d (diff)
Refactor exception handling into dedicated class
Signed-off-by: Florian Pritz <bluewind@xinu.at>
Diffstat (limited to 'application')
-rw-r--r--application/controllers/api.php2
-rw-r--r--application/controllers/tools.php2
-rw-r--r--application/libraries/ExceptionHandler.php154
3 files changed, 156 insertions, 2 deletions
diff --git a/application/controllers/api.php b/application/controllers/api.php
index 644a726e7..9540f1ff7 100644
--- a/application/controllers/api.php
+++ b/application/controllers/api.php
@@ -59,7 +59,7 @@ class Api extends MY_Controller {
} catch (\exceptions\PublicApiException $e) {
return send_json_error_reply($e->get_error_id(), $e->getMessage(), $e->get_data());
} catch (\Exception $e) {
- _log_exception($e);
+ \libraries\ExceptionHandler::log_exception($e);
return send_json_error_reply("internal-error", "An unhandled internal server error occured");
}
}
diff --git a/application/controllers/tools.php b/application/controllers/tools.php
index f2fa1f97c..f0d8ce6f9 100644
--- a/application/controllers/tools.php
+++ b/application/controllers/tools.php
@@ -89,7 +89,7 @@ class Tools extends MY_Controller {
$test->cleanup();
} catch (\Exception $e) {
echo "not ok - uncaught exception in {$testcase}->{$method->name}\n";
- _actual_exception_handler($e);
+ \libraries\ExceptionHandler::exception_handler($e);
$exitcode = 255;
}
}
diff --git a/application/libraries/ExceptionHandler.php b/application/libraries/ExceptionHandler.php
new file mode 100644
index 000000000..7dc954f2b
--- /dev/null
+++ b/application/libraries/ExceptionHandler.php
@@ -0,0 +1,154 @@
+<?php
+/*
+ * Copyright 2015 Florian "Bluewind" Pritz <bluewind@server-speed.net>
+ *
+ * Licensed under AGPLv3
+ * (see COPYING for full license text)
+ *
+ */
+
+namespace libraries;
+
+class ExceptionHandler {
+ static function setup()
+ {
+ set_error_handler(array("\libraries\ExceptionHandler", "error_handler"));
+ set_exception_handler(array("\libraries\ExceptionHandler", 'exception_handler'));
+ register_shutdown_function(array("\libraries\ExceptionHandler", "check_for_fatal"));
+ assert_options(ASSERT_ACTIVE, true);
+ assert_options(ASSERT_CALLBACK, array("\libraries\ExceptionHandler", '_assert_failure'));
+ }
+
+ static function error_handler($errno, $errstr, $errfile, $errline)
+ {
+ if (!(error_reporting() & $errno)) {
+ // This error code is not included in error_reporting
+ return;
+ }
+ throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
+ }
+
+ // Source: https://gist.github.com/abtris/1437966
+ static private function getExceptionTraceAsString($exception) {
+ $rtn = "";
+ $count = 0;
+ foreach ($exception->getTrace() as $frame) {
+ $args = "";
+ if (isset($frame['args'])) {
+ $args = array();
+ foreach ($frame['args'] as $arg) {
+ if (is_string($arg)) {
+ $args[] = "'" . $arg . "'";
+ } elseif (is_array($arg)) {
+ $args[] = "Array";
+ } elseif (is_null($arg)) {
+ $args[] = 'NULL';
+ } elseif (is_bool($arg)) {
+ $args[] = ($arg) ? "true" : "false";
+ } elseif (is_object($arg)) {
+ $args[] = get_class($arg);
+ } elseif (is_resource($arg)) {
+ $args[] = get_resource_type($arg);
+ } else {
+ $args[] = $arg;
+ }
+ }
+ $args = join(", ", $args);
+ }
+ $rtn .= sprintf( "#%s %s(%s): %s(%s)\n",
+ $count,
+ isset($frame['file']) ? $frame['file'] : 'unknown file',
+ isset($frame['line']) ? $frame['line'] : 'unknown line',
+ (isset($frame['class'])) ? $frame['class'].$frame['type'].$frame['function'] : $frame['function'],
+ $args );
+ $count++;
+ }
+ return $rtn;
+ }
+
+ static public function log_exception($ex)
+ {
+ $exceptions = array($ex);
+ while ($ex->getPrevious() !== null) {
+ $ex = $ex->getPrevious();
+ $exceptions[] = $ex;
+ }
+
+ foreach ($exceptions as $key => $e) {
+ $message = sprintf("Exception %d/%d '%s' with message '%s' in %s:%d\n", $key+1, count($exceptions), get_class($e), $e->getMessage(), $e->getFile(), $e->getLine());
+ if (method_exists($e, "get_error_id")) {
+ $message .= 'Error ID: '.$e->get_error_id()."\n";
+ }
+ if (method_exists($e, "get_data") && $e->get_data() !== NULL) {
+ $message .= 'Data: '.var_export($e->get_data(), true)."\n";
+ }
+ $message .= "Backtrace:\n".self::getExceptionTraceAsString($e)."\n";
+ error_log($message);
+ }
+ }
+
+ // The actual exception handler
+ static public function exception_handler($ex)
+ {
+ self::log_exception($ex);
+
+ $display_errors = in_array(strtolower(ini_get('display_errors')), array('1', 'on', 'true', 'stdout'));
+ if (php_sapi_name() === 'cli' OR defined('STDIN')) {
+ $display_errors = true;
+ }
+
+ $GLOBALS["is_error_page"] = true;
+ $heading = "Internal Server Error";
+ $message = "<p>An unhandled error occured.</p>\n";
+
+ if ($display_errors) {
+ $exceptions = array($ex);
+ while ($ex->getPrevious() !== null) {
+ $ex = $ex->getPrevious();
+ $exceptions[] = $ex;
+ }
+
+ foreach ($exceptions as $key => $e) {
+ $backtrace = self::getExceptionTraceAsString($e);
+ $message .= '<div>';
+ $message .= '<b>Exception '.($key+1).' of '.count($exceptions).'</b><br>';
+ $message .= '<b>Fatal error</b>: Uncaught exception '.htmlspecialchars(get_class($e)).'<br>';
+ $message .= '<b>Message</b>: '.htmlspecialchars($e->getMessage()).'<br>';
+ if (method_exists($e, "get_error_id")) {
+ $message .= '<b>Error ID</b>: '.htmlspecialchars($e->get_error_id()).'<br>';
+ }
+ if (method_exists($e, "get_data") && $e->get_data() !== NULL) {
+ $message .= '<b>Data</b>: <pre>'.htmlspecialchars(var_export($e->get_data(), true)).'</pre><br>';
+ }
+ $message .= '<b>Backtrace:</b><br>';
+ $message .= '<pre>'.htmlspecialchars(str_replace(FCPATH, "./", $backtrace)).'</pre>';
+ $message .= 'thrown in <b>'.htmlspecialchars($e->getFile()).'</b> on line <b>'.htmlspecialchars($e->getLine()).'</b><br>';
+ $message .= '</div>';
+ }
+ } else {
+ $message .="<p>More information can be found in the php error log or by enabling display_errors.</p>";
+ }
+
+ $message = "$message";
+ include FCPATH.APPPATH."/errors/error_general.php";
+ }
+
+ /**
+ * Checks for a fatal error, work around for set_error_handler not working on fatal errors.
+ */
+ static public function check_for_fatal()
+ {
+ $error = error_get_last();
+ if ($error["type"] == E_ERROR) {
+ self::exception_handler(new \ErrorException(
+ $error["message"], 0, $error["type"], $error["file"], $error["line"]));
+ }
+ }
+
+ static public function assert_failure($file, $line, $expr, $message = "")
+ {
+ self::exception_handler(new Exception("assert($expr): Assertion failed in $file at line $line".($message != "" ? " with message: '$message'" : "")));
+ exit(1);
+ }
+
+}