Repository navigation
Releases: debuss/attribute-routing
Releases · debuss/attribute-routing
Release list
2.0.0
2.0.0
This major release redesigns the attributes to follow common PHP conventions, makes route discovery stricter and fixes several bugs of the route loader. It contains breaking changes: see the upgrade guide below.
⚠️ Breaking changes
Attributes renamed
| 1.x | 2.0 |
|---|---|
#[Controller] |
#[AsController] |
#[ApiController] |
removed |
#[BaseController] |
removed |
#[HttpGet] |
#[Get] |
#[HttpPost] |
#[Post] |
#[HttpPut] |
#[Put] |
#[HttpPatch] |
#[Patch] |
#[HttpDelete] |
#[Delete] |
#[HttpHead] |
#[Head] |
#[HttpOptions] |
#[Options] |
HttpMethod (abstract) |
#[Route] (concrete) |
Controllerconflicted with the baseControllerclass provided by many frameworks: importing both in the same namespace made controllers silently extend the attribute.AsControllerfollows theAs*naming convention used by Symfony.ApiControllerandBaseControllerwere plain aliases.BaseControlleralso conflicted with CodeIgniter's base class. Create your own attribute extendingAsControllerinstead (see below).- The
Http*prefix came from ASP.NET; short names (Get,Post, …) are the common convention in PHP libraries.
Stricter route discovery
#[AsController]is now required. Classes without#[AsController](or an attribute extending it) are ignored, even if their methods have route attributes.#[AsController]on an interface, a trait, an enum or an abstract class throws aLogicException, as only concrete classes can be controllers. PHP attributes are not inherited: each concrete controller must have its own#[AsController]. Routes of abstract parents and traits are still inherited by the concrete controllers extending or using them, with the prefix of the concrete controller.- A route attribute on a non-public method throws a
LogicException, as no dispatcher can call it. AttributeRouteLoaderthrows anInvalidArgumentExceptionwhen the given path is not an existing directory (instead of anUnexpectedValueExceptionfromRecursiveDirectoryIterator).
Attributes API
Route(formerlyHttpMethod) constructor signature is nowRoute(string|array $methods, string $path = '', string $name = '', int $priority = 0).- HTTP methods are normalized to uppercase (
'get'becomes'GET'), and an empty list of methods throws anInvalidArgumentException. - The properties of
AsControllerandRouteare nowreadonly.
✨ New features
#[Route]for several HTTP methods on the same action:#[Route(['GET', 'POST'], '/search', name: 'users.search')]
- Optional path:
#[Get(name: 'users.index')]on a controller prefixed with/usersmaps to/users. - Custom attributes:
AsControllerandRoutecan be extended, for instance to define a controller attribute with a predefined prefix:#[Attribute(Attribute::TARGET_CLASS)] class AsApiController extends AsController { public function __construct(int $priority = 0) { parent::__construct('/api', $priority); } }
🐛 Bug fixes
- The priority of a controller leaked to the routes of the classes loaded after it when they had no controller attribute.
- Routes defined on
privateandprotectedmethods were registered. - Routes defined in abstract classes and enums were registered with a non-instantiable handler.
- The class name was built with
str_replace()on the whole file path, which could alter paths containing the base directory or.phpmore than once.
📚 Documentation
- New sections: discovery rules, route priority, name conflicts with frameworks, custom controller attributes.
🧰 Tooling
declare(strict_types=1)in every file.- PHPUnit test suite (100% line coverage):
composer test. - PHPStan at level max:
composer analyse. - GitHub Actions CI running
composer validate, PHPStan and PHPUnit on PHP 8.3, 8.4 and 8.5.
⬆️ Upgrade guide
- Replace the imports and attributes following the table above:
-use Routing\Attribute\{Controller, HttpGet, HttpPost}; +use Routing\Attribute\{AsController, Get, Post}; -#[Controller(prefix: '/users')] +#[AsController(prefix: '/users')] class UserController { - #[HttpGet('/{id}', name: 'users.show')] + #[Get('/{id}', name: 'users.show')] public function show(int $id): void {} }
- Replace
#[ApiController]and#[BaseController]with#[AsController], or with your own attribute extendingAsController. - Add
#[AsController]to every controller that did not have a controller attribute, otherwise its routes will no longer be loaded. - Make public the methods holding route attributes, or remove their attributes.
- Move
#[AsController]from abstract classes to their concrete controllers. - If you extended
HttpMethod, extendRouteinstead and update the call to the parent constructor:parent::__construct($methods, $path, $name, $priority). - If a short attribute name conflicts with a class of your application (e.g.
Options), import the namespace with an alias:use Routing\Attribute as Http; #[Http\Options('/users')]
Full Changelog: 1.1.0...2.0.0
1.1.0
1.0.0
Full Changelog: https://github.com/debuss/attribute-routing/commits/1.0.0