Modern PHP Features
Thème :
Junior
1 vues ·11/21 leçonsConstruire un Routeur Basé sur les Attributs
Les attributs permettent un routage déclaratif similaire aux frameworks comme Symfony ou Laravel. Construisons un système de routage complet.
Définition de l’Attribut Route
<?php
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
class Route
{
public function __construct(
public string $path,
public string $method = 'GET',
public ?string $name = null,
public array $middleware = []
) {}
}
#[Attribute(Attribute::TARGET_CLASS)]
class Controller
{
public function __construct(
public string $prefix = ''
) {}
}
#[Attribute(Attribute::TARGET_METHOD)]
class Middleware
{
public function __construct(
public string $name
) {}
}
Définition du Contrôleur
<?php
#[Controller('/api/users')]
class UserController
{
#[Route('/', 'GET', name: 'users.index')]
#[Middleware('auth')]
public function index(): array
{
return ['users' => []];
}
#[Route('/{id}', 'GET', name: 'users.show')]
#[Middleware('auth')]
public function show(int $id): array
{
return ['user' => ['id' => $id]];
}
#[Route('/', 'POST', name: 'users.create')]
#[Middleware('auth')]
#[Middleware('admin')]
public function create(): array
{
return ['created' => true];
}
}
La Collection de Routes
<?php
class RouteDefinition
{
public function __construct(
public string $path,
public string $method,
public string $controller,
public string $action,
public ?string $name,
public array $middleware
) {}
public function matches(string $method, string $path): ?array
{
if ($this->method !== $method) {
return null;
}
// Convertir {param} en regex
$pattern = preg_replace('/\{(\w+)\}/', '(?P<$1>[^/]+)', $this->path);
$pattern = '#^' . $pattern . '$#';
if (preg_match($pattern, $path, $matches)) {
return array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
}
return null;
}
}
L’Implémentation du Routeur
<?php
class AttributeRouter
{
/** @var RouteDefinition[] */
private array $routes = [];
public function registerController(string $className): void
{
$reflection = new ReflectionClass($className);
// Obtenir le préfixe du contrôleur
$prefix = '';
$controllerAttrs = $reflection->getAttributes(Controller::class);
if (!empty($controllerAttrs)) {
$prefix = $controllerAttrs[0]->newInstance()->prefix;
}
// Enregistrer chaque méthode de route
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
$routeAttrs = $method->getAttributes(Route::class);
foreach ($routeAttrs as $attr) {
$route = $attr->newInstance();
// Collecter les middlewares
$middleware = $route->middleware;
foreach ($method->getAttributes(Middleware::class) as $mwAttr) {
$middleware[] = $mwAttr->newInstance()->name;
}
$this->routes[] = new RouteDefinition(
path: $prefix . $route->path,
method: $route->method,
controller: $className,
action: $method->getName(),
name: $route->name,
middleware: $middleware
);
}
}
}
public function dispatch(string $method, string $uri): mixed
{
foreach ($this->routes as $route) {
$params = $route->matches($method, $uri);
if ($params !== null) {
// Exécuter les middlewares
foreach ($route->middleware as $mw) {
$this->runMiddleware($mw);
}
// Appeler l'action du contrôleur
$controller = new ($route->controller)();
return $controller->{$route->action}(...$params);
}
}
throw new NotFoundException('Route introuvable');
}
}
// Utilisation
$router = new AttributeRouter();
$router->registerController(UserController::class);
$result = $router->dispatch('GET', '/api/users/123');
// Appelle UserController::show(123)
Les Grimoires
📘 Cette leçon fait partie du cours PHP 8.x Moderne : Les Dernières Fonctionnalités du Langage sur la plateforme d’apprentissage RostoDev.
Prêt à passer à la suite ?
Vous avez terminé cette leçon sur Construire un Routeur Basé sur les Attributs. Continuez votre apprentissage avec la prochaine leçon ou explorez d'autres formations.