PHP 8 : Les attributs

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 tiers. 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. Cette 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éflection de PHP gràce à la méthode getAttributes().

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

Vous obtiendrais alors un tableau de ReflectionAttribute contenant le nom de vos attributs ainsi que les arguments à lui passer. On notera surtout la possibiliter 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 construit avec les bons arguments
    $route = $routeAttributes[0]->newInstance();
}

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

}
Publié
Technologies utilisées
Auteur :
Grafikart
Partager