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;
}