Files
fotospiel-app/app/Exceptions/Handler.php

179 lines
5.5 KiB
PHP

<?php
namespace App\Exceptions;
use App\Support\ApiError;
use Illuminate\Database\Connectors\ConnectionException as DatabaseConnectionException;
use Illuminate\Database\QueryException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Client\ConnectionException as HttpClientConnectionException;
use Illuminate\Queue\InvalidQueueException;
use Illuminate\Queue\MaxAttemptsExceededException;
use Illuminate\Routing\Exceptions\InvalidSignatureException;
use Illuminate\Validation\ValidationException;
use League\Flysystem\FilesystemException;
use PDOException;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* The list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->reportable(function (Throwable $e) {
//
});
}
public function render($request, Throwable $e)
{
if ($request->expectsJson()) {
if ($e instanceof ValidationException) {
return ApiError::response(
'validation_failed',
'Validation failed',
'The given data was invalid.',
422,
['errors' => $e->errors()],
);
}
$status = $this->isHttpException($e)
? $this->toHttpException($e)->getStatusCode()
: 500;
$code = $status >= 500 ? 'server_error' : 'request_failed';
return ApiError::response(
$code,
$status >= 500 ? 'Unexpected error' : 'Request could not be completed',
$this->buildGenericMessage($status),
$status,
);
}
if ($request->inertia()) {
if ($e instanceof ValidationException) {
return back()->withErrors($e->errors())->withInput($request->all());
}
}
if ($e instanceof InvalidSignatureException && ! $request->expectsJson()) {
$request->session()->flash('verification', [
'status' => 'error',
'title' => __('auth.verification.expired_title'),
'message' => __('auth.verification.expired_message'),
]);
return redirect()->route('verification.notice');
}
if (! $request->expectsJson() && ! $request->inertia()) {
if ($hintKey = $this->resolveServerErrorHint($e)) {
$request->attributes->set('serverErrorHint', __($hintKey, [], app()->getLocale()));
}
}
return parent::render($request, $e);
}
private function buildGenericMessage(int $status): string
{
if ($status >= 500) {
return 'Something went wrong on our side. Please try again later.';
}
return 'Your request could not be processed. Please verify the details and try again.';
}
private function resolveServerErrorHint(Throwable $throwable): ?string
{
$status = $this->isHttpException($throwable)
? $this->toHttpException($throwable)->getStatusCode()
: 500;
if ($status < 500) {
return null;
}
if ($this->exceptionChainContains($throwable, [
QueryException::class,
DatabaseConnectionException::class,
PDOException::class,
'Doctrine\\DBAL\\Exception',
])) {
return 'marketing.server_error.hints.database';
}
if ($this->exceptionChainContains($throwable, [
FilesystemException::class,
'League\\Flysystem\\UnableToWriteFile',
'League\\Flysystem\\UnableToCreateDirectory',
'League\\Flysystem\\UnableToCheckExistence',
])) {
return 'marketing.server_error.hints.storage';
}
if ($this->exceptionChainContains($throwable, [
InvalidQueueException::class,
MaxAttemptsExceededException::class,
'RedisException',
'Predis\\Connection\\ConnectionException',
])) {
return 'marketing.server_error.hints.queue';
}
if ($this->exceptionChainContains($throwable, [
HttpClientConnectionException::class,
'GuzzleHttp\\Exception\\ConnectException',
'Psr\\Http\\Client\\NetworkExceptionInterface',
'Symfony\\Component\\HttpClient\\Exception\\TransportExceptionInterface',
])) {
return 'marketing.server_error.hints.network';
}
return 'marketing.server_error.hints.generic';
}
private function exceptionChainContains(Throwable $throwable, array $classNames): bool
{
do {
foreach ($classNames as $className) {
if ($this->throwableIsInstanceOf($throwable, $className)) {
return true;
}
}
$throwable = $throwable->getPrevious();
} while ($throwable);
return false;
}
private function throwableIsInstanceOf(Throwable $throwable, string $className): bool
{
if ($className === '') {
return false;
}
if (! class_exists($className) && ! interface_exists($className)) {
return false;
}
return $throwable instanceof $className;
}
}