From 11e76b196524772267c0544d9eed15740a76e665 Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Fri, 9 Aug 2019 20:35:42 +0200 Subject: [PATCH 1/7] Implement new error handling stack This separates the error registry (mapping exception types to status codes) from actual handling (the middleware) as well as error formatting (Whoops, pretty error pages or JSON-API?) and reporting (log? Sentry?). The components can be reused in different places (e.g. the API client and the error handler middleware both need the registry to understand all the exceptions Flarum knows how to handle), while still allowing to change only the parts that need to change (the API stack always uses the JSON-API formatter, and the forum stack switches between Whoops and pretty error pages based on debug mode). Finally, this paves the way for some planned features and extensibility: - A console error handler can build on top of the registry. - Extensions can register new exceptions and how to handle them. - Extensions can change how we report exceptions (e.g. Sentry). - We can build more pretty error pages, even different ones for exceptions having the same status code. --- .../Foundation/ErrorHandling/Formatter.php | 20 +++++ .../Foundation/ErrorHandling/HandledError.php | 67 ++++++++++++++++ .../ErrorHandling/JsonApiRenderer.php | 33 ++++++++ .../Foundation/ErrorHandling/LogReporter.php | 32 ++++++++ .../src/Foundation/ErrorHandling/Registry.php | 73 +++++++++++++++++ .../src/Foundation/ErrorHandling/Reporter.php | 17 ++++ .../Foundation/ErrorHandling/ViewRenderer.php | 78 +++++++++++++++++++ .../ErrorHandling/WhoopsRenderer.php | 25 ++++++ framework/core/src/Foundation/KnownError.php | 17 ++++ .../core/src/Http/Middleware/HandleErrors.php | 64 +++++++++++++++ 10 files changed, 426 insertions(+) create mode 100644 framework/core/src/Foundation/ErrorHandling/Formatter.php create mode 100644 framework/core/src/Foundation/ErrorHandling/HandledError.php create mode 100644 framework/core/src/Foundation/ErrorHandling/JsonApiRenderer.php create mode 100644 framework/core/src/Foundation/ErrorHandling/LogReporter.php create mode 100644 framework/core/src/Foundation/ErrorHandling/Registry.php create mode 100644 framework/core/src/Foundation/ErrorHandling/Reporter.php create mode 100644 framework/core/src/Foundation/ErrorHandling/ViewRenderer.php create mode 100644 framework/core/src/Foundation/ErrorHandling/WhoopsRenderer.php create mode 100644 framework/core/src/Foundation/KnownError.php create mode 100644 framework/core/src/Http/Middleware/HandleErrors.php diff --git a/framework/core/src/Foundation/ErrorHandling/Formatter.php b/framework/core/src/Foundation/ErrorHandling/Formatter.php new file mode 100644 index 000000000..ec7eb4756 --- /dev/null +++ b/framework/core/src/Foundation/ErrorHandling/Formatter.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation\ErrorHandling; + +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface as Request; + +interface Formatter +{ + public function format(HandledError $error, Request $request): Response; +} diff --git a/framework/core/src/Foundation/ErrorHandling/HandledError.php b/framework/core/src/Foundation/ErrorHandling/HandledError.php new file mode 100644 index 000000000..738ba6da5 --- /dev/null +++ b/framework/core/src/Foundation/ErrorHandling/HandledError.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation\ErrorHandling; + +use Throwable; + +class HandledError +{ + private $error; + private $type; + private $statusCode; + + private $details = []; + + public static function unknown(Throwable $error) + { + return new static($error, 'unknown', 500); + } + + public function __construct(Throwable $error, $type, $statusCode) + { + $this->error = $error; + $this->type = $type; + $this->statusCode = $statusCode; + } + + public function withDetails(array $details): self + { + $this->details = $details; + + return $this; + } + + public function getError(): Throwable + { + return $this->error; + } + + public function getType(): string + { + return $this->type; + } + + public function getStatusCode(): int + { + return $this->statusCode; + } + + public function shouldBeReported(): bool + { + return $this->type === 'unknown'; + } + + public function getDetails(): array + { + return $this->details; + } +} diff --git a/framework/core/src/Foundation/ErrorHandling/JsonApiRenderer.php b/framework/core/src/Foundation/ErrorHandling/JsonApiRenderer.php new file mode 100644 index 000000000..948f8ae8f --- /dev/null +++ b/framework/core/src/Foundation/ErrorHandling/JsonApiRenderer.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation\ErrorHandling; + +use Flarum\Api\JsonApiResponse; +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface as Request; +use Tobscure\JsonApi\Document; + +class JsonApiRenderer implements Formatter +{ + public function format(HandledError $error, Request $request): Response + { + $document = new Document; + $document->setErrors([ + [ + 'status' => (string) $error->getStatusCode(), + 'code' => $error->getType(), + ], + ]); + + return new JsonApiResponse($document, $error->getStatusCode()); + } +} diff --git a/framework/core/src/Foundation/ErrorHandling/LogReporter.php b/framework/core/src/Foundation/ErrorHandling/LogReporter.php new file mode 100644 index 000000000..edff300d1 --- /dev/null +++ b/framework/core/src/Foundation/ErrorHandling/LogReporter.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation\ErrorHandling; + +use Psr\Log\LoggerInterface; + +class LogReporter implements Reporter +{ + /** + * @var LoggerInterface + */ + protected $logger; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + public function report(HandledError $error) + { + $this->logger->error($error->getError()); + } +} diff --git a/framework/core/src/Foundation/ErrorHandling/Registry.php b/framework/core/src/Foundation/ErrorHandling/Registry.php new file mode 100644 index 000000000..45ed5c4ec --- /dev/null +++ b/framework/core/src/Foundation/ErrorHandling/Registry.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation\ErrorHandling; + +use Flarum\Foundation\KnownError; +use Throwable; + +class Registry +{ + private $statusMap; + private $classMap; + private $handlerMap; + + public function __construct(array $statusMap, array $classMap, array $handlerMap) + { + $this->statusMap = $statusMap; + $this->classMap = $classMap; + $this->handlerMap = $handlerMap; + } + + public function handle(Throwable $error): HandledError + { + return $this->handleKnownTypes($error) + ?? $this->handleCustomTypes($error) + ?? HandledError::unknown($error); + } + + private function handleKnownTypes(Throwable $error): ?HandledError + { + $errorType = null; + + if ($error instanceof KnownError) { + $errorType = $error->getType(); + } else { + $errorClass = get_class($error); + if (isset($this->classMap[$errorClass])) { + $errorType = $this->classMap[$errorClass]; + } + } + + if ($errorType) { + return new HandledError( + $error, + $errorType, + $this->statusMap[$errorType] ?? 500 + ); + } + + return null; + } + + private function handleCustomTypes(Throwable $error): ?HandledError + { + $errorClass = get_class($error); + + if (isset($this->handlerMap[$errorClass])) { + $handler = new $this->handlerMap[$errorClass]; + + return $handler->handle($error); + } + + return null; + } +} diff --git a/framework/core/src/Foundation/ErrorHandling/Reporter.php b/framework/core/src/Foundation/ErrorHandling/Reporter.php new file mode 100644 index 000000000..2e52d289b --- /dev/null +++ b/framework/core/src/Foundation/ErrorHandling/Reporter.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation\ErrorHandling; + +interface Reporter +{ + public function report(HandledError $error); +} diff --git a/framework/core/src/Foundation/ErrorHandling/ViewRenderer.php b/framework/core/src/Foundation/ErrorHandling/ViewRenderer.php new file mode 100644 index 000000000..4ac50ffd7 --- /dev/null +++ b/framework/core/src/Foundation/ErrorHandling/ViewRenderer.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation\ErrorHandling; + +use Flarum\Settings\SettingsRepositoryInterface; +use Illuminate\Contracts\View\Factory as ViewFactory; +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface as Request; +use Symfony\Component\Translation\TranslatorInterface; +use Zend\Diactoros\Response\HtmlResponse; + +class ViewRenderer implements Formatter +{ + /** + * @var ViewFactory + */ + protected $view; + + /** + * @var TranslatorInterface + */ + protected $translator; + + /** + * @var SettingsRepositoryInterface + */ + protected $settings; + + public function __construct(ViewFactory $view, TranslatorInterface $translator, SettingsRepositoryInterface $settings) + { + $this->view = $view; + $this->translator = $translator; + $this->settings = $settings; + } + + public function format(HandledError $error, Request $request): Response + { + $view = $this->view->make($this->determineView($error)) + ->with('error', $error->getError()) + ->with('message', $this->getMessage($error)); + + return new HtmlResponse($view->render(), $error->getStatusCode()); + } + + private function determineView(HandledError $error): string + { + $view = [ + 'route_not_found' => '404', + 'csrf_token_mismatch' => '419', + ][$error->getType()] ?? 'default'; + + return "flarum.forum::error.$view"; + } + + private function getMessage(HandledError $error) + { + return $this->getTranslationIfExists($error->getStatusCode()) + ?? $this->getTranslationIfExists('unknown') + ?? 'An error occurred while trying to load this page.'; + } + + private function getTranslationIfExists(string $errorType) + { + $key = "core.views.error.${errorType}_message"; + $translation = $this->translator->trans($key, ['{forum}' => $this->settings->get('forum_title')]); + + return $translation === $key ? null : $translation; + } +} diff --git a/framework/core/src/Foundation/ErrorHandling/WhoopsRenderer.php b/framework/core/src/Foundation/ErrorHandling/WhoopsRenderer.php new file mode 100644 index 000000000..7be647558 --- /dev/null +++ b/framework/core/src/Foundation/ErrorHandling/WhoopsRenderer.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation\ErrorHandling; + +use Franzl\Middleware\Whoops\WhoopsRunner; +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface as Request; + +class WhoopsRenderer implements Formatter +{ + public function format(HandledError $error, Request $request): Response + { + return WhoopsRunner::handle($error->getError(), $request) + ->withStatus($error->getStatusCode()); + } +} diff --git a/framework/core/src/Foundation/KnownError.php b/framework/core/src/Foundation/KnownError.php new file mode 100644 index 000000000..bb7e4591a --- /dev/null +++ b/framework/core/src/Foundation/KnownError.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation; + +interface KnownError +{ + public function getType(): string; +} diff --git a/framework/core/src/Http/Middleware/HandleErrors.php b/framework/core/src/Http/Middleware/HandleErrors.php new file mode 100644 index 000000000..ff077f105 --- /dev/null +++ b/framework/core/src/Http/Middleware/HandleErrors.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Http\Middleware; + +use Flarum\Foundation\ErrorHandling\Formatter; +use Flarum\Foundation\ErrorHandling\Registry; +use Flarum\Foundation\ErrorHandling\Reporter; +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface as Request; +use Psr\Http\Server\MiddlewareInterface as Middleware; +use Psr\Http\Server\RequestHandlerInterface as Handler; +use Throwable; + +class HandleErrors implements Middleware +{ + /** + * @var Registry + */ + protected $registry; + + /** + * @var Formatter + */ + protected $formatter; + + /** + * @var Reporter + */ + protected $reporter; + + public function __construct(Registry $registry, Formatter $formatter, Reporter $reporter) + { + $this->registry = $registry; + $this->formatter = $formatter; + $this->reporter = $reporter; + } + + /** + * Catch all errors that happen during further middleware execution. + */ + public function process(Request $request, Handler $handler): Response + { + try { + return $handler->handle($request); + } catch (Throwable $e) { + $error = $this->registry->handle($e); + + if ($error->shouldBeReported()) { + $this->reporter->report($error); + } + + return $this->formatter->format($error, $request); + } + } +} From 13377100fb7d272d43c4771ef6510f04b1714adc Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Fri, 9 Aug 2019 20:42:03 +0200 Subject: [PATCH 2/7] Make existing extensions compatible with new stack --- .../core/src/Api/Exception/InvalidAccessTokenException.php | 7 ++++++- framework/core/src/Http/Exception/ForbiddenException.php | 7 ++++--- .../core/src/Http/Exception/MethodNotAllowedException.php | 7 ++++--- .../core/src/Http/Exception/RouteNotFoundException.php | 7 ++++--- .../core/src/Http/Exception/TokenMismatchException.php | 7 ++++--- framework/core/src/Post/Exception/FloodingException.php | 7 ++++++- .../User/Exception/InvalidConfirmationTokenException.php | 7 ++++++- .../core/src/User/Exception/PermissionDeniedException.php | 7 ++++--- 8 files changed, 38 insertions(+), 18 deletions(-) diff --git a/framework/core/src/Api/Exception/InvalidAccessTokenException.php b/framework/core/src/Api/Exception/InvalidAccessTokenException.php index dafd75a0b..c79e99e03 100644 --- a/framework/core/src/Api/Exception/InvalidAccessTokenException.php +++ b/framework/core/src/Api/Exception/InvalidAccessTokenException.php @@ -12,7 +12,12 @@ namespace Flarum\Api\Exception; use Exception; +use Flarum\Foundation\KnownError; -class InvalidAccessTokenException extends Exception +class InvalidAccessTokenException extends Exception implements KnownError { + public function getType(): string + { + return 'invalid_access_token'; + } } diff --git a/framework/core/src/Http/Exception/ForbiddenException.php b/framework/core/src/Http/Exception/ForbiddenException.php index 55cc50557..903ae98cb 100644 --- a/framework/core/src/Http/Exception/ForbiddenException.php +++ b/framework/core/src/Http/Exception/ForbiddenException.php @@ -12,11 +12,12 @@ namespace Flarum\Http\Exception; use Exception; +use Flarum\Foundation\KnownError; -class ForbiddenException extends Exception +class ForbiddenException extends Exception implements KnownError { - public function __construct($message = null, $code = 403, Exception $previous = null) + public function getType(): string { - parent::__construct($message, $code, $previous); + return 'forbidden'; } } diff --git a/framework/core/src/Http/Exception/MethodNotAllowedException.php b/framework/core/src/Http/Exception/MethodNotAllowedException.php index 0b7cd3f8e..62e0ae07f 100644 --- a/framework/core/src/Http/Exception/MethodNotAllowedException.php +++ b/framework/core/src/Http/Exception/MethodNotAllowedException.php @@ -12,11 +12,12 @@ namespace Flarum\Http\Exception; use Exception; +use Flarum\Foundation\KnownError; -class MethodNotAllowedException extends Exception +class MethodNotAllowedException extends Exception implements KnownError { - public function __construct($message = null, $code = 405, Exception $previous = null) + public function getType(): string { - parent::__construct($message, $code, $previous); + return 'method_not_allowed'; } } diff --git a/framework/core/src/Http/Exception/RouteNotFoundException.php b/framework/core/src/Http/Exception/RouteNotFoundException.php index 6e7b6aef9..820da488e 100644 --- a/framework/core/src/Http/Exception/RouteNotFoundException.php +++ b/framework/core/src/Http/Exception/RouteNotFoundException.php @@ -12,11 +12,12 @@ namespace Flarum\Http\Exception; use Exception; +use Flarum\Foundation\KnownError; -class RouteNotFoundException extends Exception +class RouteNotFoundException extends Exception implements KnownError { - public function __construct($message = null, $code = 404, Exception $previous = null) + public function getType(): string { - parent::__construct($message, $code, $previous); + return 'route_not_found'; } } diff --git a/framework/core/src/Http/Exception/TokenMismatchException.php b/framework/core/src/Http/Exception/TokenMismatchException.php index 87871937a..3c2afd209 100644 --- a/framework/core/src/Http/Exception/TokenMismatchException.php +++ b/framework/core/src/Http/Exception/TokenMismatchException.php @@ -12,11 +12,12 @@ namespace Flarum\Http\Exception; use Exception; +use Flarum\Foundation\KnownError; -class TokenMismatchException extends Exception +class TokenMismatchException extends Exception implements KnownError { - public function __construct($message = null, $code = 419, Exception $previous = null) + public function getType(): string { - parent::__construct($message, $code, $previous); + return 'csrf_token_mismatch'; } } diff --git a/framework/core/src/Post/Exception/FloodingException.php b/framework/core/src/Post/Exception/FloodingException.php index f9221b934..2f147c3e5 100644 --- a/framework/core/src/Post/Exception/FloodingException.php +++ b/framework/core/src/Post/Exception/FloodingException.php @@ -12,7 +12,12 @@ namespace Flarum\Post\Exception; use Exception; +use Flarum\Foundation\KnownError; -class FloodingException extends Exception +class FloodingException extends Exception implements KnownError { + public function getType(): string + { + return 'too_many_requests'; + } } diff --git a/framework/core/src/User/Exception/InvalidConfirmationTokenException.php b/framework/core/src/User/Exception/InvalidConfirmationTokenException.php index c8e9240b2..7ee294802 100644 --- a/framework/core/src/User/Exception/InvalidConfirmationTokenException.php +++ b/framework/core/src/User/Exception/InvalidConfirmationTokenException.php @@ -12,7 +12,12 @@ namespace Flarum\User\Exception; use Exception; +use Flarum\Foundation\KnownError; -class InvalidConfirmationTokenException extends Exception +class InvalidConfirmationTokenException extends Exception implements KnownError { + public function getType(): string + { + return 'invalid_confirmation_token'; + } } diff --git a/framework/core/src/User/Exception/PermissionDeniedException.php b/framework/core/src/User/Exception/PermissionDeniedException.php index 1bbeb766c..02814ac25 100644 --- a/framework/core/src/User/Exception/PermissionDeniedException.php +++ b/framework/core/src/User/Exception/PermissionDeniedException.php @@ -12,11 +12,12 @@ namespace Flarum\User\Exception; use Exception; +use Flarum\Foundation\KnownError; -class PermissionDeniedException extends Exception +class PermissionDeniedException extends Exception implements KnownError { - public function __construct($message = null, $code = 403, Exception $previous = null) + public function getType(): string { - parent::__construct($message, $code, $previous); + return 'permission_denied'; } } From 817e54abe02000a470cac3af462f0f13e218d69a Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Fri, 9 Aug 2019 20:38:51 +0200 Subject: [PATCH 3/7] Wire up new error handling stack --- .../IlluminateValidationExceptionHandler.php | 37 +++++++++ .../ValidationExceptionHandler.php | 38 ++++++++++ .../src/Foundation/ErrorServiceProvider.php | 76 +++++++++++++++++++ .../core/src/Foundation/InstalledSite.php | 1 + .../core/src/Foundation/UninstalledSite.php | 1 + ...luminateValidationExceptionHandlerTest.php | 53 +++++++++++++ .../ValidationExceptionHandlerTest.php | 47 ++++++++++++ 7 files changed, 253 insertions(+) create mode 100644 framework/core/src/Foundation/ErrorHandling/ExceptionHandler/IlluminateValidationExceptionHandler.php create mode 100644 framework/core/src/Foundation/ErrorHandling/ExceptionHandler/ValidationExceptionHandler.php create mode 100644 framework/core/src/Foundation/ErrorServiceProvider.php create mode 100644 framework/core/tests/unit/Foundation/ErrorHandling/ExceptionHandler/IlluminateValidationExceptionHandlerTest.php create mode 100644 framework/core/tests/unit/Foundation/ErrorHandling/ExceptionHandler/ValidationExceptionHandlerTest.php diff --git a/framework/core/src/Foundation/ErrorHandling/ExceptionHandler/IlluminateValidationExceptionHandler.php b/framework/core/src/Foundation/ErrorHandling/ExceptionHandler/IlluminateValidationExceptionHandler.php new file mode 100644 index 000000000..922a8cdd4 --- /dev/null +++ b/framework/core/src/Foundation/ErrorHandling/ExceptionHandler/IlluminateValidationExceptionHandler.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation\ErrorHandling\ExceptionHandler; + +use Flarum\Foundation\ErrorHandling\HandledError; +use Illuminate\Validation\ValidationException; + +class IlluminateValidationExceptionHandler +{ + public function handle(ValidationException $e): HandledError + { + return (new HandledError( + $e, 'validation_error', 422 + ))->withDetails($this->errorDetails($e)); + } + + protected function errorDetails(ValidationException $e): array + { + $errors = $e->errors(); + + return array_map(function ($field, $messages) { + return [ + 'detail' => implode("\n", $messages), + 'source' => ['pointer' => "/data/attributes/$field"] + ]; + }, array_keys($errors), $errors); + } +} diff --git a/framework/core/src/Foundation/ErrorHandling/ExceptionHandler/ValidationExceptionHandler.php b/framework/core/src/Foundation/ErrorHandling/ExceptionHandler/ValidationExceptionHandler.php new file mode 100644 index 000000000..224143766 --- /dev/null +++ b/framework/core/src/Foundation/ErrorHandling/ExceptionHandler/ValidationExceptionHandler.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation\ErrorHandling\ExceptionHandler; + +use Flarum\Foundation\ErrorHandling\HandledError; +use Flarum\Foundation\ValidationException; + +class ValidationExceptionHandler +{ + public function handle(ValidationException $e) + { + return (new HandledError( + $e, 'validation_error', 422 + ))->withDetails(array_merge( + $this->buildDetails($e->getAttributes(), '/data/attributes'), + $this->buildDetails($e->getRelationships(), '/data/relationships') + )); + } + + private function buildDetails(array $messages, $pointer): array + { + return array_map(function ($path, $detail) use ($pointer) { + return [ + 'detail' => $detail, + 'source' => ['pointer' => $pointer.'/'.$path] + ]; + }, array_keys($messages), $messages); + } +} diff --git a/framework/core/src/Foundation/ErrorServiceProvider.php b/framework/core/src/Foundation/ErrorServiceProvider.php new file mode 100644 index 000000000..2a1deba21 --- /dev/null +++ b/framework/core/src/Foundation/ErrorServiceProvider.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Foundation; + +use Flarum\Foundation\ErrorHandling\ExceptionHandler; +use Flarum\Foundation\ErrorHandling\LogReporter; +use Flarum\Foundation\ErrorHandling\Registry; +use Flarum\Foundation\ErrorHandling\Reporter; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Validation\ValidationException as IlluminateValidationException; +use Tobscure\JsonApi\Exception\InvalidParameterException; + +class ErrorServiceProvider extends AbstractServiceProvider +{ + public function register() + { + $this->app->singleton('flarum.error.statuses', function () { + return [ + // 400 Bad Request + 'csrf_token_mismatch' => 400, + 'invalid_parameter' => 400, + + // 401 Unauthorized + 'invalid_access_token' => 401, + + // 403 Forbidden + 'forbidden' => 403, + 'invalid_confirmation_token' => 403, + 'permission_denied' => 403, + + // 404 Not Found + 'model_not_found' => 404, + 'route_not_found' => 404, + + // 405 Method Not Allowed + 'method_not_allowed' => 405, + + // 429 Too Many Requests + 'too_many_requests' => 429, + ]; + }); + + $this->app->singleton('flarum.error.classes', function () { + return [ + InvalidParameterException::class => 'invalid_parameter', + ModelNotFoundException::class => 'model_not_found', + ]; + }); + + $this->app->singleton('flarum.error.handlers', function () { + return [ + IlluminateValidationException::class => ExceptionHandler\IlluminateValidationExceptionHandler::class, + ValidationException::class => ExceptionHandler\ValidationExceptionHandler::class, + ]; + }); + + $this->app->singleton(Registry::class, function () { + return new Registry( + $this->app->make('flarum.error.statuses'), + $this->app->make('flarum.error.classes'), + $this->app->make('flarum.error.handlers') + ); + }); + + $this->app->singleton(Reporter::class, LogReporter::class); + } +} diff --git a/framework/core/src/Foundation/InstalledSite.php b/framework/core/src/Foundation/InstalledSite.php index 614cb1119..ffdfcc3d7 100644 --- a/framework/core/src/Foundation/InstalledSite.php +++ b/framework/core/src/Foundation/InstalledSite.php @@ -117,6 +117,7 @@ class InstalledSite implements SiteInterface $laravel->register(DatabaseServiceProvider::class); $laravel->register(DiscussionServiceProvider::class); $laravel->register(ExtensionServiceProvider::class); + $laravel->register(ErrorServiceProvider::class); $laravel->register(FilesystemServiceProvider::class); $laravel->register(FormatterServiceProvider::class); $laravel->register(ForumServiceProvider::class); diff --git a/framework/core/src/Foundation/UninstalledSite.php b/framework/core/src/Foundation/UninstalledSite.php index d363959b2..92aca5142 100644 --- a/framework/core/src/Foundation/UninstalledSite.php +++ b/framework/core/src/Foundation/UninstalledSite.php @@ -69,6 +69,7 @@ class UninstalledSite implements SiteInterface $this->registerLogger($laravel); + $laravel->register(ErrorServiceProvider::class); $laravel->register(LocaleServiceProvider::class); $laravel->register(FilesystemServiceProvider::class); $laravel->register(SessionServiceProvider::class); diff --git a/framework/core/tests/unit/Foundation/ErrorHandling/ExceptionHandler/IlluminateValidationExceptionHandlerTest.php b/framework/core/tests/unit/Foundation/ErrorHandling/ExceptionHandler/IlluminateValidationExceptionHandlerTest.php new file mode 100644 index 000000000..028fa5e33 --- /dev/null +++ b/framework/core/tests/unit/Foundation/ErrorHandling/ExceptionHandler/IlluminateValidationExceptionHandlerTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Tests\unit\Foundation\ErrorHandling\ExceptionHandler; + +use Flarum\Foundation\ErrorHandling\ExceptionHandler\IlluminateValidationExceptionHandler; +use Illuminate\Translation\ArrayLoader; +use Illuminate\Translation\Translator; +use Illuminate\Validation\Factory; +use Illuminate\Validation\ValidationException; +use PHPUnit\Framework\TestCase; + +class IlluminateValidationExceptionHandlerTest extends TestCase +{ + private $handler; + + public function setUp() + { + $this->handler = new IlluminateValidationExceptionHandler; + } + + public function test_it_creates_the_desired_output() + { + $exception = new ValidationException($this->makeValidator(['foo' => ''], ['foo' => 'required'])); + + $error = $this->handler->handle($exception); + + $this->assertEquals(422, $error->getStatusCode()); + $this->assertEquals('validation_error', $error->getType()); + $this->assertEquals([ + [ + 'detail' => 'validation.required', + 'source' => ['pointer' => '/data/attributes/foo'] + ] + ], $error->getDetails()); + } + + private function makeValidator($data = [], $rules = []) + { + $translator = new Translator(new ArrayLoader(), 'en'); + $factory = new Factory($translator); + + return $factory->make($data, $rules); + } +} diff --git a/framework/core/tests/unit/Foundation/ErrorHandling/ExceptionHandler/ValidationExceptionHandlerTest.php b/framework/core/tests/unit/Foundation/ErrorHandling/ExceptionHandler/ValidationExceptionHandlerTest.php new file mode 100644 index 000000000..32062249d --- /dev/null +++ b/framework/core/tests/unit/Foundation/ErrorHandling/ExceptionHandler/ValidationExceptionHandlerTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Tests\unit\Foundation\ErrorHandling\ExceptionHandler; + +use Flarum\Foundation\ErrorHandling\ExceptionHandler\ValidationExceptionHandler; +use Flarum\Foundation\ValidationException; +use PHPUnit\Framework\TestCase; + +class ValidationExceptionHandlerTest extends TestCase +{ + private $handler; + + public function setUp() + { + $this->handler = new ValidationExceptionHandler; + } + + public function test_managing_exceptions() + { + $error = $this->handler->handle(new ValidationException( + ['foo' => 'Attribute error'], + ['bar' => 'Relationship error'] + )); + + $this->assertEquals(422, $error->getStatusCode()); + $this->assertEquals('validation_error', $error->getType()); + $this->assertEquals([ + [ + 'detail' => 'Attribute error', + 'source' => ['pointer' => '/data/attributes/foo'] + ], + [ + 'detail' => 'Relationship error', + 'source' => ['pointer' => '/data/relationships/bar'] + ] + ], $error->getDetails()); + } +} From 410028dae623d15fb4e656b9b879a61d81d59b6f Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Fri, 9 Aug 2019 20:47:37 +0200 Subject: [PATCH 4/7] Use new error handler middleware --- .../core/src/Admin/AdminServiceProvider.php | 14 +++++---- framework/core/src/Api/ApiServiceProvider.php | 30 +++++-------------- .../core/src/Forum/ForumServiceProvider.php | 14 +++++---- framework/core/src/Install/Installer.php | 11 +++++-- 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/framework/core/src/Admin/AdminServiceProvider.php b/framework/core/src/Admin/AdminServiceProvider.php index 493464ba9..66ad29ec2 100644 --- a/framework/core/src/Admin/AdminServiceProvider.php +++ b/framework/core/src/Admin/AdminServiceProvider.php @@ -16,6 +16,10 @@ use Flarum\Extension\Event\Disabled; use Flarum\Extension\Event\Enabled; use Flarum\Foundation\AbstractServiceProvider; use Flarum\Foundation\Application; +use Flarum\Foundation\ErrorHandling\Registry; +use Flarum\Foundation\ErrorHandling\Reporter; +use Flarum\Foundation\ErrorHandling\ViewRenderer; +use Flarum\Foundation\ErrorHandling\WhoopsRenderer; use Flarum\Foundation\Event\ClearingCache; use Flarum\Frontend\AddLocaleAssets; use Flarum\Frontend\AddTranslations; @@ -51,11 +55,11 @@ class AdminServiceProvider extends AbstractServiceProvider $pipe = new MiddlewarePipe; // All requests should first be piped through our global error handler - if ($app->inDebugMode()) { - $pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithWhoops::class)); - } else { - $pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithView::class)); - } + $pipe->pipe(new HttpMiddleware\HandleErrors( + $app->make(Registry::class), + $app->inDebugMode() ? $app->make(WhoopsRenderer::class) : $app->make(ViewRenderer::class), + $app->make(Reporter::class) + )); $pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class)); $pipe->pipe($app->make(HttpMiddleware\StartSession::class)); diff --git a/framework/core/src/Api/ApiServiceProvider.php b/framework/core/src/Api/ApiServiceProvider.php index dd8eb6cd9..49501d84e 100644 --- a/framework/core/src/Api/ApiServiceProvider.php +++ b/framework/core/src/Api/ApiServiceProvider.php @@ -20,12 +20,13 @@ use Flarum\Event\ConfigureMiddleware; use Flarum\Event\ConfigureNotificationTypes; use Flarum\Foundation\AbstractServiceProvider; use Flarum\Foundation\Application; +use Flarum\Foundation\ErrorHandling\JsonApiRenderer; +use Flarum\Foundation\ErrorHandling\Registry; +use Flarum\Foundation\ErrorHandling\Reporter; use Flarum\Http\Middleware as HttpMiddleware; use Flarum\Http\RouteCollection; use Flarum\Http\RouteHandlerFactory; use Flarum\Http\UrlGenerator; -use Tobscure\JsonApi\ErrorHandler; -use Tobscure\JsonApi\Exception\Handler\InvalidParameterExceptionHandler; use Zend\Stratigility\MiddlewarePipe; class ApiServiceProvider extends AbstractServiceProvider @@ -49,7 +50,11 @@ class ApiServiceProvider extends AbstractServiceProvider $this->app->singleton('flarum.api.middleware', function (Application $app) { $pipe = new MiddlewarePipe; - $pipe->pipe($app->make(Middleware\HandleErrors::class)); + $pipe->pipe(new HttpMiddleware\HandleErrors( + $app->make(Registry::class), + $app->make(JsonApiRenderer::class), + $app->make(Reporter::class) + )); $pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class)); $pipe->pipe($app->make(Middleware\FakeHttpMethods::class)); @@ -68,25 +73,6 @@ class ApiServiceProvider extends AbstractServiceProvider $this->app->afterResolving('flarum.api.middleware', function (MiddlewarePipe $pipe) { $pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.api.routes'))); }); - - $this->app->singleton(ErrorHandler::class, function () { - $handler = new ErrorHandler; - - $handler->registerHandler(new ExceptionHandler\FloodingExceptionHandler); - $handler->registerHandler(new ExceptionHandler\IlluminateValidationExceptionHandler); - $handler->registerHandler(new ExceptionHandler\InvalidAccessTokenExceptionHandler); - $handler->registerHandler(new ExceptionHandler\InvalidConfirmationTokenExceptionHandler); - $handler->registerHandler(new ExceptionHandler\MethodNotAllowedExceptionHandler); - $handler->registerHandler(new ExceptionHandler\ModelNotFoundExceptionHandler); - $handler->registerHandler(new ExceptionHandler\PermissionDeniedExceptionHandler); - $handler->registerHandler(new ExceptionHandler\RouteNotFoundExceptionHandler); - $handler->registerHandler(new ExceptionHandler\TokenMismatchExceptionHandler); - $handler->registerHandler(new ExceptionHandler\ValidationExceptionHandler); - $handler->registerHandler(new InvalidParameterExceptionHandler); - $handler->registerHandler(new ExceptionHandler\FallbackExceptionHandler($this->app->inDebugMode(), $this->app->make('log'))); - - return $handler; - }); } /** diff --git a/framework/core/src/Forum/ForumServiceProvider.php b/framework/core/src/Forum/ForumServiceProvider.php index 969edb06d..92614f0fb 100644 --- a/framework/core/src/Forum/ForumServiceProvider.php +++ b/framework/core/src/Forum/ForumServiceProvider.php @@ -18,6 +18,10 @@ use Flarum\Extension\Event\Enabled; use Flarum\Formatter\Formatter; use Flarum\Foundation\AbstractServiceProvider; use Flarum\Foundation\Application; +use Flarum\Foundation\ErrorHandling\Registry; +use Flarum\Foundation\ErrorHandling\Reporter; +use Flarum\Foundation\ErrorHandling\ViewRenderer; +use Flarum\Foundation\ErrorHandling\WhoopsRenderer; use Flarum\Foundation\Event\ClearingCache; use Flarum\Frontend\AddLocaleAssets; use Flarum\Frontend\AddTranslations; @@ -61,11 +65,11 @@ class ForumServiceProvider extends AbstractServiceProvider $pipe = new MiddlewarePipe; // All requests should first be piped through our global error handler - if ($app->inDebugMode()) { - $pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithWhoops::class)); - } else { - $pipe->pipe($app->make(HttpMiddleware\HandleErrorsWithView::class)); - } + $pipe->pipe(new HttpMiddleware\HandleErrors( + $app->make(Registry::class), + $app->inDebugMode() ? $app->make(WhoopsRenderer::class) : $app->make(ViewRenderer::class), + $app->make(Reporter::class) + )); $pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class)); $pipe->pipe($app->make(HttpMiddleware\CollectGarbage::class)); diff --git a/framework/core/src/Install/Installer.php b/framework/core/src/Install/Installer.php index 514c8e364..72bd82905 100644 --- a/framework/core/src/Install/Installer.php +++ b/framework/core/src/Install/Installer.php @@ -12,8 +12,11 @@ namespace Flarum\Install; use Flarum\Foundation\AppInterface; +use Flarum\Foundation\ErrorHandling\Registry; +use Flarum\Foundation\ErrorHandling\Reporter; +use Flarum\Foundation\ErrorHandling\WhoopsRenderer; use Flarum\Http\Middleware\DispatchRoute; -use Flarum\Http\Middleware\HandleErrorsWithWhoops; +use Flarum\Http\Middleware\HandleErrors; use Flarum\Http\Middleware\StartSession; use Flarum\Install\Console\InstallCommand; use Illuminate\Contracts\Container\Container; @@ -37,7 +40,11 @@ class Installer implements AppInterface public function getRequestHandler() { $pipe = new MiddlewarePipe; - $pipe->pipe($this->container->make(HandleErrorsWithWhoops::class)); + $pipe->pipe(new HandleErrors( + $this->container->make(Registry::class), + $this->container->make(WhoopsRenderer::class), + $this->container->make(Reporter::class) + )); $pipe->pipe($this->container->make(StartSession::class)); $pipe->pipe( new DispatchRoute($this->container->make('flarum.install.routes')) From 8e0cd27f5417433b3375405978460f0f59678160 Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Fri, 9 Aug 2019 23:57:33 +0200 Subject: [PATCH 5/7] API Client: Use new error handling mechanism --- framework/core/src/Api/Client.php | 32 ++++++++----------- framework/core/src/Frontend/Frontend.php | 2 -- .../api/Auth/AuthenticateWithApiKeyTest.php | 6 ++-- .../api/Controller/ApiControllerTestCase.php | 2 -- .../CreateDiscussionControllerTest.php | 13 +++----- .../Controller/CreateGroupControllerTest.php | 11 ++----- .../Controller/CreateUserControllerTest.php | 16 +++------- .../ListNotificationsControllerTest.php | 5 ++- .../Controller/ListUsersControllerTest.php | 5 ++- .../ShowDiscussionControllerTest.php | 9 ++---- 10 files changed, 33 insertions(+), 68 deletions(-) diff --git a/framework/core/src/Api/Client.php b/framework/core/src/Api/Client.php index beaed329d..684a00a55 100644 --- a/framework/core/src/Api/Client.php +++ b/framework/core/src/Api/Client.php @@ -12,11 +12,14 @@ namespace Flarum\Api; use Exception; +use Flarum\Foundation\ErrorHandling\JsonApiRenderer; +use Flarum\Foundation\ErrorHandling\Registry; use Flarum\User\User; use Illuminate\Contracts\Container\Container; use InvalidArgumentException; use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\RequestHandlerInterface; +use Throwable; use Zend\Diactoros\ServerRequestFactory; class Client @@ -27,18 +30,18 @@ class Client protected $container; /** - * @var ErrorHandler + * @var Registry */ - protected $errorHandler; + protected $registry; /** * @param Container $container - * @param ErrorHandler $errorHandler + * @param Registry $registry */ - public function __construct(Container $container, ErrorHandler $errorHandler = null) + public function __construct(Container $container, Registry $registry) { $this->container = $container; - $this->errorHandler = $errorHandler; + $this->registry = $registry; } /** @@ -69,23 +72,14 @@ class Client try { return $controller->handle($request); - } catch (Exception $e) { - if (! $this->errorHandler) { + } catch (Throwable $e) { + $error = $this->registry->handle($e); + + if ($error->shouldBeReported()) { throw $e; } - return $this->errorHandler->handle($e); + return (new JsonApiRenderer)->format($error, $request); } } - - /** - * @param ErrorHandler $errorHandler - * @return Client - */ - public function setErrorHandler(?ErrorHandler $errorHandler): self - { - $this->errorHandler = $errorHandler; - - return $this; - } } diff --git a/framework/core/src/Frontend/Frontend.php b/framework/core/src/Frontend/Frontend.php index 791d1feb2..acf0564cc 100644 --- a/framework/core/src/Frontend/Frontend.php +++ b/framework/core/src/Frontend/Frontend.php @@ -70,8 +70,6 @@ class Frontend { $actor = $request->getAttribute('actor'); - $this->api->setErrorHandler(null); - return $this->getResponseBody( $this->api->send(ShowForumController::class, $actor) ); diff --git a/framework/core/tests/integration/api/Auth/AuthenticateWithApiKeyTest.php b/framework/core/tests/integration/api/Auth/AuthenticateWithApiKeyTest.php index d4a5fadde..1ba3ce5a1 100644 --- a/framework/core/tests/integration/api/Auth/AuthenticateWithApiKeyTest.php +++ b/framework/core/tests/integration/api/Auth/AuthenticateWithApiKeyTest.php @@ -17,7 +17,6 @@ use Flarum\Api\Client; use Flarum\Api\Controller\CreateGroupController; use Flarum\Tests\integration\RetrievesAuthorizedUsers; use Flarum\Tests\integration\TestCase; -use Flarum\User\Exception\PermissionDeniedException; use Flarum\User\Guest; use Flarum\User\User; use Illuminate\Support\Str; @@ -63,11 +62,10 @@ class AuthenticateWithApiKeyTest extends TestCase { /** @var Client $api */ $api = $this->app()->getContainer()->make(Client::class); - $api->setErrorHandler(null); - $this->expectException(PermissionDeniedException::class); + $response = $api->send(CreateGroupController::class, new Guest); - $api->send(CreateGroupController::class, new Guest); + $this->assertEquals(403, $response->getStatusCode()); } /** diff --git a/framework/core/tests/integration/api/Controller/ApiControllerTestCase.php b/framework/core/tests/integration/api/Controller/ApiControllerTestCase.php index 4f0b1296f..05085b801 100644 --- a/framework/core/tests/integration/api/Controller/ApiControllerTestCase.php +++ b/framework/core/tests/integration/api/Controller/ApiControllerTestCase.php @@ -53,8 +53,6 @@ abstract class ApiControllerTestCase extends TestCase /** @var Client $api */ $api = $this->app()->getContainer()->make(Client::class); - $api->setErrorHandler(null); - return $api->send($controller, $actor ?? new Guest, $queryParams, $body); } } diff --git a/framework/core/tests/integration/api/Controller/CreateDiscussionControllerTest.php b/framework/core/tests/integration/api/Controller/CreateDiscussionControllerTest.php index eec919d94..7e23f319d 100644 --- a/framework/core/tests/integration/api/Controller/CreateDiscussionControllerTest.php +++ b/framework/core/tests/integration/api/Controller/CreateDiscussionControllerTest.php @@ -15,7 +15,6 @@ use Flarum\Api\Controller\CreateDiscussionController; use Flarum\Discussion\Discussion; use Flarum\User\User; use Illuminate\Support\Arr; -use Illuminate\Validation\ValidationException; class CreateDiscussionControllerTest extends ApiControllerTestCase { @@ -72,11 +71,9 @@ class CreateDiscussionControllerTest extends ApiControllerTestCase $this->actor = User::find(1); $data = Arr::except($this->data, 'content'); + $response = $this->callWith($data); - $this->expectException(ValidationException::class); - $this->expectExceptionMessage('The given data was invalid.'); - - $this->callWith($data); + $this->assertEquals(422, $response->getStatusCode()); } /** @@ -87,10 +84,8 @@ class CreateDiscussionControllerTest extends ApiControllerTestCase $this->actor = User::find(1); $data = Arr::except($this->data, 'title'); + $response = $this->callWith($data); - $this->expectException(ValidationException::class); - $this->expectExceptionMessage('The given data was invalid.'); - - $this->callWith($data); + $this->assertEquals(422, $response->getStatusCode()); } } diff --git a/framework/core/tests/integration/api/Controller/CreateGroupControllerTest.php b/framework/core/tests/integration/api/Controller/CreateGroupControllerTest.php index 7ab771fa6..d2edcada1 100644 --- a/framework/core/tests/integration/api/Controller/CreateGroupControllerTest.php +++ b/framework/core/tests/integration/api/Controller/CreateGroupControllerTest.php @@ -13,11 +13,9 @@ namespace Flarum\Tests\integration\api\Controller; use Flarum\Api\Controller\CreateGroupController; use Flarum\Group\Group; -use Flarum\User\Exception\PermissionDeniedException; use Flarum\User\User; use Illuminate\Support\Arr; use Illuminate\Support\Str; -use Illuminate\Validation\ValidationException; class CreateGroupControllerTest extends ApiControllerTestCase { @@ -55,10 +53,7 @@ class CreateGroupControllerTest extends ApiControllerTestCase { $this->actor = User::find(1); - $this->expectException(ValidationException::class); - $this->expectExceptionMessage('The given data was invalid.'); - - $this->callWith(); + $this->assertEquals(422, $this->callWith()->getStatusCode()); } /** @@ -89,8 +84,6 @@ class CreateGroupControllerTest extends ApiControllerTestCase { $this->actor = User::find(2); - $this->expectException(PermissionDeniedException::class); - - $this->callWith($this->data); + $this->assertEquals(403, $this->callWith($this->data)->getStatusCode()); } } diff --git a/framework/core/tests/integration/api/Controller/CreateUserControllerTest.php b/framework/core/tests/integration/api/Controller/CreateUserControllerTest.php index f437ec699..d2caad23e 100644 --- a/framework/core/tests/integration/api/Controller/CreateUserControllerTest.php +++ b/framework/core/tests/integration/api/Controller/CreateUserControllerTest.php @@ -13,10 +13,8 @@ namespace Flarum\Tests\integration\api\Controller; use Flarum\Api\Controller\CreateUserController; use Flarum\Settings\SettingsRepositoryInterface; -use Flarum\User\Exception\PermissionDeniedException; use Flarum\User\User; use Illuminate\Support\Arr; -use Illuminate\Validation\ValidationException; class CreateUserControllerTest extends ApiControllerTestCase { @@ -53,10 +51,9 @@ class CreateUserControllerTest extends ApiControllerTestCase */ public function cannot_create_user_without_data() { - $this->expectException(ValidationException::class); - $this->expectExceptionMessage('The given data was invalid.'); + $response = $this->callWith(); - $this->callWith(); + $this->assertEquals(422, $response->getStatusCode()); } /** @@ -106,12 +103,9 @@ class CreateUserControllerTest extends ApiControllerTestCase $settings = app(SettingsRepositoryInterface::class); $settings->set('allow_sign_up', false); - $this->expectException(PermissionDeniedException::class); + $response = $this->callWith($this->data); + $this->assertEquals(403, $response->getStatusCode()); - try { - $this->callWith($this->data); - } finally { - $settings->set('allow_sign_up', true); - } + $settings->set('allow_sign_up', true); } } diff --git a/framework/core/tests/integration/api/Controller/ListNotificationsControllerTest.php b/framework/core/tests/integration/api/Controller/ListNotificationsControllerTest.php index a7a64924a..9f301607b 100644 --- a/framework/core/tests/integration/api/Controller/ListNotificationsControllerTest.php +++ b/framework/core/tests/integration/api/Controller/ListNotificationsControllerTest.php @@ -12,7 +12,6 @@ namespace Flarum\Tests\integration\api\Controller; use Flarum\Api\Controller\ListNotificationsController; -use Flarum\User\Exception\PermissionDeniedException; use Flarum\User\User; class ListNotificationsControllerTest extends ApiControllerTestCase @@ -35,9 +34,9 @@ class ListNotificationsControllerTest extends ApiControllerTestCase */ public function disallows_index_for_guest() { - $this->expectException(PermissionDeniedException::class); + $response = $this->callWith(); - $this->callWith(); + $this->assertEquals(403, $response->getStatusCode()); } /** diff --git a/framework/core/tests/integration/api/Controller/ListUsersControllerTest.php b/framework/core/tests/integration/api/Controller/ListUsersControllerTest.php index 919f01ddc..35eee7504 100644 --- a/framework/core/tests/integration/api/Controller/ListUsersControllerTest.php +++ b/framework/core/tests/integration/api/Controller/ListUsersControllerTest.php @@ -12,7 +12,6 @@ namespace Flarum\Tests\integration\api\Controller; use Flarum\Api\Controller\ListUsersController; -use Flarum\User\Exception\PermissionDeniedException; use Flarum\User\User; class ListUsersControllerTest extends ApiControllerTestCase @@ -41,9 +40,9 @@ class ListUsersControllerTest extends ApiControllerTestCase */ public function disallows_index_for_guest() { - $this->expectException(PermissionDeniedException::class); + $response = $this->callWith(); - $this->callWith(); + $this->assertEquals(403, $response->getStatusCode()); } /** diff --git a/framework/core/tests/integration/api/Controller/ShowDiscussionControllerTest.php b/framework/core/tests/integration/api/Controller/ShowDiscussionControllerTest.php index 50619c789..db9da7c67 100644 --- a/framework/core/tests/integration/api/Controller/ShowDiscussionControllerTest.php +++ b/framework/core/tests/integration/api/Controller/ShowDiscussionControllerTest.php @@ -15,7 +15,6 @@ use Carbon\Carbon; use Flarum\Api\Controller\ShowDiscussionController; use Flarum\Discussion\Discussion; use Flarum\User\User; -use Illuminate\Database\Eloquent\ModelNotFoundException; class ShowDiscussionControllerTest extends ApiControllerTestCase { @@ -73,11 +72,9 @@ class ShowDiscussionControllerTest extends ApiControllerTestCase */ public function guest_cannot_see_empty_discussion() { - $this->expectException(ModelNotFoundException::class); - $response = $this->callWith([], ['id' => 1]); - $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals(404, $response->getStatusCode()); } /** @@ -95,8 +92,8 @@ class ShowDiscussionControllerTest extends ApiControllerTestCase */ public function guests_cannot_see_private_discussion() { - $this->expectException(ModelNotFoundException::class); + $response = $this->callWith([], ['id' => 3]); - $this->callWith([], ['id' => 3]); + $this->assertEquals(404, $response->getStatusCode()); } } From f73a39d3f4bb28f7ce9fce1163d5830a54f75c46 Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Fri, 9 Aug 2019 20:49:09 +0200 Subject: [PATCH 6/7] Remove old error handler, middleware and tests --- framework/core/src/Api/ErrorHandler.php | 51 -------- .../FallbackExceptionHandler.php | 77 ------------ .../FloodingExceptionHandler.php | 42 ------- .../IlluminateValidationExceptionHandler.php | 58 --------- .../InvalidAccessTokenExceptionHandler.php | 42 ------- ...validConfirmationTokenExceptionHandler.php | 42 ------- .../MethodNotAllowedExceptionHandler.php | 42 ------- .../ModelNotFoundExceptionHandler.php | 42 ------- .../PermissionDeniedExceptionHandler.php | 42 ------- .../RouteNotFoundExceptionHandler.php | 42 ------- .../TokenMismatchExceptionHandler.php | 42 ------- .../ValidationExceptionHandler.php | 53 --------- .../core/src/Api/Middleware/HandleErrors.php | 47 -------- .../Http/Middleware/HandleErrorsWithView.php | 111 ------------------ .../Middleware/HandleErrorsWithWhoops.php | 60 ---------- .../FloodingExceptionHandlerTest.php | 46 -------- ...luminateValidationExceptionHandlerTest.php | 63 ---------- ...InvalidAccessTokenExceptionHandlerTest.php | 46 -------- ...dConfirmationTokenExceptionHandlerTest.php | 46 -------- .../MethodNotAllowedExceptionHandlerTest.php | 46 -------- .../ModelNotFoundExceptionHandlerTest.php | 46 -------- .../PermissionDeniedExceptionHandlerTest.php | 46 -------- .../RouteNotFoundExceptionHandlerTest.php | 46 -------- .../TokenMismatchExceptionHandlerTest.php | 46 -------- .../ValidationExceptionHandlerTest.php | 57 --------- 25 files changed, 1281 deletions(-) delete mode 100644 framework/core/src/Api/ErrorHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/FallbackExceptionHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/FloodingExceptionHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/IlluminateValidationExceptionHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/InvalidAccessTokenExceptionHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/InvalidConfirmationTokenExceptionHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/MethodNotAllowedExceptionHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/ModelNotFoundExceptionHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/PermissionDeniedExceptionHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/RouteNotFoundExceptionHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/TokenMismatchExceptionHandler.php delete mode 100644 framework/core/src/Api/ExceptionHandler/ValidationExceptionHandler.php delete mode 100644 framework/core/src/Api/Middleware/HandleErrors.php delete mode 100644 framework/core/src/Http/Middleware/HandleErrorsWithView.php delete mode 100644 framework/core/src/Http/Middleware/HandleErrorsWithWhoops.php delete mode 100644 framework/core/tests/unit/Api/ExceptionHandler/FloodingExceptionHandlerTest.php delete mode 100644 framework/core/tests/unit/Api/ExceptionHandler/IlluminateValidationExceptionHandlerTest.php delete mode 100644 framework/core/tests/unit/Api/ExceptionHandler/InvalidAccessTokenExceptionHandlerTest.php delete mode 100644 framework/core/tests/unit/Api/ExceptionHandler/InvalidConfirmationTokenExceptionHandlerTest.php delete mode 100644 framework/core/tests/unit/Api/ExceptionHandler/MethodNotAllowedExceptionHandlerTest.php delete mode 100644 framework/core/tests/unit/Api/ExceptionHandler/ModelNotFoundExceptionHandlerTest.php delete mode 100644 framework/core/tests/unit/Api/ExceptionHandler/PermissionDeniedExceptionHandlerTest.php delete mode 100644 framework/core/tests/unit/Api/ExceptionHandler/RouteNotFoundExceptionHandlerTest.php delete mode 100644 framework/core/tests/unit/Api/ExceptionHandler/TokenMismatchExceptionHandlerTest.php delete mode 100644 framework/core/tests/unit/Api/ExceptionHandler/ValidationExceptionHandlerTest.php diff --git a/framework/core/src/Api/ErrorHandler.php b/framework/core/src/Api/ErrorHandler.php deleted file mode 100644 index 21ea56e3b..000000000 --- a/framework/core/src/Api/ErrorHandler.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api; - -use Exception; -use Throwable; -use Tobscure\JsonApi\Document; -use Tobscure\JsonApi\ErrorHandler as JsonApiErrorHandler; - -class ErrorHandler -{ - /** - * @var JsonApiErrorHandler - */ - protected $errorHandler; - - /** - * @param JsonApiErrorHandler $errorHandler - */ - public function __construct(JsonApiErrorHandler $errorHandler) - { - $this->errorHandler = $errorHandler; - } - - /** - * @param Exception $e - * @return JsonApiResponse - */ - public function handle(Throwable $e) - { - if (! $e instanceof Exception) { - $e = new Exception($e->getMessage(), $e->getCode(), $e); - } - - $response = $this->errorHandler->handle($e); - - $document = new Document; - $document->setErrors($response->getErrors()); - - return new JsonApiResponse($document, $response->getStatus()); - } -} diff --git a/framework/core/src/Api/ExceptionHandler/FallbackExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/FallbackExceptionHandler.php deleted file mode 100644 index 1087dbe89..000000000 --- a/framework/core/src/Api/ExceptionHandler/FallbackExceptionHandler.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Psr\Log\LoggerInterface; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class FallbackExceptionHandler implements ExceptionHandlerInterface -{ - /** - * @var bool - */ - protected $debug; - - /** - * @var LoggerInterface - */ - protected $logger; - - /** - * @param bool $debug - * @param LoggerInterface $logger - */ - public function __construct($debug, LoggerInterface $logger) - { - $this->debug = $debug; - $this->logger = $logger; - } - - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return true; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $status = 500; - $error = $this->constructError($e, $status); - - $this->logger->error($e); - - return new ResponseBag($status, [$error]); - } - - /** - * @param Exception $e - * @param $status - * @return array - */ - private function constructError(Exception $e, $status) - { - $error = ['code' => $status, 'title' => 'Internal server error']; - - if ($this->debug) { - $error['detail'] = (string) $e; - } - - return $error; - } -} diff --git a/framework/core/src/Api/ExceptionHandler/FloodingExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/FloodingExceptionHandler.php deleted file mode 100644 index 0a55870dc..000000000 --- a/framework/core/src/Api/ExceptionHandler/FloodingExceptionHandler.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Flarum\Post\Exception\FloodingException; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class FloodingExceptionHandler implements ExceptionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return $e instanceof FloodingException; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $status = 429; - $error = [ - 'status' => (string) $status, - 'code' => 'too_many_requests' - ]; - - return new ResponseBag($status, [$error]); - } -} diff --git a/framework/core/src/Api/ExceptionHandler/IlluminateValidationExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/IlluminateValidationExceptionHandler.php deleted file mode 100644 index afc496c1d..000000000 --- a/framework/core/src/Api/ExceptionHandler/IlluminateValidationExceptionHandler.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Illuminate\Validation\ValidationException; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class IlluminateValidationExceptionHandler implements ExceptionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return $e instanceof ValidationException; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $status = 422; - - $errors = $this->formatErrors($e->errors()); - - return new ResponseBag($status, $errors); - } - - /** - * @param array $errors - * @return array - */ - protected function formatErrors(array $errors) - { - $errors = array_map(function ($field, $messages) { - return [ - 'status' => '422', - 'code' => 'validation_error', - 'detail' => implode("\n", $messages), - 'source' => ['pointer' => "/data/attributes/$field"] - ]; - }, array_keys($errors), $errors); - - return $errors; - } -} diff --git a/framework/core/src/Api/ExceptionHandler/InvalidAccessTokenExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/InvalidAccessTokenExceptionHandler.php deleted file mode 100644 index c91964f17..000000000 --- a/framework/core/src/Api/ExceptionHandler/InvalidAccessTokenExceptionHandler.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\Exception\InvalidAccessTokenException; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class InvalidAccessTokenExceptionHandler implements ExceptionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return $e instanceof InvalidAccessTokenException; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $status = 401; - $error = [ - 'status' => (string) $status, - 'code' => 'invalid_access_token' - ]; - - return new ResponseBag($status, [$error]); - } -} diff --git a/framework/core/src/Api/ExceptionHandler/InvalidConfirmationTokenExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/InvalidConfirmationTokenExceptionHandler.php deleted file mode 100644 index 541b620f7..000000000 --- a/framework/core/src/Api/ExceptionHandler/InvalidConfirmationTokenExceptionHandler.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Flarum\User\Exception\InvalidConfirmationTokenException; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class InvalidConfirmationTokenExceptionHandler implements ExceptionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return $e instanceof InvalidConfirmationTokenException; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $status = 403; - $error = [ - 'status' => (string) $status, - 'code' => 'invalid_confirmation_token' - ]; - - return new ResponseBag($status, [$error]); - } -} diff --git a/framework/core/src/Api/ExceptionHandler/MethodNotAllowedExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/MethodNotAllowedExceptionHandler.php deleted file mode 100644 index 729815e95..000000000 --- a/framework/core/src/Api/ExceptionHandler/MethodNotAllowedExceptionHandler.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Flarum\Http\Exception\MethodNotAllowedException; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class MethodNotAllowedExceptionHandler implements ExceptionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return $e instanceof MethodNotAllowedException; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $status = 405; - $error = [ - 'status' => (string) $status, - 'code' => 'method_not_allowed' - ]; - - return new ResponseBag($status, [$error]); - } -} diff --git a/framework/core/src/Api/ExceptionHandler/ModelNotFoundExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/ModelNotFoundExceptionHandler.php deleted file mode 100644 index e921eb166..000000000 --- a/framework/core/src/Api/ExceptionHandler/ModelNotFoundExceptionHandler.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Illuminate\Database\Eloquent\ModelNotFoundException; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class ModelNotFoundExceptionHandler implements ExceptionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return $e instanceof ModelNotFoundException; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $status = 404; - $error = [ - 'status' => (string) $status, - 'code' => 'resource_not_found' - ]; - - return new ResponseBag($status, [$error]); - } -} diff --git a/framework/core/src/Api/ExceptionHandler/PermissionDeniedExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/PermissionDeniedExceptionHandler.php deleted file mode 100644 index a4b4f9d53..000000000 --- a/framework/core/src/Api/ExceptionHandler/PermissionDeniedExceptionHandler.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Flarum\User\Exception\PermissionDeniedException; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class PermissionDeniedExceptionHandler implements ExceptionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return $e instanceof PermissionDeniedException; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $status = 401; - $error = [ - 'status' => (string) $status, - 'code' => 'permission_denied' - ]; - - return new ResponseBag($status, [$error]); - } -} diff --git a/framework/core/src/Api/ExceptionHandler/RouteNotFoundExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/RouteNotFoundExceptionHandler.php deleted file mode 100644 index 67c25d6a1..000000000 --- a/framework/core/src/Api/ExceptionHandler/RouteNotFoundExceptionHandler.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Flarum\Http\Exception\RouteNotFoundException; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class RouteNotFoundExceptionHandler implements ExceptionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return $e instanceof RouteNotFoundException; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $status = 404; - $error = [ - 'status' => (string) $status, - 'code' => 'route_not_found' - ]; - - return new ResponseBag($status, [$error]); - } -} diff --git a/framework/core/src/Api/ExceptionHandler/TokenMismatchExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/TokenMismatchExceptionHandler.php deleted file mode 100644 index 2258b4cd6..000000000 --- a/framework/core/src/Api/ExceptionHandler/TokenMismatchExceptionHandler.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Flarum\Http\Exception\TokenMismatchException; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class TokenMismatchExceptionHandler implements ExceptionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return $e instanceof TokenMismatchException; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $status = 400; - $error = [ - 'status' => (string) $status, - 'code' => 'csrf_token_mismatch' - ]; - - return new ResponseBag($status, [$error]); - } -} diff --git a/framework/core/src/Api/ExceptionHandler/ValidationExceptionHandler.php b/framework/core/src/Api/ExceptionHandler/ValidationExceptionHandler.php deleted file mode 100644 index f36b253fd..000000000 --- a/framework/core/src/Api/ExceptionHandler/ValidationExceptionHandler.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\ExceptionHandler; - -use Exception; -use Flarum\Foundation\ValidationException; -use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface; -use Tobscure\JsonApi\Exception\Handler\ResponseBag; - -class ValidationExceptionHandler implements ExceptionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function manages(Exception $e) - { - return $e instanceof ValidationException; - } - - /** - * {@inheritdoc} - */ - public function handle(Exception $e) - { - $errors = array_merge( - $this->buildErrors($e->getAttributes(), '/data/attributes'), - $this->buildErrors($e->getRelationships(), '/data/relationships') - ); - - return new ResponseBag(422, $errors); - } - - private function buildErrors(array $messages, $pointer) - { - return array_map(function ($path, $detail) use ($pointer) { - return [ - 'status' => '422', - 'code' => 'validation_error', - 'detail' => $detail, - 'source' => ['pointer' => $pointer.'/'.$path] - ]; - }, array_keys($messages), $messages); - } -} diff --git a/framework/core/src/Api/Middleware/HandleErrors.php b/framework/core/src/Api/Middleware/HandleErrors.php deleted file mode 100644 index cfcc54068..000000000 --- a/framework/core/src/Api/Middleware/HandleErrors.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Api\Middleware; - -use Flarum\Api\ErrorHandler; -use Psr\Http\Message\ResponseInterface as Response; -use Psr\Http\Message\ServerRequestInterface as Request; -use Psr\Http\Server\MiddlewareInterface as Middleware; -use Psr\Http\Server\RequestHandlerInterface as Handler; -use Throwable; - -class HandleErrors implements Middleware -{ - /** - * @var ErrorHandler - */ - protected $errorHandler; - - /** - * @param ErrorHandler $errorHandler - */ - public function __construct(ErrorHandler $errorHandler) - { - $this->errorHandler = $errorHandler; - } - - /** - * Catch all errors that happen during further middleware execution. - */ - public function process(Request $request, Handler $handler): Response - { - try { - return $handler->handle($request); - } catch (Throwable $e) { - return $this->errorHandler->handle($e); - } - } -} diff --git a/framework/core/src/Http/Middleware/HandleErrorsWithView.php b/framework/core/src/Http/Middleware/HandleErrorsWithView.php deleted file mode 100644 index 960074f39..000000000 --- a/framework/core/src/Http/Middleware/HandleErrorsWithView.php +++ /dev/null @@ -1,111 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Http\Middleware; - -use Flarum\Settings\SettingsRepositoryInterface; -use Illuminate\Contracts\View\Factory as ViewFactory; -use Psr\Http\Message\ResponseInterface as Response; -use Psr\Http\Message\ServerRequestInterface as Request; -use Psr\Http\Server\MiddlewareInterface as Middleware; -use Psr\Http\Server\RequestHandlerInterface as Handler; -use Psr\Log\LoggerInterface; -use Symfony\Component\Translation\TranslatorInterface; -use Throwable; -use Zend\Diactoros\Response\HtmlResponse; - -class HandleErrorsWithView implements Middleware -{ - /** - * @var ViewFactory - */ - protected $view; - - /** - * @var LoggerInterface - */ - protected $logger; - - /** - * @var TranslatorInterface - */ - protected $translator; - - /** - * @var SettingsRepositoryInterface - */ - protected $settings; - - /** - * @param ViewFactory $view - * @param LoggerInterface $logger - * @param TranslatorInterface $translator - * @param SettingsRepositoryInterface $settings - */ - public function __construct(ViewFactory $view, LoggerInterface $logger, TranslatorInterface $translator, SettingsRepositoryInterface $settings) - { - $this->view = $view; - $this->logger = $logger; - $this->translator = $translator; - $this->settings = $settings; - } - - /** - * Catch all errors that happen during further middleware execution. - */ - public function process(Request $request, Handler $handler): Response - { - try { - return $handler->handle($request); - } catch (Throwable $e) { - return $this->formatException($e); - } - } - - protected function formatException(Throwable $error) - { - $status = 500; - $errorCode = $error->getCode(); - - // If it seems to be a valid HTTP status code, we pass on the - // exception's status. - if (is_int($errorCode) && $errorCode >= 400 && $errorCode < 600) { - $status = $errorCode; - } - - if (! $this->view->exists($name = "flarum.forum::error.$status")) { - $name = 'flarum.forum::error.default'; - - $this->logger->error($error); - } - - $view = $this->view->make($name) - ->with('error', $error) - ->with('message', $this->getMessage($status)); - - return new HtmlResponse($view->render(), $status); - } - - private function getMessage($status) - { - return $this->getTranslationIfExists($status) - ?? $this->getTranslationIfExists(500) - ?? 'An error occurred while trying to load this page.'; - } - - private function getTranslationIfExists($status) - { - $key = "core.views.error.${status}_message"; - $translation = $this->translator->trans($key, ['{forum}' => $this->settings->get('forum_title')]); - - return $translation === $key ? null : $translation; - } -} diff --git a/framework/core/src/Http/Middleware/HandleErrorsWithWhoops.php b/framework/core/src/Http/Middleware/HandleErrorsWithWhoops.php deleted file mode 100644 index 317d3dd2d..000000000 --- a/framework/core/src/Http/Middleware/HandleErrorsWithWhoops.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Http\Middleware; - -use Franzl\Middleware\Whoops\WhoopsRunner; -use Psr\Http\Message\ResponseInterface as Response; -use Psr\Http\Message\ServerRequestInterface as Request; -use Psr\Http\Server\MiddlewareInterface as Middleware; -use Psr\Http\Server\RequestHandlerInterface as Handler; -use Psr\Log\LoggerInterface; -use Throwable; - -class HandleErrorsWithWhoops implements Middleware -{ - /** - * @var LoggerInterface - */ - protected $logger; - - /** - * @param LoggerInterface $logger - */ - public function __construct(LoggerInterface $logger) - { - $this->logger = $logger; - } - - /** - * Catch all errors that happen during further middleware execution. - */ - public function process(Request $request, Handler $handler): Response - { - try { - return $handler->handle($request); - } catch (Throwable $e) { - $status = 500; - $errorCode = $e->getCode(); - - if ($errorCode !== 404) { - $this->logger->error($e); - } - - if (is_int($errorCode) && $errorCode >= 400 && $errorCode < 600) { - $status = $errorCode; - } - - return WhoopsRunner::handle($e, $request) - ->withStatus($status); - } - } -} diff --git a/framework/core/tests/unit/Api/ExceptionHandler/FloodingExceptionHandlerTest.php b/framework/core/tests/unit/Api/ExceptionHandler/FloodingExceptionHandlerTest.php deleted file mode 100644 index e2043b493..000000000 --- a/framework/core/tests/unit/Api/ExceptionHandler/FloodingExceptionHandlerTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Tests\unit\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\ExceptionHandler\FloodingExceptionHandler; -use Flarum\Post\Exception\FloodingException; -use PHPUnit\Framework\TestCase; - -class FloodingExceptionHandlerTest extends TestCase -{ - private $handler; - - public function setUp() - { - $this->handler = new FloodingExceptionHandler; - } - - public function test_it_handles_recognisable_exceptions() - { - $this->assertFalse($this->handler->manages(new Exception)); - $this->assertTrue($this->handler->manages(new FloodingException)); - } - - public function test_it_provides_expected_output() - { - $result = $this->handler->handle(new FloodingException); - - $this->assertEquals(429, $result->getStatus()); - $this->assertEquals([ - [ - 'status' => '429', - 'code' => 'too_many_requests' - ] - ], $result->getErrors()); - } -} diff --git a/framework/core/tests/unit/Api/ExceptionHandler/IlluminateValidationExceptionHandlerTest.php b/framework/core/tests/unit/Api/ExceptionHandler/IlluminateValidationExceptionHandlerTest.php deleted file mode 100644 index ed3d799f6..000000000 --- a/framework/core/tests/unit/Api/ExceptionHandler/IlluminateValidationExceptionHandlerTest.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Tests\unit\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\ExceptionHandler\IlluminateValidationExceptionHandler; -use Illuminate\Translation\ArrayLoader; -use Illuminate\Translation\Translator; -use Illuminate\Validation\Factory; -use Illuminate\Validation\ValidationException; -use PHPUnit\Framework\TestCase; - -class IlluminateValidationExceptionHandlerTest extends TestCase -{ - private $handler; - - public function setUp() - { - $this->handler = new IlluminateValidationExceptionHandler; - } - - public function test_it_handles_familiar_exceptions() - { - $validException = new ValidationException($this->makeValidator()); - - $this->assertFalse($this->handler->manages(new Exception)); - $this->assertTrue($this->handler->manages($validException)); - } - - public function test_it_creates_the_desired_output() - { - $exception = new ValidationException($this->makeValidator(['foo' => ''], ['foo' => 'required'])); - - $response = $this->handler->handle($exception); - - $this->assertEquals(422, $response->getStatus()); - $this->assertEquals([ - [ - 'status' => '422', - 'code' => 'validation_error', - 'detail' => 'validation.required', - 'source' => ['pointer' => '/data/attributes/foo'] - ] - ], $response->getErrors()); - } - - private function makeValidator($data = [], $rules = []) - { - $translator = new Translator(new ArrayLoader(), 'en'); - $factory = new Factory($translator); - - return $factory->make($data, $rules); - } -} diff --git a/framework/core/tests/unit/Api/ExceptionHandler/InvalidAccessTokenExceptionHandlerTest.php b/framework/core/tests/unit/Api/ExceptionHandler/InvalidAccessTokenExceptionHandlerTest.php deleted file mode 100644 index 3d7355eac..000000000 --- a/framework/core/tests/unit/Api/ExceptionHandler/InvalidAccessTokenExceptionHandlerTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Tests\unit\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\Exception\InvalidAccessTokenException; -use Flarum\Api\ExceptionHandler\InvalidAccessTokenExceptionHandler; -use PHPUnit\Framework\TestCase; - -class InvalidAccessTokenExceptionHandlerTest extends TestCase -{ - private $handler; - - public function setUp() - { - $this->handler = new InvalidAccessTokenExceptionHandler; - } - - public function test_it_handles_recognisable_exceptions() - { - $this->assertFalse($this->handler->manages(new Exception)); - $this->assertTrue($this->handler->manages(new InvalidAccessTokenException)); - } - - public function test_output() - { - $response = $this->handler->handle(new InvalidAccessTokenException); - - $this->assertEquals(401, $response->getStatus()); - $this->assertEquals([ - [ - 'status' => '401', - 'code' => 'invalid_access_token' - ] - ], $response->getErrors()); - } -} diff --git a/framework/core/tests/unit/Api/ExceptionHandler/InvalidConfirmationTokenExceptionHandlerTest.php b/framework/core/tests/unit/Api/ExceptionHandler/InvalidConfirmationTokenExceptionHandlerTest.php deleted file mode 100644 index 1f289fe73..000000000 --- a/framework/core/tests/unit/Api/ExceptionHandler/InvalidConfirmationTokenExceptionHandlerTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Tests\unit\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\ExceptionHandler\InvalidConfirmationTokenExceptionHandler; -use Flarum\User\Exception\InvalidConfirmationTokenException; -use PHPUnit\Framework\TestCase; - -class InvalidConfirmationTokenExceptionHandlerTest extends TestCase -{ - private $handler; - - public function setUp() - { - $this->handler = new InvalidConfirmationTokenExceptionHandler; - } - - public function test_it_handles_recognisable_exceptions() - { - $this->assertFalse($this->handler->manages(new Exception)); - $this->assertTrue($this->handler->manages(new InvalidConfirmationTokenException)); - } - - public function test_output() - { - $response = $this->handler->handle(new InvalidConfirmationTokenException); - - $this->assertEquals(403, $response->getStatus()); - $this->assertEquals([ - [ - 'status' => '403', - 'code' => 'invalid_confirmation_token' - ] - ], $response->getErrors()); - } -} diff --git a/framework/core/tests/unit/Api/ExceptionHandler/MethodNotAllowedExceptionHandlerTest.php b/framework/core/tests/unit/Api/ExceptionHandler/MethodNotAllowedExceptionHandlerTest.php deleted file mode 100644 index 23ce0761f..000000000 --- a/framework/core/tests/unit/Api/ExceptionHandler/MethodNotAllowedExceptionHandlerTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Tests\unit\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\ExceptionHandler\MethodNotAllowedExceptionHandler; -use Flarum\Http\Exception\MethodNotAllowedException; -use PHPUnit\Framework\TestCase; - -class MethodNotAllowedExceptionHandlerTest extends TestCase -{ - private $handler; - - public function setUp() - { - $this->handler = new MethodNotAllowedExceptionHandler(); - } - - public function test_it_handles_recognisable_exceptions() - { - $this->assertFalse($this->handler->manages(new Exception)); - $this->assertTrue($this->handler->manages(new MethodNotAllowedException())); - } - - public function test_managing_exceptions() - { - $response = $this->handler->handle(new MethodNotAllowedException); - - $this->assertEquals(405, $response->getStatus()); - $this->assertEquals([ - [ - 'status' => '405', - 'code' => 'method_not_allowed' - ] - ], $response->getErrors()); - } -} diff --git a/framework/core/tests/unit/Api/ExceptionHandler/ModelNotFoundExceptionHandlerTest.php b/framework/core/tests/unit/Api/ExceptionHandler/ModelNotFoundExceptionHandlerTest.php deleted file mode 100644 index c1eb2efcc..000000000 --- a/framework/core/tests/unit/Api/ExceptionHandler/ModelNotFoundExceptionHandlerTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Tests\unit\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\ExceptionHandler\ModelNotFoundExceptionHandler; -use Illuminate\Database\Eloquent\ModelNotFoundException; -use PHPUnit\Framework\TestCase; - -class ModelNotFoundExceptionHandlerTest extends TestCase -{ - private $handler; - - public function setUp() - { - $this->handler = new ModelNotFoundExceptionHandler; - } - - public function test_it_handles_recognisable_exceptions() - { - $this->assertFalse($this->handler->manages(new Exception)); - $this->assertTrue($this->handler->manages(new ModelNotFoundException)); - } - - public function test_managing_exceptions() - { - $response = $this->handler->handle(new ModelNotFoundException); - - $this->assertEquals(404, $response->getStatus()); - $this->assertEquals([ - [ - 'status' => '404', - 'code' => 'resource_not_found' - ] - ], $response->getErrors()); - } -} diff --git a/framework/core/tests/unit/Api/ExceptionHandler/PermissionDeniedExceptionHandlerTest.php b/framework/core/tests/unit/Api/ExceptionHandler/PermissionDeniedExceptionHandlerTest.php deleted file mode 100644 index dcfdb33f8..000000000 --- a/framework/core/tests/unit/Api/ExceptionHandler/PermissionDeniedExceptionHandlerTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Tests\unit\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\ExceptionHandler\PermissionDeniedExceptionHandler; -use Flarum\User\Exception\PermissionDeniedException; -use PHPUnit\Framework\TestCase; - -class PermissionDeniedExceptionHandlerTest extends TestCase -{ - private $handler; - - public function setUp() - { - $this->handler = new PermissionDeniedExceptionHandler; - } - - public function test_it_handles_recognisable_exceptions() - { - $this->assertFalse($this->handler->manages(new Exception)); - $this->assertTrue($this->handler->manages(new PermissionDeniedException)); - } - - public function test_managing_exceptions() - { - $response = $this->handler->handle(new PermissionDeniedException); - - $this->assertEquals(401, $response->getStatus()); - $this->assertEquals([ - [ - 'status' => '401', - 'code' => 'permission_denied' - ] - ], $response->getErrors()); - } -} diff --git a/framework/core/tests/unit/Api/ExceptionHandler/RouteNotFoundExceptionHandlerTest.php b/framework/core/tests/unit/Api/ExceptionHandler/RouteNotFoundExceptionHandlerTest.php deleted file mode 100644 index 51c82cb70..000000000 --- a/framework/core/tests/unit/Api/ExceptionHandler/RouteNotFoundExceptionHandlerTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Tests\unit\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\ExceptionHandler\RouteNotFoundExceptionHandler; -use Flarum\Http\Exception\RouteNotFoundException; -use PHPUnit\Framework\TestCase; - -class RouteNotFoundExceptionHandlerTest extends TestCase -{ - private $handler; - - public function setUp() - { - $this->handler = new RouteNotFoundExceptionHandler(); - } - - public function test_it_handles_recognisable_exceptions() - { - $this->assertFalse($this->handler->manages(new Exception)); - $this->assertTrue($this->handler->manages(new RouteNotFoundException())); - } - - public function test_managing_exceptions() - { - $response = $this->handler->handle(new RouteNotFoundException); - - $this->assertEquals(404, $response->getStatus()); - $this->assertEquals([ - [ - 'status' => '404', - 'code' => 'route_not_found' - ] - ], $response->getErrors()); - } -} diff --git a/framework/core/tests/unit/Api/ExceptionHandler/TokenMismatchExceptionHandlerTest.php b/framework/core/tests/unit/Api/ExceptionHandler/TokenMismatchExceptionHandlerTest.php deleted file mode 100644 index eebb1bff8..000000000 --- a/framework/core/tests/unit/Api/ExceptionHandler/TokenMismatchExceptionHandlerTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Tests\unit\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\ExceptionHandler\TokenMismatchExceptionHandler; -use Flarum\Http\Exception\TokenMismatchException; -use PHPUnit\Framework\TestCase; - -class TokenMismatchExceptionHandlerTest extends TestCase -{ - private $handler; - - public function setUp() - { - $this->handler = new TokenMismatchExceptionHandler; - } - - public function test_it_handles_recognisable_exceptions() - { - $this->assertFalse($this->handler->manages(new Exception)); - $this->assertTrue($this->handler->manages(new TokenMismatchException())); - } - - public function test_managing_exceptions() - { - $response = $this->handler->handle(new TokenMismatchException); - - $this->assertEquals(400, $response->getStatus()); - $this->assertEquals([ - [ - 'status' => '400', - 'code' => 'csrf_token_mismatch' - ] - ], $response->getErrors()); - } -} diff --git a/framework/core/tests/unit/Api/ExceptionHandler/ValidationExceptionHandlerTest.php b/framework/core/tests/unit/Api/ExceptionHandler/ValidationExceptionHandlerTest.php deleted file mode 100644 index a014a96e5..000000000 --- a/framework/core/tests/unit/Api/ExceptionHandler/ValidationExceptionHandlerTest.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Flarum\Tests\unit\Api\ExceptionHandler; - -use Exception; -use Flarum\Api\ExceptionHandler\ValidationExceptionHandler; -use Flarum\Foundation\ValidationException; -use PHPUnit\Framework\TestCase; - -class ValidationExceptionHandlerTest extends TestCase -{ - private $handler; - - public function setUp() - { - $this->handler = new ValidationExceptionHandler; - } - - public function test_it_handles_recognisable_exceptions() - { - $this->assertFalse($this->handler->manages(new Exception)); - $this->assertTrue($this->handler->manages(new ValidationException([]))); - } - - public function test_managing_exceptions() - { - $response = $this->handler->handle(new ValidationException( - ['foo' => 'Attribute error'], - ['bar' => 'Relationship error'] - )); - - $this->assertEquals(422, $response->getStatus()); - $this->assertEquals([ - [ - 'status' => '422', - 'code' => 'validation_error', - 'detail' => 'Attribute error', - 'source' => ['pointer' => '/data/attributes/foo'] - ], - [ - 'status' => '422', - 'code' => 'validation_error', - 'detail' => 'Relationship error', - 'source' => ['pointer' => '/data/relationships/bar'] - ] - ], $response->getErrors()); - } -} From 01c77b8e2a941bd82c3cc1398be24bc26f12806e Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Sat, 10 Aug 2019 11:03:44 +0200 Subject: [PATCH 7/7] Support multiple error reporters The error handling middleware now expects an array of reporters. Extensions can register new reporters in the container like this: use Flarum\Foundation\ErrorHandling\Reporter; $container->tag(NewReporter::class, Reporter::class); Note that this is just an implementation detail and will be hidden behind an extender. --- framework/core/src/Admin/AdminServiceProvider.php | 2 +- framework/core/src/Api/ApiServiceProvider.php | 2 +- framework/core/src/Forum/ForumServiceProvider.php | 2 +- .../core/src/Foundation/ErrorServiceProvider.php | 2 +- framework/core/src/Http/Middleware/HandleErrors.php | 13 +++++++------ framework/core/src/Install/Installer.php | 2 +- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/framework/core/src/Admin/AdminServiceProvider.php b/framework/core/src/Admin/AdminServiceProvider.php index 66ad29ec2..ce7a6b462 100644 --- a/framework/core/src/Admin/AdminServiceProvider.php +++ b/framework/core/src/Admin/AdminServiceProvider.php @@ -58,7 +58,7 @@ class AdminServiceProvider extends AbstractServiceProvider $pipe->pipe(new HttpMiddleware\HandleErrors( $app->make(Registry::class), $app->inDebugMode() ? $app->make(WhoopsRenderer::class) : $app->make(ViewRenderer::class), - $app->make(Reporter::class) + $app->tagged(Reporter::class) )); $pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class)); diff --git a/framework/core/src/Api/ApiServiceProvider.php b/framework/core/src/Api/ApiServiceProvider.php index 49501d84e..abe53ef91 100644 --- a/framework/core/src/Api/ApiServiceProvider.php +++ b/framework/core/src/Api/ApiServiceProvider.php @@ -53,7 +53,7 @@ class ApiServiceProvider extends AbstractServiceProvider $pipe->pipe(new HttpMiddleware\HandleErrors( $app->make(Registry::class), $app->make(JsonApiRenderer::class), - $app->make(Reporter::class) + $app->tagged(Reporter::class) )); $pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class)); diff --git a/framework/core/src/Forum/ForumServiceProvider.php b/framework/core/src/Forum/ForumServiceProvider.php index 92614f0fb..a6f6faf1e 100644 --- a/framework/core/src/Forum/ForumServiceProvider.php +++ b/framework/core/src/Forum/ForumServiceProvider.php @@ -68,7 +68,7 @@ class ForumServiceProvider extends AbstractServiceProvider $pipe->pipe(new HttpMiddleware\HandleErrors( $app->make(Registry::class), $app->inDebugMode() ? $app->make(WhoopsRenderer::class) : $app->make(ViewRenderer::class), - $app->make(Reporter::class) + $app->tagged(Reporter::class) )); $pipe->pipe($app->make(HttpMiddleware\ParseJsonBody::class)); diff --git a/framework/core/src/Foundation/ErrorServiceProvider.php b/framework/core/src/Foundation/ErrorServiceProvider.php index 2a1deba21..ec82370ee 100644 --- a/framework/core/src/Foundation/ErrorServiceProvider.php +++ b/framework/core/src/Foundation/ErrorServiceProvider.php @@ -71,6 +71,6 @@ class ErrorServiceProvider extends AbstractServiceProvider ); }); - $this->app->singleton(Reporter::class, LogReporter::class); + $this->app->tag(LogReporter::class, Reporter::class); } } diff --git a/framework/core/src/Http/Middleware/HandleErrors.php b/framework/core/src/Http/Middleware/HandleErrors.php index ff077f105..94d749f8b 100644 --- a/framework/core/src/Http/Middleware/HandleErrors.php +++ b/framework/core/src/Http/Middleware/HandleErrors.php @@ -13,7 +13,6 @@ namespace Flarum\Http\Middleware; use Flarum\Foundation\ErrorHandling\Formatter; use Flarum\Foundation\ErrorHandling\Registry; -use Flarum\Foundation\ErrorHandling\Reporter; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Server\MiddlewareInterface as Middleware; @@ -33,15 +32,15 @@ class HandleErrors implements Middleware protected $formatter; /** - * @var Reporter + * @var \Flarum\Foundation\ErrorHandling\Reporter[] */ - protected $reporter; + protected $reporters; - public function __construct(Registry $registry, Formatter $formatter, Reporter $reporter) + public function __construct(Registry $registry, Formatter $formatter, array $reporters) { $this->registry = $registry; $this->formatter = $formatter; - $this->reporter = $reporter; + $this->reporters = $reporters; } /** @@ -55,7 +54,9 @@ class HandleErrors implements Middleware $error = $this->registry->handle($e); if ($error->shouldBeReported()) { - $this->reporter->report($error); + foreach ($this->reporters as $reporter) { + $reporter->report($error); + } } return $this->formatter->format($error, $request); diff --git a/framework/core/src/Install/Installer.php b/framework/core/src/Install/Installer.php index 72bd82905..ba4e2c098 100644 --- a/framework/core/src/Install/Installer.php +++ b/framework/core/src/Install/Installer.php @@ -43,7 +43,7 @@ class Installer implements AppInterface $pipe->pipe(new HandleErrors( $this->container->make(Registry::class), $this->container->make(WhoopsRenderer::class), - $this->container->make(Reporter::class) + $this->container->tagged(Reporter::class) )); $pipe->pipe($this->container->make(StartSession::class)); $pipe->pipe(