Skip to content

Commit 8af1704

Browse files
committed
Refactor exception handler for L12
1 parent 8cb8f11 commit 8af1704

File tree

1 file changed

+63
-66
lines changed

1 file changed

+63
-66
lines changed

src/Foundation/Exception/Handler.php

Lines changed: 63 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
11
<?php namespace October\Rain\Foundation\Exception;
22

3-
use Log;
43
use Event;
54
use Response;
5+
use Illuminate\Auth\Access\AuthorizationException;
6+
use Illuminate\Auth\AuthenticationException;
7+
use Illuminate\Database\RecordNotFoundException;
8+
use Illuminate\Database\RecordsNotFoundException;
9+
use Illuminate\Database\Eloquent\ModelNotFoundException;
610
use Illuminate\Http\Exceptions\HttpResponseException;
11+
use Illuminate\Http\Response as HttpResponse;
712
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
813
use Illuminate\Contracts\Support\Responsable;
9-
use Illuminate\Support\Reflector;
14+
use Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException;
15+
use Illuminate\Routing\Router;
16+
use Illuminate\Session\TokenMismatchException;
17+
use Illuminate\Validation\ValidationException;
18+
use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
19+
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
20+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
21+
use Symfony\Component\HttpKernel\Exception\HttpException;
1022
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
1123
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
1224
use October\Rain\Exception\ForbiddenException;
1325
use October\Rain\Exception\NotFoundException;
1426
use October\Rain\Exception\AjaxException;
1527
use Throwable;
16-
use Exception;
17-
use Closure;
1828

1929
/**
2030
* Handler is the core exception handler
@@ -67,26 +77,7 @@ public function report(Throwable $exception)
6777
return;
6878
}
6979

70-
$exception = $this->mapException($exception);
71-
72-
if ($this->shouldntReport($exception)) {
73-
return;
74-
}
75-
76-
if (Reflector::isCallable($reportCallable = [$exception, 'report']) &&
77-
$this->container->call($reportCallable) !== false) {
78-
return;
79-
}
80-
81-
foreach ($this->reportCallbacks as $reportCallback) {
82-
if ($reportCallback->handles($exception) && $reportCallback($exception) === false) {
83-
return;
84-
}
85-
}
86-
87-
if (class_exists('Log')) {
88-
Log::error($exception);
89-
}
80+
parent::report($exception);
9081

9182
/**
9283
* @event exception.report
@@ -106,7 +97,7 @@ public function report(Throwable $exception)
10697
*
10798
* @param \Illuminate\Http\Request $request
10899
* @param \Throwable $exception
109-
* @return \Illuminate\Http\Response
100+
* @return \Symfony\Component\HttpFoundation\Response
110101
*/
111102
public function render($request, Throwable $exception)
112103
{
@@ -115,22 +106,33 @@ public function render($request, Throwable $exception)
115106
return parent::render($request, $exception);
116107
}
117108

109+
$exception = $this->mapException($exception);
110+
111+
// Exception has a render method (Laravel 12)
112+
if (method_exists($exception, 'render') && $response = $exception->render($request)) {
113+
return $this->finalizeRenderedResponse(
114+
$request,
115+
Router::toResponse($request, $response),
116+
$exception
117+
);
118+
}
119+
118120
// Exception wants to return its own response
119121
if ($exception instanceof Responsable) {
120-
return $exception->toResponse($request);
122+
return $this->finalizeRenderedResponse($request, $exception->toResponse($request), $exception);
121123
}
122124

123125
// Convert to public-friendly exception
124-
$exception = $this->prepareException($this->mapException($exception));
126+
$exception = $this->prepareException($exception);
125127

126128
// Custom handlers
127129
if ($response = $this->renderViaCallbacks($request, $exception)) {
128-
return $response;
130+
return $this->finalizeRenderedResponse($request, $response, $exception);
129131
}
130132

