mirror of
https://github.com/flarum/core.git
synced 2025-08-09 18:07:02 +02:00
chore: use ExceptionHandler
contract
This commit is contained in:
@@ -88,6 +88,7 @@
|
||||
"symfony/mime": "^6.3",
|
||||
"symfony/polyfill-intl-messageformatter": "^1.27",
|
||||
"symfony/postmark-mailer": "^6.3",
|
||||
"symfony/psr-http-message-bridge": "^2.3",
|
||||
"symfony/translation": "^6.3",
|
||||
"symfony/translation-contracts": "^2.5",
|
||||
"symfony/yaml": "^6.3",
|
||||
|
@@ -37,7 +37,6 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
$this->container->singleton('flarum.admin.middleware', function () {
|
||||
return [
|
||||
HttpMiddleware\InjectActorReference::class,
|
||||
'flarum.admin.error_handler',
|
||||
HttpMiddleware\StartSession::class,
|
||||
HttpMiddleware\RememberFromCookie::class,
|
||||
HttpMiddleware\AuthenticateWithSession::class,
|
||||
@@ -50,14 +49,6 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
];
|
||||
});
|
||||
|
||||
$this->container->bind('flarum.admin.error_handler', function (Container $container) {
|
||||
return new HttpMiddleware\HandleErrors(
|
||||
$container->make(Registry::class),
|
||||
$container['flarum.config']->inDebugMode() ? $container->make(WhoopsFormatter::class) : $container->make(ViewFormatter::class),
|
||||
$container->tagged(Reporter::class)
|
||||
);
|
||||
});
|
||||
|
||||
$this->container->bind('flarum.assets.admin', function (Container $container) {
|
||||
/** @var \Flarum\Frontend\Assets $assets */
|
||||
$assets = $container->make('flarum.assets.factory')('admin');
|
||||
|
@@ -45,7 +45,6 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
$this->container->singleton('flarum.api.middleware', function () {
|
||||
return [
|
||||
HttpMiddleware\InjectActorReference::class,
|
||||
'flarum.api.error_handler',
|
||||
Middleware\FakeHttpMethods::class,
|
||||
HttpMiddleware\StartSession::class,
|
||||
HttpMiddleware\RememberFromCookie::class,
|
||||
@@ -57,14 +56,6 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
];
|
||||
});
|
||||
|
||||
$this->container->bind('flarum.api.error_handler', function (Container $container) {
|
||||
return new HttpMiddleware\HandleErrors(
|
||||
$container->make(Registry::class),
|
||||
new JsonApiFormatter($container['flarum.config']->inDebugMode()),
|
||||
$container->tagged(Reporter::class)
|
||||
);
|
||||
});
|
||||
|
||||
$this->container->singleton('flarum.api.notification_serializers', function () {
|
||||
return [
|
||||
'discussionRenamed' => BasicDiscussionSerializer::class
|
||||
|
@@ -44,7 +44,6 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
$this->container->singleton('flarum.forum.middleware', function () {
|
||||
return [
|
||||
HttpMiddleware\InjectActorReference::class,
|
||||
'flarum.forum.error_handler',
|
||||
HttpMiddleware\CollectGarbage::class,
|
||||
HttpMiddleware\StartSession::class,
|
||||
HttpMiddleware\RememberFromCookie::class,
|
||||
@@ -58,14 +57,6 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
];
|
||||
});
|
||||
|
||||
$this->container->bind('flarum.forum.error_handler', function (Container $container) {
|
||||
return new HttpMiddleware\HandleErrors(
|
||||
$container->make(Registry::class),
|
||||
$container['flarum.config']->inDebugMode() ? $container->make(WhoopsFormatter::class) : $container->make(ViewFormatter::class),
|
||||
$container->tagged(Reporter::class)
|
||||
);
|
||||
});
|
||||
|
||||
$this->container->bind('flarum.assets.forum', function (Container $container) {
|
||||
/** @var Assets $assets */
|
||||
$assets = $container->make('flarum.assets.factory')('forum');
|
||||
|
@@ -83,6 +83,7 @@ class Application extends IlluminateContainer implements LaravelApplication
|
||||
protected function registerBaseServiceProviders(): void
|
||||
{
|
||||
$this->register(new EventServiceProvider($this));
|
||||
$this->register(new ErrorServiceProvider($this));
|
||||
$this->register(new RoutingServiceProvider($this));
|
||||
|
||||
// Because we need to check very early if the version of the app
|
||||
|
@@ -42,7 +42,6 @@ class RegisterCoreProviders implements IlluminateBootstrapperInterface
|
||||
$app->register(ConsoleServiceProvider::class);
|
||||
$app->register(DiscussionServiceProvider::class);
|
||||
$app->register(ExtensionServiceProvider::class);
|
||||
$app->register(ErrorServiceProvider::class);
|
||||
$app->register(FilesystemServiceProvider::class);
|
||||
$app->register(FilterServiceProvider::class);
|
||||
$app->register(FormatterServiceProvider::class);
|
||||
|
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Foundation\ErrorHandling;
|
||||
|
||||
use Flarum\Foundation\Config;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandling;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Symfony\Component\Console\Application as ConsoleApplication;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Catch exceptions thrown in the routing stack and console and handle them safely.
|
||||
*
|
||||
* All errors will be rendered using the provided formatter. In addition,
|
||||
* unknown errors will be passed on to one or multiple
|
||||
* {@see \Flarum\Foundation\ErrorHandling\Reporter} instances.
|
||||
*/
|
||||
class ExceptionHandler implements ExceptionHandling
|
||||
{
|
||||
protected array $handledErrors = [];
|
||||
|
||||
public function __construct(
|
||||
protected readonly Registry $registry,
|
||||
protected readonly iterable $formatters,
|
||||
/** @var \Flarum\Foundation\ErrorHandling\Reporter[] $reporters */
|
||||
protected readonly iterable $reporters,
|
||||
protected readonly Config $config
|
||||
) {
|
||||
}
|
||||
|
||||
public function report(Throwable $e): void
|
||||
{
|
||||
$error = $this->getHandledError($e);
|
||||
|
||||
if ($error->shouldBeReported()) {
|
||||
foreach ($this->reporters as $reporter) {
|
||||
$reporter->report($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function render($request, Throwable $e): Response /** @phpstan-ignore-line */
|
||||
{
|
||||
return $this->resolveFormatter($request)->format(
|
||||
$this->getHandledError($e), $request
|
||||
);
|
||||
}
|
||||
|
||||
public function renderForConsole($output, Throwable $e): void
|
||||
{
|
||||
(new ConsoleApplication())->renderThrowable($e, $output);
|
||||
}
|
||||
|
||||
public function shouldReport(Throwable $e): bool
|
||||
{
|
||||
return $this->getHandledError($e)->shouldBeReported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and cache the handled error for the given exception.
|
||||
*/
|
||||
protected function getHandledError(Throwable $e): HandledError
|
||||
{
|
||||
return $this->handledErrors[$this->exceptionKey($e)] ??= $this->registry->handle($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a unique key for the given exception.
|
||||
*/
|
||||
protected function exceptionKey(Throwable $e): string
|
||||
{
|
||||
return get_class($e).':'.$e->getMessage().$e->getLine();
|
||||
}
|
||||
|
||||
protected function resolveFormatter(Request $request): HttpFormatter
|
||||
{
|
||||
return match (true) {
|
||||
$request->expectsJson(),
|
||||
$request->routeIs('api.*') => Arr::first($this->formatters, fn (HttpFormatter $formatter) => $formatter instanceof JsonApiFormatter),
|
||||
$this->config->inDebugMode() => Arr::first($this->formatters, fn (HttpFormatter $formatter) => $formatter instanceof WhoopsFormatter),
|
||||
default => Arr::first($this->formatters, fn (HttpFormatter $formatter) => $formatter instanceof ViewFormatter),
|
||||
};
|
||||
}
|
||||
}
|
@@ -10,6 +10,7 @@
|
||||
namespace Flarum\Foundation\ErrorHandling;
|
||||
|
||||
use Flarum\Api\JsonApiResponse;
|
||||
use Flarum\Foundation\Config;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Tobscure\JsonApi\Document;
|
||||
@@ -22,7 +23,7 @@ use Tobscure\JsonApi\Document;
|
||||
class JsonApiFormatter implements HttpFormatter
|
||||
{
|
||||
public function __construct(
|
||||
private readonly bool $includeTrace = false
|
||||
private readonly Config $config
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -46,7 +47,7 @@ class JsonApiFormatter implements HttpFormatter
|
||||
'code' => $error->getType(),
|
||||
];
|
||||
|
||||
if ($this->includeTrace) {
|
||||
if ($this->config->inDebugMode()) {
|
||||
$default['detail'] = (string) $error->getException();
|
||||
}
|
||||
|
||||
|
@@ -11,6 +11,9 @@ namespace Flarum\Foundation;
|
||||
|
||||
use Flarum\Extension\Exception as ExtensionException;
|
||||
use Flarum\Foundation\ErrorHandling as Handling;
|
||||
use Flarum\Foundation\ErrorHandling\ExceptionHandler;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Validation\ValidationException as IlluminateValidationException;
|
||||
use Tobscure\JsonApi\Exception\InvalidParameterException;
|
||||
@@ -19,6 +22,13 @@ class ErrorServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
$this->container->singleton(ExceptionHandlerContract::class, function (Container $container) {
|
||||
return $container->make(ExceptionHandler::class, [
|
||||
'formatters' => $container->tagged(Handling\HttpFormatter::class),
|
||||
'reporters' => $container->tagged(Handling\Reporter::class),
|
||||
]);
|
||||
});
|
||||
|
||||
$this->container->singleton('flarum.error.statuses', function () {
|
||||
return [
|
||||
// 400 Bad Request
|
||||
@@ -73,5 +83,11 @@ class ErrorServiceProvider extends AbstractServiceProvider
|
||||
});
|
||||
|
||||
$this->container->tag(Handling\LogReporter::class, Handling\Reporter::class);
|
||||
|
||||
$this->container->tag([
|
||||
Handling\JsonApiFormatter::class,
|
||||
Handling\ViewFormatter::class,
|
||||
Handling\WhoopsFormatter::class,
|
||||
], Handling\HttpFormatter::class);
|
||||
}
|
||||
}
|
||||
|
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Flarum\Foundation\ErrorHandling\HttpFormatter;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Catch exceptions thrown in a PSR-15 middleware stack and handle them safely.
|
||||
*
|
||||
* All errors will be rendered using the provided formatter. In addition,
|
||||
* unknown errors will be passed on to one or multiple
|
||||
* {@see \Flarum\Foundation\ErrorHandling\Reporter} instances.
|
||||
*/
|
||||
class HandleErrors implements IlluminateMiddlewareInterface
|
||||
{
|
||||
public function __construct(
|
||||
protected Registry $registry,
|
||||
protected HttpFormatter $formatter,
|
||||
/** @var \Flarum\Foundation\ErrorHandling\Reporter[] $reporters */
|
||||
protected iterable $reporters
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch all errors that happen during further middleware execution.
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
try {
|
||||
return $next($request);
|
||||
} catch (Throwable $e) {
|
||||
$error = $this->registry->handle($e);
|
||||
|
||||
if ($error->shouldBeReported()) {
|
||||
foreach ($this->reporters as $reporter) {
|
||||
$reporter->report($error->getException());
|
||||
}
|
||||
}
|
||||
|
||||
// dump($e);
|
||||
|
||||
return $this->formatter->format($error, $request);
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,13 +16,13 @@ class UrlGenerator extends IlluminateUrlGenerator
|
||||
|
||||
public function base(string $frontend): string
|
||||
{
|
||||
$url = $this->config->url();
|
||||
$url = rtrim($this->config->url(), '/');
|
||||
|
||||
if ($frontend) {
|
||||
$url .= '/'.$this->config->path($frontend);
|
||||
}
|
||||
|
||||
return $url;
|
||||
return rtrim($url, '/');
|
||||
}
|
||||
|
||||
public function path(string $frontend, string $path): string
|
||||
|
@@ -10,23 +10,18 @@
|
||||
namespace Flarum\Install;
|
||||
|
||||
use Flarum\Foundation\AppInterface;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Flarum\Foundation\ErrorHandling\Reporter;
|
||||
use Flarum\Foundation\ErrorHandling\WhoopsFormatter;
|
||||
use Flarum\Http\Middleware as HttpMiddleware;
|
||||
use Flarum\Install\Console\InstallCommand;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Laminas\Stratigility\MiddlewarePipe;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
|
||||
class Installer implements AppInterface
|
||||
{
|
||||
public function __construct(
|
||||
protected Container $container
|
||||
protected Application $container
|
||||
) {
|
||||
}
|
||||
|
||||
public function getContainer(): Container
|
||||
public function getContainer(): Application
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
@@ -34,11 +29,6 @@ class Installer implements AppInterface
|
||||
public function getMiddlewareStack(): array
|
||||
{
|
||||
return [
|
||||
new HttpMiddleware\HandleErrors(
|
||||
$this->container->make(Registry::class),
|
||||
$this->container->make(WhoopsFormatter::class),
|
||||
$this->container->tagged(Reporter::class)
|
||||
),
|
||||
$this->container->make(HttpMiddleware\StartSession::class),
|
||||
];
|
||||
}
|
||||
|
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Queue;
|
||||
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandling;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Throwable;
|
||||
|
||||
class ExceptionHandler implements ExceptionHandling
|
||||
{
|
||||
public function __construct(
|
||||
private readonly LoggerInterface $logger
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* @param Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
public function report(Throwable $e)
|
||||
{
|
||||
$this->logger->error((string) $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
public function render($request, Throwable $e) /** @phpstan-ignore-line */
|
||||
{
|
||||
// dd($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception to the console.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @param Throwable $e
|
||||
* @return void
|
||||
*/
|
||||
public function renderForConsole($output, Throwable $e)
|
||||
{
|
||||
// TODO: Implement renderForConsole() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the exception should be reported.
|
||||
*
|
||||
* @param Throwable $e
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldReport(Throwable $e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -61,10 +61,6 @@ class QueueServiceProvider extends AbstractServiceProvider
|
||||
return $queue;
|
||||
});
|
||||
|
||||
$this->container->singleton(ExceptionHandling::class, function (Container $container) {
|
||||
return new ExceptionHandler($container['log']);
|
||||
});
|
||||
|
||||
$this->container->singleton(Worker::class, function (Container $container) {
|
||||
/** @var Config $config */
|
||||
$config = $container->make(Config::class);
|
||||
|
Reference in New Issue
Block a user