1010use Psr \Http \Message \ServerRequestInterface ;
1111use Psr \Http \Message \StreamFactoryInterface ;
1212use Respect \Parameter \Resolver ;
13+ use Respect \Rest \Handlers \ErrorHandler ;
14+ use Respect \Rest \Handlers \ExceptionHandler ;
15+ use Respect \Rest \Handlers \StatusHandler ;
1316use Respect \Rest \Routes \AbstractRoute ;
1417use Throwable ;
1518
19+ use function in_array ;
1620use function is_a ;
1721use function rawurldecode ;
1822use function rtrim ;
@@ -50,7 +54,7 @@ final class DispatchContext implements ContainerInterface
5054 private string $ effectivePath = '' ;
5155
5256 /** @var array<int, AbstractRoute> */
53- private array $ sideRoutes = [];
57+ private array $ handlers = [];
5458
5559 private Resolver |null $ resolver = null ;
5660
@@ -138,23 +142,22 @@ public function response(): ResponseInterface|null
138142 }
139143
140144 $ route = $ this ->route ;
145+ $ isHandler = in_array ($ route , $ this ->handlers , true );
146+ $ previousErrorHandler = $ isHandler ? null : $ this ->installErrorHandler ();
141147
142148 try {
143- $ errorHandler = $ this ->prepareForErrorForwards ($ route );
144149 $ preRoutineResult = $ this ->routinePipeline ()->processBy ($ this , $ route );
145150
146- if ($ preRoutineResult !== null ) {
147- if ($ preRoutineResult instanceof AbstractRoute) {
148- return $ this ->forward ($ preRoutineResult );
149- }
151+ if ($ preRoutineResult instanceof AbstractRoute) {
152+ return $ this ->forward ($ preRoutineResult );
153+ }
150154
151- if ($ preRoutineResult instanceof ResponseInterface) {
152- return $ this ->finalizeResponse ($ preRoutineResult );
153- }
155+ if ($ preRoutineResult instanceof ResponseInterface) {
156+ return $ this ->finalizeResponse ($ preRoutineResult );
157+ }
154158
155- if ($ preRoutineResult === false ) {
156- return $ this ->finalizeResponse ('' );
157- }
159+ if ($ preRoutineResult === false ) {
160+ return $ this ->finalizeResponse ('' );
158161 }
159162
160163 $ rawResult = $ route ->dispatchTarget ($ this ->method (), $ this ->params , $ this );
@@ -164,20 +167,28 @@ public function response(): ResponseInterface|null
164167 }
165168
166169 $ processedResult = $ this ->routinePipeline ()->processThrough ($ this , $ route , $ rawResult );
167- $ errorResponse = $ this ->forwardErrors ($ errorHandler , $ route );
168170
169- if ($ errorResponse !== null ) {
170- return $ errorResponse ;
171+ if (!$ isHandler ) {
172+ $ errorResponse = $ this ->forwardCollectedErrors ();
173+ if ($ errorResponse !== null ) {
174+ return $ errorResponse ;
175+ }
171176 }
172177
173178 return $ this ->finalizeResponse ($ processedResult );
174179 } catch (Throwable $ e ) {
175- $ exceptionResponse = $ this ->catchExceptions ($ e , $ route );
176- if ($ exceptionResponse === null ) {
177- throw $ e ;
180+ if (!$ isHandler ) {
181+ $ exceptionResponse = $ this ->catchExceptions ($ e );
182+ if ($ exceptionResponse !== null ) {
183+ return $ exceptionResponse ;
184+ }
178185 }
179186
180- return $ exceptionResponse ;
187+ throw $ e ;
188+ } finally {
189+ if ($ previousErrorHandler !== null ) {
190+ set_error_handler ($ previousErrorHandler );
191+ }
181192 }
182193 }
183194
@@ -193,10 +204,10 @@ public function setRoutinePipeline(RoutinePipeline $routinePipeline): void
193204 $ this ->routinePipeline = $ routinePipeline ;
194205 }
195206
196- /** @param array<int, AbstractRoute> $sideRoutes */
197- public function setSideRoutes (array $ sideRoutes ): void
207+ /** @param array<int, AbstractRoute> $handlers */
208+ public function setHandlers (array $ handlers ): void
198209 {
199- $ this ->sideRoutes = $ sideRoutes ;
210+ $ this ->handlers = $ handlers ;
200211 }
201212
202213 public function setResponder (Responder $ responder ): void
@@ -228,19 +239,19 @@ public function get(string $id): mixed
228239 throw new NotFoundException (sprintf ('No entry found for "%s" ' , $ id ));
229240 }
230241
231- /** @return callable|null The previous error handler, or null */
232- protected function prepareForErrorForwards ( AbstractRoute $ route ): callable |null
242+ /** @return callable|null The previous error handler, or null if no ErrorHandler is registered */
243+ private function installErrorHandler ( ): callable |null
233244 {
234- foreach ($ route -> sideRoutes as $ sideRoute ) {
235- if ($ sideRoute instanceof Routes \Error ) {
245+ foreach ($ this -> handlers as $ handler ) {
246+ if ($ handler instanceof ErrorHandler ) {
236247 return set_error_handler (
237248 static function (
238249 int $ errno ,
239250 string $ errstr ,
240251 string $ errfile = '' ,
241252 int $ errline = 0 ,
242- ) use ($ sideRoute ): bool {
243- $ sideRoute ->errors [] = [$ errno , $ errstr , $ errfile , $ errline ];
253+ ) use ($ handler ): bool {
254+ $ handler ->errors [] = [$ errno , $ errstr , $ errfile , $ errline ];
244255
245256 return true ;
246257 },
@@ -251,54 +262,50 @@ static function (
251262 return null ;
252263 }
253264
254- protected function forwardErrors ( callable | null $ errorHandler , AbstractRoute $ route ): ResponseInterface |null
265+ private function forwardCollectedErrors ( ): ResponseInterface |null
255266 {
256- if ($ errorHandler !== null ) {
257- set_error_handler ($ errorHandler );
258- }
259-
260- foreach ($ route ->sideRoutes as $ sideRoute ) {
261- if ($ sideRoute instanceof Routes \Error && $ sideRoute ->errors ) {
262- return $ this ->forward ($ sideRoute );
267+ foreach ($ this ->handlers as $ handler ) {
268+ if ($ handler instanceof ErrorHandler && $ handler ->errors ) {
269+ return $ this ->forward ($ handler );
263270 }
264271 }
265272
266273 return null ;
267274 }
268275
269- protected function catchExceptions (Throwable $ e, AbstractRoute $ route ): ResponseInterface |null
276+ private function catchExceptions (Throwable $ e ): ResponseInterface |null
270277 {
271- foreach ($ route -> sideRoutes as $ sideRoute ) {
272- if (!$ sideRoute instanceof Routes \Exception ) {
278+ foreach ($ this -> handlers as $ handler ) {
279+ if (!$ handler instanceof ExceptionHandler ) {
273280 continue ;
274281 }
275282
276- if (is_a ($ e , $ sideRoute ->class )) {
277- $ sideRoute ->exception = $ e ;
283+ if (is_a ($ e , $ handler ->class )) {
284+ $ handler ->exception = $ e ;
278285
279- return $ this ->forward ($ sideRoute );
286+ return $ this ->forward ($ handler );
280287 }
281288 }
282289
283290 return null ;
284291 }
285292
286- protected function forwardToStatusRoute (ResponseInterface $ preparedResponse ): ResponseInterface |null
293+ private function forwardToStatusRoute (ResponseInterface $ preparedResponse ): ResponseInterface |null
287294 {
288295 $ statusCode = $ preparedResponse ->getStatusCode ();
289296
290- foreach ($ this ->sideRoutes as $ sideRoute ) {
297+ foreach ($ this ->handlers as $ handler ) {
291298 if (
292- $ sideRoute instanceof Routes \Status
293- && ($ sideRoute ->statusCode === $ statusCode || $ sideRoute ->statusCode === null )
299+ $ handler instanceof StatusHandler
300+ && ($ handler ->statusCode === $ statusCode || $ handler ->statusCode === null )
294301 ) {
295302 $ this ->hasStatusOverride = true ;
296303
297304 // Run routine negotiation (e.g. Accept) before forwarding,
298305 // since the normal route-selection phase was skipped
299- $ this ->routinePipeline ()->matches ($ this , $ sideRoute , $ this ->params );
306+ $ this ->routinePipeline ()->matches ($ this , $ handler , $ this ->params );
300307
301- $ result = $ this ->forward ($ sideRoute );
308+ $ result = $ this ->forward ($ handler );
302309
303310 // Preserve the original status code on the forwarded response
304311 return $ result ?->withStatus($ statusCode );
@@ -308,7 +315,7 @@ protected function forwardToStatusRoute(ResponseInterface $preparedResponse): Re
308315 return null ;
309316 }
310317
311- protected function finalizeResponse (mixed $ response ): ResponseInterface
318+ private function finalizeResponse (mixed $ response ): ResponseInterface
312319 {
313320 return $ this ->responder ()->finalize (
314321 $ response ,
0 commit comments