Les nouveautés de PHP 8 : les attributs
La nouvelle release de PHP, la version 8, apporte beaucoup de nouveautés au langage. Parmi celles-ci, nous avons déjà découvert les arguments nommés et les propriétés promues. Dans cet article, nous allons explorer les attributs de PHP 8.
Dans cette série d’articles, je reviens avec vous sur les nouveautés de la dernière version de PHP. Découvrons ensemble ce que la version 8 de PHP à de nouveau à nous apporter et comment cela pourrait nous être utile.
Si vous avez déjà travaillé avec Doctrine, Symfony ou un autre projet PHP populaire, vous connaissez sûrement les annotations. Il s'agit d'une réponse de la communauté à un manque du langage. En utilisant la syntaxe de PHPDoc, elle a permis aux projets PHP d'utiliser ce qui s'apparente dans d'autres langages à « des décorateurs ».
Dans PHP 8, les développeurs ont trouvé le moyen d'ajouter les annotations à la syntaxe native du langage.
Les attributs.
Il est possible d'ajouter des attributs aux fonctions, aux paramètres, aux classes, aux méthodes, aux propriétés et aux constantes de classes pour leur attribuer des méta-données qui seront lues et utilisées au moment du runtime.
La syntaxe des attributs est la suivante :
À quoi servent les attributs de PHP 8 ?
Encore une fois, les attributs serviront à peu près dans les mêmes situations que les annotations PHPDocs. Mais, rentrons un peu plus dans les détails.
Les attributs servent à ajouter des méta-données à différent symbole de notre code. Par exemple dans la déclaration de classe suivante :
Nous attachons à la méthode produtListView
une méta-donnée indiquant qu'elle est obsolète et qu'il faut éviter de l'utiliser.
Toujours dans le cas d'un contrôleur, voici un nouvel exemple :
La méta-donnée que l'on rajoute ici indique que homeAction
est l'action qui va rendre la vue de la page d'accueil.
Donc avec les attributs, nous pouvons ajouter des données structurées a nos symboles sans pour autant les coupler à l'implémentation du symbole en question ou même l'implémentation de l'attribut lui-même.
L'implémentation générique d'une fonctionnalité et son utilisation réelle dans l'application peuvent être découplé grâce aux attributs de PHP 8.
Je m'explique. L'attribut Route
est attaché sur homeAction
. Mais, ce n'est pas au moment où homeAction
est exécuté que notre attribut est utilisé. Cela n'aurait pas de sens. Dans ce cas, homeAction
ne serait jamais exécuté puisque la route ne serait jamais définie.
De même, ce n'est pas au moment de l'implémentation de l'attribut Route
qu'il est utilisé. À ce moment-là, nous ignorons encore quelle action il concerne. En cela, d'ailleurs, il rejoint la définition classique des classes et des interfaces. Vous comprendrez sûrement pourquoi dans la suite de l'article.
Comment créer vos propres attributs avec PHP 8 ?
Nous allons essayer d'implémenter un système de validation élémentaire pour des POPO (Plain Old PHP Object). Pour cela, nous avons besoin d'un attribut MaxLength
qui représentera une de nos règles de validation.
Les lecteurs premium du blog ont accès aux sources complètes, fonctionnelles et commentées de l'article. Elles contiennent également plus d'exemples de cas d'utilisation.
Il s'agit simplement d'une classe MaxLength
sur laquelle on utilise l'attribut Attribute
. Nous utilisons le constructeur de la classe pour indiquer les paramètres dont aura besoin l'attribut. Si vous n'êtes pas sûr de la syntaxe utilisée ici, je vous invite à lire mon article sur les propriétés promues en PHP 8.
Nous allons utiliser ce nouvel attribut de validation sur un objet User
très simple qui représente un utilisateur dans notre modèle.
En utilisant l'attribut, nous définissons une règle sur le modèle User
: les adresses e-mails des utilisateurs ne doivent pas dépasser 10 caractères. Très simple à comprendre, à mettre en place et à réutiliser.
Par contre, pour l'instant, il ne se passe rien. Pour que l'attribut MaxLength
vérifie réellement la taille maximal d'une propriété, il va falloir lui dire comment faire. Ajoutons donc une méthode validate()
à notre attribut qui implémentera la règle de validation.
Ensuite, il nous faut coder la partie qui va utiliser nos règles de validation. Je vous propose de créer un objet Validator
qui se chargera d'appliquer les règles de validations sur chaque objet que nous lui passons.
La méthode validate()
de l'objet Validator
prend en paramètre un objet à valider. Grâce à la Reflection API, il parcourt la liste de ses propriétés à la recherche des attributs de validations. En l'occurrence, MaxLength
que nous venons de créer. Il applique ensuite la règle à la valeur de la propriété.
Plusieurs choses intéressantes ici : on accède aux attributs grâce à la méthode getAttributes()
de la propriété, mais cette méthode existe aussi sur les autres symboles (classe, fonction/méthodes, etc). On peut préciser en paramètre de cette même méthode quel attribut nous recherchons en lui passant la classe de l'attribut comme ceci : getAttributes(MaxLength::class)
.
Il est maintenant possible d'appeler validator->validate(obj: $objetATester);
et il jouera toutes les règles de validation sur l'objet passé en paramètre. Si vous ne reconnaissez pas cette syntaxe, je vous invite à lire mon article sur les arguments nommés, une autre nouveauté de PHP 8.
Bien-sûr, dans notre exemple, nous n'avons créé qu'un seul attribut de validation (MaxLength
) mais il est maintenant simple d'en créer de nouveaux et de faire en sorte que Validator
les prennent en compte.
Comme tout est découplé, vous n'avez pas besoin de toucher au code existant pour ajouter de nouvelles fonctionnalités. C'est essentiellement pour cela que j'apprécie cette fonctionnalité de PHP 8.