PHP 8 : Les attributs

Résumé Support

Dans cette vidéo je vous propose de découvrir une des nouvelles fonctionnalités de PHP 8 : Les attributs.

Des annotations mais natives

Le concept des attributs n'est pas nouveau et si vous travaillez avec doctrine ou symfony vous l'avez déjà surement rencontré sous une autre forme : les annotations.

/** * @Route("/demo") **/ function demo () { return 'Bonjour tout le monde !'; }

Le principe est d'ajouter une métadonnée à une méthode, une propriété, une fonction ou une classe. Le problème était que jusqu'à maintenant il n'existait pas de manière native de gérer les choses et on devait avoir recours à une librairie tierce. PHP 8 introduit donc une nouvelle syntaxe et une nouvelle terminologie Attribut (pour ne pas confondre avec les annotations).

#[Route("/demo")] function demo () { return 'Bonjour tout le monde !'; }

La syntaxe est différente, mais le principe est le même.

Créer un attribut

Pour créer un attribut il suffit de créer une classe qui va elle-même avoir l'attribut Attribute. Cet attribut peut recevoir en paramètre le type de contenu qui pourra recevoir l'attribut.

<?php namespace App\Attribute; use Attribute; #[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_CLASS)] class Route { public function __construct ( private string $path, private string $method = 'get' ) { } public function getPath(): string { return $this->path; } public function getMethod(): string { return $this->method; } }

Vous pourrez ensuite utiliser votre attribut où vous le souhaitez.

#[Route('/api')] class HelloController { #[Route('/hello/{name}')] public function hello (string $name) { return <<<HTML Hello {$name} <form method="post" action="/api/goodbye/{$name}"> <button>Envoyer</button> </form> HTML; } #[Route('/goodbye/{name}', method: 'post')] public function goodbye (Response $response, string $name) { $response->getBody()->write("Goodbye, $name"); return $response; } }

Enfin, vous pourrez lire les attributs en utilisant l'API de réflexion de PHP grâce à la méthode getAttributes().

$class = new ReflectionClass(HelloController::class); $routeAttributes = $class->getAttributes(\App\Attribute\Route::class);

Vous obtiendrez alors un tableau de ReflectionAttribute contenant le nom de vos attributs ainsi que les arguments à lui passer. On notera surtout la possibilité de récupérer une instance de notre attribut à l'aide de la méthode newInstance().

$class = new ReflectionClass(HelloController::class); $routeAttributes = $class->getAttributes(\App\Attribute\Route::class); if (!empty($routeAttributes)) { // On a ici notre instance Route construite avec les bons arguments $route = $routeAttributes[0]->newInstance(); }

À partir de là c'est à vous d'imaginer les utilisations possibles.

Quelques pistes d'utilisation

Maintenant que l'on a vu le principe, on peut se poser la question des cas d'utilisation d'une telle fonctionnalité. Aussi voici quelques pistes qui peuvent être explorées.

EventListener

Pour un écouteur d'évènement, les annotations peuvent permettre de définir à quel évènement répond une méthode précise.

class CommentEventListener { #[SubscribeTo(CommentCreatedEvent::class)] public function onCreated (CommentCreatedEvent $event) { } #[ SubscribeTo(CommentDeletedEvent::class), SubscribeTo(CommentBlacklisted::class) ] public function onDelete (CommentCreatedEvent $event) { } }

Un système de commande

On peut l'utiliser pour enregistrer automatiquement des commandes.

class ServerCommand { #[Command("app:run", arguments: ['port', 'host'])] public function build (IO $io, array $args) { } }

La validation des données

class Post { #[ Validation\Unique, Validation\Email, ] private string $email; #[ Validation\NotBlank, Validation\Length(min: 5, max: 12), ] private string $username; }

La connexion avec la base de données

class Post { #[ORM\Column(name: 'email', type: 'VARCHAR', length: 255)] private string $email; }