Skip to content

Fix exception handling, rank fallthrough, and add statusRoute()#176

Merged
alganet merged 1 commit intoRespect:masterfrom
alganet:misc
Mar 20, 2026
Merged

Fix exception handling, rank fallthrough, and add statusRoute()#176
alganet merged 1 commit intoRespect:masterfrom
alganet:misc

Conversation

@alganet
Copy link
Member

@alganet alganet commented Mar 20, 2026

Exception routes now use is_a() for class matching, supporting inheritance and Throwable. handle() no longer swallows exceptions silently — unhandled exceptions propagate per PSR-15.

routineMatch() no longer falls through from exact-method (rank 0) to any-method (rank 1) routes when a when() routine fails, preventing validation bypass via catch-all routes.

New statusRoute() API lets users handle framework-generated HTTP status codes (404, 405, 400, etc.) with custom callbacks, including content negotiation via accept(). Passing null as status code creates a catch-all handler for any status.

@codecov-commenter
Copy link

codecov-commenter commented Mar 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.91%. Comparing base (1d8db28) to head (d8e8ea9).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #176      +/-   ##
============================================
+ Coverage     96.67%   96.91%   +0.24%     
- Complexity      430      437       +7     
============================================
  Files            31       32       +1     
  Lines          1082     1103      +21     
============================================
+ Hits           1046     1069      +23     
+ Misses           36       34       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@alganet alganet requested a review from Copilot March 20, 2026 01:07
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the router/dispatch pipeline to (1) propagate unhandled exceptions instead of converting them to generic 500 responses, (2) prevent routine validation failures from falling through to lower-priority “ANY” routes, and (3) introduce a new statusRoute() API to customize framework-generated status responses (404/405/400/etc.), including supporting content negotiation.

Changes:

  • Add statusRoute() and a new Routes\Status side-route type, plus dispatch-time plumbing to forward prepared status responses to those handlers.
  • Change exception route matching to use is_a() (inheritance + Throwable support) and stop swallowing unhandled exceptions in handle().
  • Fix route selection so routine failures at a higher method-rank do not fall through to lower-rank catch-all routes.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Routes/Status.php Adds a new side-route type for handling prepared HTTP status responses.
src/DispatchContext.php Adds forwarding of prepared status responses to status side-routes; changes exception matching to is_a().
src/DispatchEngine.php Stops catching Throwable in handle(); injects side-routes into the dispatch context.
src/RouteProvider.php Extends the provider contract with getSideRoutes().
src/Router.php Adds statusRoute() and exposes side-routes via getSideRoutes().
tests/Routes/StatusTest.php New tests for statusRoute() behavior (404/405/400, JSON responses, dispatch/handle).
tests/Routes/ExceptionTest.php Adds tests for inheritance matching and Throwable exception routes.
tests/DispatchEngineTest.php Updates expected behavior to propagate unhandled exceptions.
tests/RouterTest.php Updates expected behavior to propagate unhandled exceptions.
Comments suppressed due to low confidence (1)

src/DispatchContext.php:282

  • Switching exception route matching to is_a($e, $sideRoute->class) changes the previous special-casing where exceptionRoute('Exception', ...) would catch any Throwable (including PHP Error). With is_a, an Exception handler will no longer catch Error instances; users must register Throwable explicitly. If this behavior change is intended, it likely needs a compatibility shim (treat 'Exception'/'\Exception' as 'Throwable') or documentation/tests to avoid surprising upgrades.
                continue;
            }

            if (is_a($e, $sideRoute->class)) {
                $sideRoute->exception = $e;

                return $this->forward($sideRoute);
            }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Exception routes now use is_a() for class matching, supporting
inheritance and Throwable. handle() no longer swallows exceptions
silently — unhandled exceptions propagate per PSR-15.

routineMatch() no longer falls through from exact-method (rank 0)
to any-method (rank 1) routes when a when() routine fails, preventing
validation bypass via catch-all routes.

New statusRoute() API lets users handle framework-generated HTTP
status codes (404, 405, 400, etc.) with custom callbacks, including
content negotiation via accept(). Passing null as status code creates
a catch-all handler for any status.
@alganet alganet marked this pull request as ready for review March 20, 2026 01:43
@alganet alganet merged commit 56231a7 into Respect:master Mar 20, 2026
3 checks passed
@alganet alganet deleted the misc branch March 20, 2026 01:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants