11<?php namespace October \Rain \Foundation \Exception ;
22
3- use Log ;
43use Event ;
54use 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 ;
610use Illuminate \Http \Exceptions \HttpResponseException ;
11+ use Illuminate \Http \Response as HttpResponse ;
712use Illuminate \Foundation \Exceptions \Handler as ExceptionHandler ;
813use 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 ;
1022use Symfony \Component \HttpKernel \Exception \HttpExceptionInterface ;
1123use Symfony \Component \HttpKernel \Exception \NotFoundHttpException ;
1224use October \Rain \Exception \ForbiddenException ;
1325use October \Rain \Exception \NotFoundException ;
1426use October \Rain \Exception \AjaxException ;
1527use 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