131133
// Exception is a response
132134
if ($exception instanceof HttpResponseException) {
133-
return $exception->getResponse();
135+
return $this->finalizeRenderedResponse($request, $exception->getResponse(), $exception);
134136
}
135137

136138
/**
@@ -145,24 +147,46 @@ public function render($request, Throwable $exception)
145147
*/
146148
$statusCode = $this->getStatusCode($exception);
147149
if (($event = Event::fire('exception.beforeRender', [$exception, $statusCode, $request], true)) !== null) {
148-
return Response::make($event, $statusCode);
150+
return $this->finalizeRenderedResponse(
151+
$request,
152+
Response::make($event, $statusCode),
153+
$exception
154+
);
149155
}
150156

151-
return parent::render($request, $exception);
157+
// Standard Laravel 12 rendering
158+
return $this->finalizeRenderedResponse($request, match (true) {
159+
$exception instanceof AuthenticationException => $this->unauthenticated($request, $exception),
160+
$exception instanceof ValidationException => $this->convertValidationExceptionToResponse($exception, $request),
161+
default => $this->renderExceptionResponse($request, $exception),
162+
}, $exception);
152163
}
153164

154165
/**
155166
* prepareException for rendering.
167+
*
168+
* @param \Throwable $e
169+
* @return \Throwable
156170
*/
157-
protected function prepareException(Throwable $e)
171+
protected function prepareException(Throwable $e): Throwable
158172
{
159-
$e = parent::prepareException($e);
160-
161-
if ($e instanceof NotFoundException) {
162-
$e = new NotFoundHttpException($e->getMessage(), $e);
163-
}
164-
165-
return $e;
173+
return match (true) {
174+
// October-specific: NotFoundException → NotFoundHttpException
175+
$e instanceof NotFoundException => new NotFoundHttpException($e->getMessage(), $e),
176+
177+
// Laravel 12 standard conversions
178+
$e instanceof BackedEnumCaseNotFoundException => new NotFoundHttpException($e->getMessage(), $e),
179+
$e instanceof ModelNotFoundException => new NotFoundHttpException($e->getMessage(), $e),
180+
$e instanceof AuthorizationException && $e->hasStatus() => new HttpException(
181+
$e->status(), $e->response()?->message() ?: (HttpResponse::$statusTexts[$e->status()] ?? 'Whoops, looks like something went wrong.'), $e
182+
),
183+
$e instanceof AuthorizationException && ! $e->hasStatus() => new AccessDeniedHttpException($e->getMessage(), $e),
184+
$e instanceof TokenMismatchException => new HttpException(419, $e->getMessage(), $e),
185+
$e instanceof RequestExceptionInterface => new BadRequestHttpException('Bad request.', $e),
186+
$e instanceof RecordNotFoundException => new NotFoundHttpException('Not found.', $e),
187+
$e instanceof RecordsNotFoundException => new NotFoundHttpException('Not found.', $e),
188+
default => $e,
189+
};
166190
}
167191

168192
/**
@@ -214,33 +238,6 @@ public function error(callable $callback)
214238
$this->renderable($callback);
215239
}
216240

217-
/**
218-
* renderViaCallbacks tries to render a response from request and exception via render callbacks.
219-
* @param \Illuminate\Http\Request $request
220-
* @return mixed
221-
*/
222-
protected function renderViaCallbacks($request, Throwable $e)
223-
{
224-
foreach ($this->renderCallbacks as $renderCallback) {
225-
foreach ($this->firstClosureParameterTypes($renderCallback) as $type) {
226-
if (!is_a($e, $type)) {
227-
continue;
228-
}
229-
230-
$response = $renderCallback($e, $request);
231-
if (!$response) {
232-
continue;
233-
}
234-
235-
if (is_string($response)) {
236-
return Response::make($response);
237-
}
238-
239-
return $response;
240-
}
241-
}
242-
}
243-
244241
/**
245242
* hasBootedEvents checks if we can broadcast events
246243
*/

0 commit comments

Comments
 (0)