From 88dfe7a9508a340296908b3d1b7bbef29d1863a0 Mon Sep 17 00:00:00 2001 From: flashwave Date: Sat, 14 Jul 2018 19:57:21 +0200 Subject: [PATCH] Added error reporting. --- misuzu.php | 6 +- src/Application.php | 8 ++- src/ExceptionHandler.php | 131 +++++++++++++++++++++------------------ 3 files changed, 80 insertions(+), 65 deletions(-) diff --git a/misuzu.php b/misuzu.php index 284fd87a..21705e38 100644 --- a/misuzu.php +++ b/misuzu.php @@ -126,6 +126,8 @@ if (PHP_SAPI === 'cli') { } } } else { + ob_start($app->inDebugMode() ? null : 'ob_gzhandler'); + $storage_dir = $app->getStoragePath(); if (!$storage_dir->isReadable() || !$storage_dir->isWritable()) { @@ -133,10 +135,6 @@ if (PHP_SAPI === 'cli') { exit; } - if (!$app->inDebugMode()) { - ob_start('ob_gzhandler'); - } - $app->startTemplating(); $tpl = $app->getTemplating(); diff --git a/src/Application.php b/src/Application.php index ab0b45f2..42f50009 100644 --- a/src/Application.php +++ b/src/Application.php @@ -61,9 +61,13 @@ class Application extends ApplicationBase { parent::__construct(); $this->debugMode = $debug; - ExceptionHandler::register(); - ExceptionHandler::debug($this->debugMode); $this->configInstance = new ConfigManager($configFile); + + ExceptionHandler::register( + $debug, + $this->configInstance->get('Exceptions', 'report_url', 'string', null), + $this->configInstance->get('Exceptions', 'hash_key', 'string', null) + ); } /** diff --git a/src/ExceptionHandler.php b/src/ExceptionHandler.php index c744a573..74d7fb76 100644 --- a/src/ExceptionHandler.php +++ b/src/ExceptionHandler.php @@ -17,6 +17,12 @@ class ExceptionHandler */ private static $reportUrl = null; + /** + * HMAC key that will be used to sign the request. + * @var string + */ + private static $reportSign = null; + /** * Whether debug mode is active. * If true (or in CLI) a backtrace will be displayed. @@ -25,19 +31,19 @@ class ExceptionHandler */ private static $debugMode = false; - /** - * Internal bool used to prevent an infinite loop when the templating engine is not available. - * @var bool - */ - private static $failSafe = false; - /** * Registers the exception handler and make it so all errors are thrown as ErrorExceptions. + * @param bool $debugMode + * @param string|null $reportUrl + * @param string|null $reportSign */ - public static function register(): void + public static function register(bool $debugMode, ?string $reportUrl = null, ?string $reportSign = null): void { - set_exception_handler([static::class, 'exception']); - set_error_handler([static::class, 'error']); + self::$debugMode = $debugMode; + self::$reportUrl = $reportUrl; + self::$reportSign = $reportSign; + set_exception_handler([self::class, 'exception']); + set_error_handler([self::class, 'error']); } /** @@ -49,15 +55,6 @@ class ExceptionHandler restore_error_handler(); } - /** - * Turns debug mode on or off. - * @param bool $mode - */ - public static function debug(bool $mode): void - { - static::$debugMode = $mode; - } - /** * The actual handler for rendering and reporting exceptions. * Checks if the exception is extends on HttpException, @@ -66,14 +63,13 @@ class ExceptionHandler */ public static function exception(Throwable $exception): void { - $is_http = is_subclass_of($exception, HttpException::class); - $report = !static::$debugMode && !$is_http && static::$reportUrl !== null; + $report = !self::$debugMode && self::$reportUrl !== null; if ($report) { - static::report($exception); + self::report($exception); } - static::render($exception, $report); + self::render($exception, $report); } /** @@ -92,59 +88,76 @@ class ExceptionHandler /** * Shoots a POST request to the report URL. * @todo Implement this. - * @param Throwable $exception + * @param Throwable $throwable */ - private static function report(Throwable $exception): void + private static function report(Throwable $throwable): bool { - // send POST request with json encoded exception to destination + if (!strlen(self::$reportUrl)) { + return false; + } + + if (!($curl = curl_init(self::$reportUrl))) { + return false; + } + + $json = json_encode([ + 'git' => [ + 'branch' => git_branch(), + 'hash' => git_commit_hash(true), + ], + 'exception' => [ + 'type' => get_class($throwable), + 'message' => $throwable->getMessage(), + 'code' => $throwable->getCode(), + 'file' => str_replace(dirname(__DIR__, 1), '', $throwable->getFile()), + 'line' => $throwable->getLine(), + 'trace' => $throwable->getTraceAsString(), + ], + ]); + + $headers = [ + 'Content-Type: application/json;charset=utf-8', + ]; + + if (strlen(self::$reportSign)) { + $headers[] = 'X-Misuzu-Signature: sha256=' . hash_hmac('sha256', $json, self::$reportSign); + } + + $setOpts = curl_setopt_array($curl, [ + CURLOPT_TCP_NODELAY => true, + CURLOPT_POSTFIELDS => $json, + CURLOPT_HTTPHEADER => $headers, + ]); + + if (!$setOpts) { + return false; + } + + return curl_exec($curl) !== false; } /** * Renders exceptions. * In debug or cli mode a backtrace is displayed. - * Otherwise if the error extends on HttpException the respective error code is set. - * If the View alias is still available the script will attempt to render a path 'errors/{error code}.twig'. * @param Throwable $exception * @param bool $reported */ private static function render(Throwable $exception, bool $reported): void { - $is_http = false;//$exception instanceof HttpException; - - if (PHP_SAPI === 'cli' || (!$is_http && static::$debugMode)) { - if (PHP_SAPI !== 'cli' && !headers_sent()) { - http_response_code(500); - header('Content-Type: text/plain'); - } + if (PHP_SAPI !== 'cli' && !headers_sent()) { + ob_clean(); + http_response_code(500); + header('Content-Type: text/plain'); + } + if (PHP_SAPI === 'cli' || self::$debugMode) { echo $exception; - return; - } + } else { + echo 'Something broke!'; - $http_code = $is_http ? $exception->httpCode : 500; - http_response_code($http_code); - - static::$failSafe = true; - /*if (!static::$failSafe && View::available()) { - static::$failSafe = true; - $template = "errors.{$http_code}"; - $namespace = View::findNamespace($template); - - if ($namespace !== null) { - echo View::render("@{$namespace}.{$template}", compact('reported')); - return; + if ($reported) { + echo PHP_EOL . 'Information about this error has been sent to the devs.'; } - }*/ - - if ($is_http) { - echo "Error {$http_code}"; - return; - } - - echo "Something broke!"; - - if ($reported) { - echo "
The error has been reported to the developers."; } } }