D’après la documentation officielle du PHP RFC sur les attributs, cette syntaxe permet d’attacher des métadonnées structurées directement à une classe, une méthode ou une propriété, sans passer par des commentaires de documentation analysés au moment de l’exécution. Pour une extension WooCommerce qui expose plusieurs points de terminaison REST personnalisés, cette fonctionnalité de PHP 8.0 ouvre une alternative nette au tableau de configuration habituellement utilisé pour décrire chaque route avant de l’enregistrer via register_rest_route().
Ce billet montre comment migrer une déclaration de routes REST basée sur un tableau vers une syntaxe à base d’attributs, en conservant exactement le même comportement final côté register_rest_route().
Le tableau de configuration classique
Avant la migration, une extension déclare généralement ses routes dans un tableau associatif, relu à chaque appel de rest_api_init :
$routes = [
[
'route' => '/commandes/(?P<id>\d+)/statut',
'methods' => 'GET',
'callback' => [ $this, 'get_statut_commande' ],
],
];
foreach ( $routes as $route ) {
register_rest_route( 'mon-extension/v1', $route['route'], [
'methods' => $route['methods'],
'callback' => $route['callback'],
] );
}
La même déclaration avec des attributs PHP 8

Un attribut personnalisé se déclare comme une classe ordinaire, préfixée de #[Attribute], puis s’applique directement au-dessus de la méthode concernée :
#[Attribute]
class RouteRest {
public function __construct(
public string $route,
public string $methodes = 'GET'
) {}
}
class ControleurCommandes {
#[RouteRest( route: '/commandes/(?P<id>\d+)/statut', methodes: 'GET' )]
public function get_statut_commande( WP_REST_Request $request ) {
// logique inchangée
}
}
La classe RouteRest ne fait rien par elle-même : un attribut est une donnée statique attachée au code, lue via la Reflection API au moment de l’enregistrement des routes, sur le hook rest_api_init :
add_action( 'rest_api_init', function () {
$reflection = new ReflectionClass( ControleurCommandes::class );
foreach ( $reflection->getMethods() as $methode ) {
foreach ( $methode->getAttributes( RouteRest::class ) as $attribut ) {
$config = $attribut->newInstance();
register_rest_route( 'mon-extension/v1', $config->route, [
'methods' => $config->methodes,
'callback' => [ new ControleurCommandes(), $methode->getName() ],
] );
}
}
} );
Ce que ce changement apporte vraiment
- La déclaration de route reste physiquement collée à la méthode qu’elle concerne, plutôt que dans un tableau séparé à maintenir en parallèle
- Un attribut se valide à la compilation dans son typage de constructeur, contrairement à une clé de tableau qui peut être mal orthographiée sans erreur immédiate
- La Reflection API permet d’introspecter automatiquement l’ensemble des routes déclarées, utile pour générer une documentation interne
Ce que cette migration ne couvre pas
La sécurisation du permission_callback de chaque route reste un sujet distinct, déjà traité par ailleurs sur ce blog : le passage aux attributs ne change rien à la nécessité de définir une politique d’autorisation explicite pour chaque point de terminaison exposé.
Sur les extensions que nous modernisons vers PHP 8, ce refactoring est souvent l’un des premiers appliqués : le gain de lisibilité est immédiat, et le risque de régression reste très faible puisque le comportement de
register_rest_route()n’est pas modifié.
Un attribut peut porter plusieurs paramètres de validation
Rien n’empêche d’enrichir l’attribut avec des paramètres supplémentaires, comme un schéma d’arguments attendu, pour rapprocher encore la déclaration de route de sa documentation. La limite reste la même que pour n’importe quelle métadonnée : l’attribut décrit une intention, il revient toujours au code d’enregistrement, exécuté sur rest_api_init, de la traduire en appel réel à register_rest_route().
En résumé
Les attributs PHP 8 ne remplacent pas la logique d’enregistrement REST de WordPress, ils en modernisent seulement la déclaration. Pour une extension qui accumule plusieurs dizaines de routes, ce changement de forme améliore sensiblement la maintenabilité, sans toucher au comportement observable par les clients de l’API.