PHP 8 : Les attributs

Voir la vidéo

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;

}
Publié
Technologies utilisées
Auteur :
Grafikart
Partager