Conversation
📝 WalkthroughWalkthroughAdds attribute-based routing: a Route attribute and method-specific attributes (Get/Post/Put/Delete), class-level RouteGroup, RouteDefinition DTO, RouteScanner for reflection- and directory-based discovery with caching, README documentation, and comprehensive unit tests for scanner behavior. Changes
Sequence DiagramsequenceDiagram
actor App as Application
participant Scanner as RouteScanner
participant Reflection as PHP Reflection
participant Controller as Controller Class
participant Group as RouteGroup
participant RouteAttr as Route / Method Attributes
participant Registry as RouteDefinition[]
App->>Scanner: scanClass("SomeController")
activate Scanner
Scanner->>Reflection: new ReflectionClass("SomeController")
Reflection-->>Scanner: class metadata
Scanner->>Controller: read class attributes
alt RouteGroup present
Controller-->>Scanner: RouteGroup instance
Scanner->>Group: getPrefix(), getFilters()
Group-->>Scanner: prefix, filters
end
Note over Scanner: iterate methods
loop per method
Scanner->>Reflection: getMethods()
Reflection-->>Scanner: ReflectionMethod[]
Scanner->>RouteAttr: get Route attributes
RouteAttr-->>Scanner: Route instances
alt group prefix exists
Scanner->>Group: applyPrefix(routePath)
Group-->>Scanner: combinedPath
end
alt group filters exist
Scanner->>Group: mergeFilters(routeFilters)
Group-->>Scanner: mergedFilters
end
Scanner->>Registry: append RouteDefinition(path, method, controller, action, name, filters)
end
Scanner-->>App: return RouteDefinition[]
deactivate Scanner
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment Thanks for integrating Codecov - We've got you covered ☂️ |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
tests/unit/RouteScannerTest.php (1)
41-142: Comprehensive test suite for RouteScanner.Good coverage of:
- Path composition with
RouteGroupprefixes- Filter merging (group + method filters)
- Route names
- Multiple routes per method
- Cache behavior and clearing
- Non-existent class handling
The use of
assertSameintestScanClassesCachesResultscorrectly verifies object identity for cache validation.Consider adding tests for
PutandDeleteattributes to complete coverage of all HTTP method attributes, though the current tests adequately validate the pattern since all method attributes follow the same inheritance structure.src/Routing/Attributes/RouteGroup.php (1)
65-79: Edge case: trailing slash when routePath is/or empty.If
routePathis/(or empty), the result will have a trailing slash (e.g.,/admin/). This may cause route matching issues depending on how the router handles trailing slashes.Consider trimming the trailing slash from the result:
🔎 Proposed fix
public function applyPrefix( string $routePath ): string { if( $this->prefix === '' ) { return $routePath; } // Ensure prefix starts with / and doesn't end with / $prefix = '/' . trim( $this->prefix, '/' ); // Ensure route starts with / $path = '/' . ltrim( $routePath, '/' ); - return $prefix . $path; + return rtrim( $prefix . $path, '/' ) ?: '/'; }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
README.mdsrc/Routing/Attributes/Delete.phpsrc/Routing/Attributes/Get.phpsrc/Routing/Attributes/Post.phpsrc/Routing/Attributes/Put.phpsrc/Routing/Attributes/Route.phpsrc/Routing/Attributes/RouteGroup.phpsrc/Routing/RouteDefinition.phpsrc/Routing/RouteScanner.phptests/unit/RouteScannerTest.php
🧰 Additional context used
🧬 Code graph analysis (9)
src/Routing/Attributes/Delete.php (4)
src/Routing/Attributes/Get.php (2)
Attribute(21-37)__construct(29-36)src/Routing/Attributes/Post.php (2)
Attribute(18-34)__construct(26-33)src/Routing/Attributes/Put.php (2)
Attribute(18-34)__construct(26-33)src/Routing/Attributes/Route.php (2)
Attribute(23-72)__construct(32-39)
src/Routing/RouteDefinition.php (6)
src/Routing/Attributes/Delete.php (1)
__construct(26-33)src/Routing/Attributes/Get.php (1)
__construct(29-36)src/Routing/Attributes/Post.php (1)
__construct(26-33)src/Routing/Attributes/Put.php (1)
__construct(26-33)src/Routing/Attributes/Route.php (1)
__construct(32-39)src/Routing/Attributes/RouteGroup.php (1)
__construct(36-41)
src/Routing/Attributes/Get.php (4)
src/Routing/Attributes/Post.php (2)
Attribute(18-34)__construct(26-33)src/Routing/Attributes/Put.php (2)
Attribute(18-34)__construct(26-33)src/Routing/Attributes/Route.php (2)
Attribute(23-72)__construct(32-39)src/Routing/RouteDefinition.php (1)
__construct(22-31)
src/Routing/Attributes/RouteGroup.php (3)
src/Routing/Attributes/Route.php (3)
Attribute(23-72)__construct(32-39)getFilters(68-71)tests/unit/RouteScannerTest.php (1)
RouteGroup(12-23)src/Routing/RouteDefinition.php (1)
__construct(22-31)
tests/unit/RouteScannerTest.php (2)
src/Routing/RouteScanner.php (4)
RouteScanner(18-191)scanClass(29-64)clearCache(187-190)scanClasses(73-84)src/Routing/RouteDefinition.php (1)
getControllerMethod(38-41)
src/Routing/Attributes/Put.php (5)
src/Routing/Attributes/Delete.php (2)
Attribute(18-34)__construct(26-33)src/Routing/Attributes/Get.php (2)
Attribute(21-37)__construct(29-36)src/Routing/Attributes/Post.php (2)
Attribute(18-34)__construct(26-33)src/Routing/Attributes/Route.php (2)
Attribute(23-72)__construct(32-39)src/Routing/Route.php (1)
Route(8-64)
src/Routing/Attributes/Post.php (2)
src/Routing/Attributes/Get.php (2)
Attribute(21-37)__construct(29-36)src/Routing/Attributes/Route.php (2)
Attribute(23-72)__construct(32-39)
src/Routing/RouteScanner.php (4)
tests/unit/RouteScannerTest.php (1)
RouteGroup(12-23)src/Routing/Attributes/Route.php (4)
getPath(44-47)getFilters(68-71)getMethod(52-55)getName(60-63)src/Routing/Attributes/RouteGroup.php (3)
getFilters(54-57)applyPrefix(65-79)mergeFilters(87-90)src/Routing/RouteDefinition.php (1)
RouteDefinition(12-42)
src/Routing/Attributes/Route.php (6)
src/Routing/Attributes/Delete.php (2)
Attribute(18-34)__construct(26-33)src/Routing/Attributes/Get.php (2)
Attribute(21-37)__construct(29-36)src/Routing/Attributes/Post.php (2)
Attribute(18-34)__construct(26-33)src/Routing/Attributes/Put.php (2)
Attribute(18-34)__construct(26-33)src/Routing/Attributes/RouteGroup.php (3)
Attribute(29-91)__construct(36-41)getFilters(54-57)src/Routing/RouteDefinition.php (1)
__construct(22-31)
🪛 markdownlint-cli2 (0.18.1)
README.md
92-92: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
107-107: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
134-134: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
146-146: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
164-164: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
179-179: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
218-218: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
227-227: Code block style
Expected: indented; Actual: fenced
(MD046, code-block-style)
🔇 Additional comments (11)
README.md (1)
84-236: Well-structured documentation for the new attribute-based routing feature.The documentation is comprehensive, covering usage patterns, configuration, migration path, and performance considerations. The examples align with the actual implementation in the codebase.
The static analysis hints about code block style (fenced vs indented) can be safely ignored—fenced code blocks are more readable for these multi-line examples and are consistent with the existing documentation style in this file.
src/Routing/Attributes/Delete.php (1)
1-34: LGTM!The
Deleteattribute follows the same pattern as the other HTTP method attributes (Get,Post,Put), properly extendingRouteand delegating to the parent constructor with the correct HTTP method.src/Routing/Attributes/Get.php (1)
1-37: LGTM!The
Getattribute is well-implemented with clear documentation and usage examples. The repeatable attribute pattern allows multiple routes on a single method, which is a useful feature for API versioning.src/Routing/Attributes/Put.php (1)
1-34: LGTM!The
Putattribute is implemented consistently with the other HTTP method attributes.src/Routing/RouteDefinition.php (1)
1-42: Clean immutable data class.The use of constructor property promotion with
readonlyis idiomatic PHP 8.1+. ThegetControllerMethod()helper provides a convenient format for route registration.src/Routing/Attributes/Post.php (1)
1-34: LGTM!The
Postattribute follows the established pattern consistently.src/Routing/Attributes/Route.php (1)
23-72: Well-designed base attribute class.The Route class provides a clean foundation for HTTP method attributes. The
getMethod()normalization to uppercase ensures consistency. The readonly properties with getter methods provide good encapsulation while maintaining immutability.Note: The class accepts any HTTP method string, which provides flexibility for adding
PATCH,OPTIONS, orHEADattributes in the future without modifying the base class.tests/unit/RouteScannerTest.php (1)
11-39: Test controllers are well-structured for validating RouteScanner behavior.The inline test controllers provide good coverage for:
RouteGroupwith prefix and filters- Controllers without
RouteGroup- Multiple routes on a single method
These cover the key scenarios in the attribute-based routing system.
src/Routing/Attributes/RouteGroup.php (1)
29-41: Clean use of PHP 8 promoted properties.The attribute definition and constructor leverage modern PHP 8 features appropriately with readonly promoted properties and sensible defaults.
src/Routing/RouteScanner.php (2)
29-64: Well-structured class scanning with caching.Good defensive programming with the
class_existscheck and effective caching to prevent redundant scanning. The implementation correctly handles class-levelRouteGroupattributes and iterates only over public methods.
113-150: Correct use ofIS_INSTANCEOFfor polymorphic attribute matching.The use of
\ReflectionAttribute::IS_INSTANCEOFcorrectly captures all Route subclasses (Get, Post, Put, Delete), and the RouteGroup integration properly applies prefixes and merges filters.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…to feature/attributes
Summary by CodeRabbit
New Features
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.