From 25e434bce4986b48bd72729a55aa1096e5a76be3 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Fri, 24 Nov 2017 15:08:43 +0100 Subject: Refactored ExceptionHandler --- src/Exceptions/BasicHandler.php | 119 -------------------------- src/Exceptions/ExceptionsServiceProvider.php | 46 +++++++++- src/Exceptions/Handler.php | 100 ++++++++++++++++++++-- src/Exceptions/Handlers/HandlerInterface.php | 21 +++++ src/Exceptions/Handlers/Legacy.php | 42 +++++++++ src/Exceptions/Handlers/LegacyDevelopment.php | 57 ++++++++++++ src/Exceptions/Handlers/Whoops.php | 85 ++++++++++++++++++ 7 files changed, 344 insertions(+), 126 deletions(-) delete mode 100644 src/Exceptions/BasicHandler.php create mode 100644 src/Exceptions/Handlers/HandlerInterface.php create mode 100644 src/Exceptions/Handlers/Legacy.php create mode 100644 src/Exceptions/Handlers/LegacyDevelopment.php create mode 100644 src/Exceptions/Handlers/Whoops.php (limited to 'src') diff --git a/src/Exceptions/BasicHandler.php b/src/Exceptions/BasicHandler.php deleted file mode 100644 index 2ba960a2..00000000 --- a/src/Exceptions/BasicHandler.php +++ /dev/null @@ -1,119 +0,0 @@ -exceptionHandler($exception); - } - - /** - * @param Throwable $e - */ - public function exceptionHandler($e) - { - $this->handle( - $e->getCode(), - get_class($e) . ': ' . $e->getMessage(), - $e->getFile(), - $e->getLine(), - ['exception' => $e] - ); - } - - /** - * @param int $number - * @param string $string - * @param string $file - * @param int $line - * @param array $context - * @param array $trace - */ - protected function handle($number, $string, $file, $line, $context = [], $trace = []) - { - error_log(sprintf('Exception: Number: %s, String: %s, File: %s:%u, Context: %s', - $number, - $string, - $file, - $line, - json_encode($context) - )); - - $file = $this->stripBasePath($file); - - if ($this->environment == self::ENV_DEVELOPMENT) { - echo '
';
-            echo sprintf('%s: (%s)' . PHP_EOL, ucfirst($type), $number);
-            var_export([
-                'string'     => $string,
-                'file'       => $file . ':' . $line,
-                'context'    => $context,
-                'stacktrace' => $this->formatStackTrace($trace),
-            ]);
-            echo '
'; - die(); - } - - echo 'An unexpected error occurred, a team of untrained monkeys has been dispatched to deal with it.'; - die(); - } - - /** - * @param array $stackTrace - * @return array - */ - protected function formatStackTrace($stackTrace) - { - $return = []; - - foreach ($stackTrace as $trace) { - $path = ''; - $line = ''; - - if (isset($trace['file']) && isset($trace['line'])) { - $path = $this->stripBasePath($trace['file']); - $line = $trace['line']; - } - - $functionName = $trace['function']; - - $return[] = [ - 'file' => $path . ':' . $line, - $functionName => $trace['args'], - ]; - } - - return $return; - } - - /** - * @param string $path - * @return string - */ - protected function stripBasePath($path) - { - $basePath = realpath(__DIR__ . '/../..') . '/'; - return str_replace($basePath, '', $path); - } -} diff --git a/src/Exceptions/ExceptionsServiceProvider.php b/src/Exceptions/ExceptionsServiceProvider.php index 8eeccf61..a9bc2b17 100644 --- a/src/Exceptions/ExceptionsServiceProvider.php +++ b/src/Exceptions/ExceptionsServiceProvider.php @@ -3,14 +3,56 @@ namespace Engelsystem\Exceptions; use Engelsystem\Container\ServiceProvider; +use Engelsystem\Exceptions\Handlers\HandlerInterface; +use Engelsystem\Exceptions\Handlers\Legacy; +use Engelsystem\Exceptions\Handlers\LegacyDevelopment; +use Engelsystem\Exceptions\Handlers\Whoops; +use Whoops\Run as WhoopsRunner; class ExceptionsServiceProvider extends ServiceProvider { public function register() { - $errorHandler = $this->app->make(BasicHandler::class); - $errorHandler->register(); + $errorHandler = $this->app->make(Handler::class); + $this->addProductionHandler($errorHandler); + $this->addDevelopmentHandler($errorHandler); $this->app->instance('error.handler', $errorHandler); $this->app->bind(Handler::class, 'error.handler'); + $errorHandler->register(); + } + + public function boot() + { + /** @var Handler $handler */ + $handler = $this->app->get('error.handler'); + $request = $this->app->get('request'); + + $handler->setRequest($request); + } + + /** + * @param Handler $errorHandler + */ + protected function addProductionHandler($errorHandler) + { + $handler = $this->app->make(Legacy::class); + $this->app->instance('error.handler.production', $handler); + $errorHandler->setHandler(Handler::ENV_PRODUCTION, $handler); + $this->app->bind(HandlerInterface::class, 'error.handler.production'); + } + + /** + * @param Handler $errorHandler + */ + protected function addDevelopmentHandler($errorHandler) + { + $handler = $this->app->make(LegacyDevelopment::class); + + if (class_exists(WhoopsRunner::class)) { + $handler = $this->app->make(Whoops::class); + } + + $this->app->instance('error.handler.development', $handler); + $errorHandler->setHandler(Handler::ENV_DEVELOPMENT, $handler); } } diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php index cdf94e32..ee15717a 100644 --- a/src/Exceptions/Handler.php +++ b/src/Exceptions/Handler.php @@ -2,18 +2,29 @@ namespace Engelsystem\Exceptions; -abstract class Handler +use Engelsystem\Exceptions\Handlers\HandlerInterface; +use Engelsystem\Http\Request; +use ErrorException; +use Throwable; + +class Handler { /** @var string */ protected $environment; + /** @var HandlerInterface[] */ + protected $handler = []; + + /** @var Request */ + protected $request; + const ENV_PRODUCTION = 'prod'; const ENV_DEVELOPMENT = 'dev'; /** * Handler constructor. * - * @param string $environment production|development + * @param string $environment prod|dev */ public function __construct($environment = self::ENV_PRODUCTION) { @@ -25,14 +36,47 @@ abstract class Handler */ public function register() { + set_error_handler([$this, 'errorHandler']); + set_exception_handler([$this, 'exceptionHandler']); } /** - * @param string $environment + * @param int $number + * @param string $message + * @param string $file + * @param int $line */ - public function setEnvironment($environment) + public function errorHandler($number, $message, $file, $line) { - $this->environment = $environment; + $exception = new ErrorException($message, 0, $number, $file, $line); + $this->exceptionHandler($exception); + } + + /** + * @param Throwable $e + */ + public function exceptionHandler($e) + { + if (!$this->request instanceof Request) { + $this->request = new Request(); + } + + $handler = $this->handler[$this->environment]; + $handler->report($e); + $handler->render($this->request, $e); + $this->die(); + } + + /** + * Exit the application + * + * @codeCoverageIgnore + * @param string $message + */ + protected function die($message = '') + { + echo $message; + die(); } /** @@ -42,4 +86,50 @@ abstract class Handler { return $this->environment; } + + /** + * @param string $environment + */ + public function setEnvironment($environment) + { + $this->environment = $environment; + } + + /** + * @param string $environment + * @return HandlerInterface|HandlerInterface[] + */ + public function getHandler($environment = null) + { + if (!is_null($environment)) { + return $this->handler[$environment]; + } + + return $this->handler; + } + + /** + * @param string $environment + * @param HandlerInterface $handler + */ + public function setHandler($environment, HandlerInterface $handler) + { + $this->handler[$environment] = $handler; + } + + /** + * @return Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * @param Request $request + */ + public function setRequest(Request $request) + { + $this->request = $request; + } } diff --git a/src/Exceptions/Handlers/HandlerInterface.php b/src/Exceptions/Handlers/HandlerInterface.php new file mode 100644 index 00000000..9de33e1f --- /dev/null +++ b/src/Exceptions/Handlers/HandlerInterface.php @@ -0,0 +1,21 @@ +unexpected error occurred, a team of untrained monkeys has been dispatched to deal with it.'; + } + + /** + * @param Throwable $e + */ + public function report(Throwable $e) + { + error_log(sprintf('Exception: Code: %s, Message: %s, File: %s:%u, Trace: %s', + $e->getCode(), + $e->getMessage(), + $this->stripBasePath($e->getFile()), + $e->getLine(), + json_encode($e->getTrace()) + )); + } + + /** + * @param string $path + * @return string + */ + protected function stripBasePath($path) + { + $basePath = realpath(__DIR__ . '/../../..') . '/'; + return str_replace($basePath, '', $path); + } +} diff --git a/src/Exceptions/Handlers/LegacyDevelopment.php b/src/Exceptions/Handlers/LegacyDevelopment.php new file mode 100644 index 00000000..86f86f4c --- /dev/null +++ b/src/Exceptions/Handlers/LegacyDevelopment.php @@ -0,0 +1,57 @@ +stripBasePath($e->getFile()); + + echo '
';
+        echo sprintf('%s: (%s)' . PHP_EOL, get_class($e), $e->getCode());
+        $data = [
+            'string'     => $e->getMessage(),
+            'file'       => $file . ':' . $e->getLine(),
+            'stacktrace' => $this->formatStackTrace($e->getTrace()),
+        ];
+        var_dump($data);
+        echo '
'; + } + + /** + * @param array $stackTrace + * @return array + */ + protected function formatStackTrace($stackTrace) + { + $return = []; + $stackTrace = array_reverse($stackTrace); + + foreach ($stackTrace as $trace) { + $path = ''; + $line = ''; + + if (isset($trace['file']) && isset($trace['line'])) { + $path = $this->stripBasePath($trace['file']); + $line = $trace['line']; + } + + $functionName = $trace['function']; + + $return[] = [ + 'file' => $path . ':' . $line, + $functionName => isset($trace['args']) ? $trace['args'] : null, + ]; + } + + return $return; + } +} diff --git a/src/Exceptions/Handlers/Whoops.php b/src/Exceptions/Handlers/Whoops.php new file mode 100644 index 00000000..807f5eb0 --- /dev/null +++ b/src/Exceptions/Handlers/Whoops.php @@ -0,0 +1,85 @@ +app = $app; + } + + /** + * @param Request $request + * @param Throwable $e + */ + public function render($request, Throwable $e) + { + $whoops = $this->app->make(WhoopsRunner::class); + $handler = $this->getPrettyPageHandler($e); + $whoops->pushHandler($handler); + + if ($request->isXmlHttpRequest()) { + $handler = $this->getJsonResponseHandler(); + $whoops->pushHandler($handler); + } + + echo $whoops->handleException($e); + } + + /** + * @param Throwable $e + * @return PrettyPageHandler + */ + protected function getPrettyPageHandler(Throwable $e) + { + $handler = $this->app->make(PrettyPageHandler::class); + + $handler->setPageTitle('Just another ' . get_class($e) . ' to fix :('); + $handler->setApplicationPaths([realpath(__DIR__ . '/../..')]); + + $data = $this->getData(); + $handler->addDataTable('Application', $data); + + return $handler; + } + + /** + * @return JsonResponseHandler + */ + protected function getJsonResponseHandler() + { + $handler = $this->app->make(JsonResponseHandler::class); + $handler->setJsonApi(true); + $handler->addTraceToOutput(true); + + return $handler; + } + + /** + * Aggregate application data + * + * @return array + */ + protected function getData() + { + global $user; + + $data = []; + $data['user'] = $user; + $data['Booted'] = $this->app->isBooted(); + + return $data; + } +} -- cgit v1.2.3-54-g00ecf