Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #225 +/- ##
============================================
Coverage 100.00% 100.00%
- Complexity 129 192 +63
============================================
Files 13 15 +2
Lines 400 528 +128
============================================
+ Hits 400 528 +128 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
PR SummaryThis pull request introduces numerous updates aimed at better organizing and streamlining how routes are handled in the system:
These changes all contribute to a more robust and efficient route handling system within the software, aligning the codebase with modern best practices in PHP coding. |
We can mention it in docs or drop the current syntax.
Yes, doesn't fix directly. Since there is no exception, there is no need for it. |
But what goal of syntax change? |
vjik
left a comment
There was a problem hiding this comment.
Can we make Route and Group constructors public and keep BC?
I think, GroupBuilder and RouteBuilder are not needed.
Did you mean builders? Route and Group constructors are public in this PR.
Will we abandon the current syntax? |
No, I mean
Mark as deprecated, then remove in major version. |
Let's abandon right away since the PR has major changes. |
…ray_is_list, and apply readonly properties
What major changes are needed? |
# Conflicts: # composer.json # src/Debug/DebugRoutesCommand.php # src/Route.php
# Conflicts: # src/Group.php # src/Route.php # src/RouteCollection.php # src/RouteCollector.php # tests/ConfigTest.php # tests/Debug/DebugRoutesCommandTest.php # tests/Debug/RouterCollectorTest.php # tests/Debug/UrlMatcherInterfaceProxyTest.php # tests/GroupTest.php # tests/RouteTest.php
…ove method consistency
There was a problem hiding this comment.
Pull request overview
This PR introduces a new routing syntax by adding immutable builder classes (RouteBuilder, GroupBuilder) while refactoring core Route/Group to mutable objects with constructors + getters/setters. It also updates routing collection/dispatch/debug code and rewrites tests to match the new APIs (breaking BC as noted).
Changes:
- Add
RoutableInterfaceplus newBuilder\RouteBuilderandBuilder\GroupBuilderthat convert to coreRoute/GroupviatoRoute(). - Refactor
Route,Group,RouteCollector, andRouteCollectionAPIs (getters/setters; middleware collection rename; host→hosts changes). - Update tests and debug tooling to use builders/getters, and bump dev dependencies.
Reviewed changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/RouteTest.php | Reworked tests for new Route constructor + setters/getters and validation. |
| tests/RouteCollectorTest.php | Updated to builder aliases and getMiddlewares() API. |
| tests/RouteCollectionTest.php | Updated for new route/group APIs and enabled middleware retrieval via getters. |
| tests/Middleware/RouterTest.php | Adjusted matcher to return built routes via toRoute(). |
| tests/MatchingResultTest.php | Updated to instantiate Route directly with methods/pattern. |
| tests/HydratorAttribute/RouteArgumentTest.php | Updated to convert builder route to core route with toRoute(). |
| tests/GroupTest.php | Reworked tests for new Group constructor + setters/getters and validation. |
| tests/Debug/UrlMatcherInterfaceProxyTest.php | Updated to instantiate Route with methods/pattern. |
| tests/Debug/RouterCollectorTest.php | Updated expectations for collected keys (hosts) and new route creation patterns. |
| tests/Debug/DebugRoutesCommandTest.php | Updated to use RouteBuilder in fixtures. |
| tests/CurrentRouteTest.php | Updated CurrentRoute API host→hosts and new Route constructor usage. |
| tests/ConfigTest.php | Updated config test to use RouteBuilder and toRoute(). |
| tests/Builder/RouteBuilderTest.php | New test coverage for builder-based fluent syntax and immutability expectations. |
| tests/Builder/GroupBuilderTest.php | New test coverage for group builder syntax, CORS behavior, and immutability expectations. |
| src/RouteCollectorInterface.php | Accept RoutableInterface and rename middleware getter to getMiddlewares(). |
| src/RouteCollector.php | Implement RoutableInterface support and rename internal middleware storage/getter. |
| src/RouteCollection.php | Convert RoutableInterface via toRoute(), switch to setter-based middleware/host/pattern/name updates. |
| src/Route.php | Make Route mutable with constructor + getters/setters; action separated from middleware list; add validations/caching. |
| src/RoutableInterface.php | New interface to unify builder→route/group conversion. |
| src/Middleware/Router.php | Dispatch using Route::getEnabledMiddlewares() instead of getData(). |
| src/Group.php | Make Group mutable with constructor + getters/setters; add validation and enabled middleware caching. |
| src/Debug/RouterCollector.php | Switch debug output to new getters; host→hosts key changes; middleware/action extraction updated. |
| src/Debug/DebugRoutesCommand.php | Replace custom array list detection with array_is_list(). |
| src/CurrentRoute.php | Update to use route getters and host→hosts API. |
| src/Builder/RouteBuilder.php | New immutable fluent builder for routes, converting to core Route. |
| src/Builder/GroupBuilder.php | New immutable fluent builder for groups, converting to core Group. |
| composer.json | Bump dev dependencies (PHPUnit, Rector, Psalm, yiisoft packages). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…on, and apply `readonly` for constructor injection
…dlewares and adjust defaults assignment logic
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** @var Route $item */ | ||
| $item->setPattern($prefix . $item->getPattern()); | ||
|
|
||
| if (!str_contains($modifiedItem->getData('name'), implode(', ', $modifiedItem->getData('methods')))) { | ||
| $modifiedItem = $modifiedItem->name($namePrefix . $modifiedItem->getData('name')); | ||
| if (!str_contains($item->getName(), implode(', ', $item->getMethods()))) { | ||
| $item->setName($namePrefix . $item->getName()); | ||
| } |
There was a problem hiding this comment.
injectGroup() mutates the original Route instance in-place (pattern/name/hosts/middlewares). If the same Route object is reused across multiple groups or added both inside and outside a group, later injections will see already-prefixed state (double prefix/name, duplicated middleware layering), producing incorrect route definitions. Consider cloning the Route/Group before applying group/collector transformations (prefix/namePrefix/hosts/middlewares/CORS), or otherwise ensure each injection works on a distinct instance.
|
|
||
| $middlewares = $route->getData('enabledMiddlewares'); | ||
| $middlewares = $route->getEnabledMiddlewares(); | ||
| $action = array_pop($middlewares); |
There was a problem hiding this comment.
getMiddlewaresAndAction() always treats the last enabled middleware as the action via array_pop(). Routes can legitimately have middlewares but no action (e.g. a route defined without action() but inheriting group middlewares), so this will misreport the last middleware as the action. Use $route->getAction() directly (and $route->getMiddlewares() / $route->getEnabledMiddlewares() accordingly), or only pop when $route->getAction() !== null.
| $action = array_pop($middlewares); | |
| $action = $route->getAction(); |
| foreach ($defaults as $key => $value) { | ||
| if (!is_scalar($value) && !($value instanceof Stringable) && null !== $value) { | ||
| throw new InvalidArgumentException( | ||
| 'Invalid $defaults provided, indexed array of scalar or `Stringable` or null expected.', |
There was a problem hiding this comment.
The exception message in setDefaults() says "indexed array", but the implementation allows associative keys (and defaults are described as being indexed by parameter names). Consider rewording the message to avoid implying numeric indexing (e.g. "array of scalar/Stringable/null values expected").
| 'Invalid $defaults provided, indexed array of scalar or `Stringable` or null expected.', | |
| 'Invalid $defaults provided, array of scalar, Stringable, or null values expected.', |
| * Returns the current route hosts. | ||
| * | ||
| * @return string|null The current route host. | ||
| * @return array|null The current route hosts. | ||
| */ | ||
| public function getHost(): ?string | ||
| public function getHosts(): ?array | ||
| { | ||
| return $this->route?->getData('host'); | ||
| return $this->route?->getHosts(); |
There was a problem hiding this comment.
CurrentRoute::getHosts() returns ?array, but the underlying value is a list of host strings. Tightening the return type/phpdoc to ?string[] (or ?array<int,string>) would improve static analysis and make the BC break clearer for consumers.
Current syntax