Couverture de l'article Gagner en performance et confort de développement : aperçu des nouveautés dans Symfony 6.3
Retour aux articles

L'agence

WanadevStudio

Gagner en performance et confort de développement : aperçu des nouveautés dans Symfony 6.3

Suite à la migration d'un de nos projets en version Symfony 6.3 nous avons voulu partager les nouveautés qui ont le plus marqué notre attention.

Avec Gauthier nous avons pris le temps de rédiger cet article qui s'adresse aux développeurs voulant bénéficier des dernières nouveautés, des optimisations de performance ainsi qu'être à jour sur les dernières normes de sécurité.

Composant Clock : faciliter la gestion de date

Ce composant permet de gérer les dates de manière centralisée dans le projet, pour par exemple les configurer sur un fuseau horaire d'un pays de votre choix. L'utiliser c'est un gain de temps, et un moyen simple de ne pas se planter sur de la gestion de date.

Il est maintenant possible d’utiliser le composant Clock en l’injectant via l’interface ClockInterface ou en utilisant le trait ClockAwareTrait.

Source: https://symfony.com/blog/new-in-symfony-6-3-clock-improvements

Composant Scheduler : se simplifier la gestion de tâches récurrentes

Pour gérer des tâches récurrentes, il n'est plus nécessaire de passer par un Cron configuré sur le serveur, il suffit d'utiliser le nouveau composant Scheduler:

#[AsSchedule('default')]
class DefaultScheduleProvider implements ScheduleProviderInterface
{
    public function getSchedule(): Schedule
    {
        return (new Schedule())->add(
            RecurringMessage::every('2 days', new PendingOrdersMessage())
        );
    }
}

Ce nouveau système s’appuie sur le composant Messenger, il faut donc créer le message PendingOrdersMessage et son handler associé, sans oublier de démarrer le consumer qui se chargera à la fois de générer et consommer les tâches planifiées :

symfony console messenger:consume -v scheduler_default

Composant Notifier : diffuser ses données sur le web

Le composant Notifier gère de nouvelles intégration, dont Twitter & Mastodon pour les plus connues, pour retrouver la liste complète c’est ici: https://symfony.com/blog/new-in-symfony-6-3-notifier-integrations

Composant Webhook : réagir aux évenements externes

Un Webhook permet de récupérer les données d’un service tiers en lui fournissant une URL qu’il va requêter à chaque fois que l’état de ses données aura changé.

Le nouveau composant Webhook permet de connecter son application Symfony à des Webhook tierces (github, gitlab etc…) en étendant la classe abstraite AbstractRequestParser qui permet de valider la provenance et le format de la requête entrante.

Exemple de parser:

final class MailerWebhookParser extends AbstractRequestParser
{
    protected function getRequestMatcher(): RequestMatcherInterface
    {
        // these define the conditions that the incoming webhook request
        // must match in order to be handled by this parser
        return new ChainRequestMatcher([
            new HostRequestMatcher('github.com'),
            new IsJsonRequestMatcher(),
            new MethodRequestMatcher('POST'),
        ]);
    }

    protected function doParse(Request $request, string $secret): ?RemoteEvent
    {
        // in this method you check the request payload to see if it contains
        // the needed information to process this webhook
        $content = $request->toArray();
        if (!isset($content['signature']['token'])) {
            throw new RejectWebhookException(406, 'Payload is malformed.');
        }

        // you can either return `null` or a `RemoteEvent` object
        return new RemoteEvent('mailer_callback.event', 'event-id', $content);
    }
}

Le consumer qui se chargera d'exécuter notre logique:

#[AsRemoteEventConsumer(name: 'mailer_callback.event')]
class MailerCallbackEventConsumer
{
    public function consume(RemoteEvent $event): void
    {
        // Process the event returned by our parser
    }
}

La bonne nouvelle c’est qu’en plus de ça, Symfony à standardisé la manière de gérer les WebHooks pour des providers de type Mailer et Notifier, ce qui présente 2 avantages:

  • on ne se focalise plus que sur notre consumer
  • si on change de provider (par exemple si l'on migre de Mailgun à Mailchimp), il suffit de modifier une ligne dans la configuration du projet

Exemple de consumer pour le Mailer:

#[AsRemoteEventConsumer(name: 'emails')]
class MailerEventConsumer implements ConsumerInterface
{
    public function consume(Event $event): void
    {
        $email = $event->getRecipientEmail();

        error_log(match ($event->getName()) {
            MailerDeliveryEvent::BOUNCE => sprintf('Email to %s bounced (%s)', $email, $event->getReason()),
            MailerEngagementEvent::UNSUBSCRIBE => sprintf('Unsubscribe from %s', $email),
            default => sprintf('Receive unhandled email event %s', $event->getName()),
        });
    }
}

Configuration:

framework:
    webhook:
        routing:
            emails:
                service: '...' // Pour le moment Symfony supporte Mailgun, Postmark et Twilio
                secret: '%env(MAILGUN_WEBHOOK_SECRET)%'

Et si votre provider n’est pas encore géré nativement par Symfony, c’est l’occasion parfaite pour contribuer au projet :-)

Sources:

Composant HttpClient : interagir avec d'autres services web

L’interface HttpClientInterface expose la nouvelle méthode crypto_method() qui permet de définir la version minimum de TLS utilisé dans nos requêtes, Symfony configure par défaut la version à TLS 1.2 minimum.

Il est également possible de définir des urls de fallback lors d’une requête:

$response = $client->request('GET', 'foo-bar', [
    'base_uri' => [
        'http://a.example.com/', // first request will use this base URI
        'http://b.example.com/', // if first request fails, the second base URI will be used
    ],
]);

Pour consulter la liste complète des améliorations: https://symfony.com/blog/new-in-symfony-6-3-httpclient-improvements

Exceptions HTTP

On peut maintenant déclarer les exceptions HTTP via un attribut, plutôt que de les définir dans un fichier de config exception.yaml.

#[WithHttpStatus(Response::HTTP_NOT_FOUND, headers: ['x-header' => 'foo'])]
#[WithLogLevel(LogLevel::DEBUG)]
class OrderNotFound extends \Exception

Source: https://symfony.com/blog/new-in-symfony-6-3-http-exception-attributes

Early Hints : optimisez le temps de chargement de vos pages

Permet au navigateur de commencer à télécharger les assets (js, css & fonts) avant même que le serveur ne renvoie le contenu HTML de la page pour réduire le temps d’affichage d’une page web.

#[Route("/", name: "homepage")]
public function index(): Response
{
    $response = $this->sendEarlyHints([
        new Link(rel: 'preconnect', href: 'https://fonts.google.com'),
        (new Link(href: '/main.css'))->withAttribute('as', 'stylesheet'),
        (new Link(href: '/app.js'))->withAttribute('as', 'script'),
    ]);

    // prepare the contents of the response...
    return $this->render('homepage/index.html.twig', response: $response);
}

Source: https://symfony.com/blog/new-in-symfony-6-3-early-hints

Clarifier son code avec une meilleure gestion des payloads

Grâce à l’attribut MapRequestPayload, on peut dorénavant mapper le contenu d’une requête directement dans un DTO, tout en gardant le contrôle sur les règles de validation. Le contrôleur renverra une 400 si le contenu de la requête est erroné, et une 422 si les règles de validation n’ont pas été respectées.

public function __invoke(
    #[MapRequestPayload] ProductReviewDto $productReview,
): Response {
    // here, $productReview is a fully typed representation of the request data
}

Il existe également la version MapQueryString dans le cas d’une requête GET (pratique pour la gestion des filtres):

// example: url?filterA=foo&filterB=bar
public function __invoke(
    #[MapQueryString] FilterQueryDto $query,
): Response {
    // here, $query is a fully typed representation of the request data
}

Source: https://symfony.com/blog/new-in-symfony-6-3-mapping-request-data-to-typed-objects

Injection de dépendances

L'attribut Autowire à de nouvelles options pour gérer les variables d'environnements, les paramètres ainsi que le lazy loading:

public function __construct(
    #[Autowire(param: 'kernel.debug')]
    bool $debugMode,

    #[Autowire(env: 'SOME_ENV_VAR')]
    string $senderName,

    #[Autowire(lazy: true)]
    MyServiceInterface $service,

    #[Autowire(lazy: FirstServiceInterface::class)]
    FirstServiceInterface|SecondServiceInterface $service
    ) {
}

On peut même aller plus loin, en créant nos propres attributs d’injection personnalisés:

// Création de notre attribut
class Repository extends Autowire
{
    public function __construct(string $class)
    {
        parent::__construct(expression: \sprintf("service('some.repository.factory').create('%s')", $class));
    }
}

// Utilisation de l'attribut
/**
 * @param ObjectRepository<User> $repository
 */
public function __construct(
    #[Repository(User::class)] private ObjectRepository $repository
) {
}

Pour voir la liste complète des nouveaux concernant l'injection de dépendances c’est par ici: https://symfony.com/blog/new-in-symfony-6-3-dependency-injection-improvements

Nouvelles contraintes : valider ses données

La contrainte NoSuspiciousCharacters permet de détecter la présence de caractères invisibles ou impossible à différencier, comme par exemple la lettre s en latin et la lettre s en cyrillique. (faille pouvant être exploitée dans des attaques IDN homograph pour du phishing).

La contrainte PasswordStrengthValidator permet de valider la sécurité des mots de passe en proposant 5 niveaux de complexité.

Sources:

OpenID Connect : authentification décentralisée

Il est maintenant possible de se connecter à un serveur OIDC (OpenID Connect) pour assurer l’authentification de son application API Restful, pour plus d’informations: https://symfony.com/blog/new-in-symfony-6-3-openid-connect-token-handler

Pour rappel, OIDC est la 3ème génération du système OpenID, c’est un système d’authentification qui s’appuie sur le système d'autorisation OAuth 2.0 et c’est aujourd’hui la norme en terme de sécurité concernant l’authentification décentralisée. (par exemple, c'est ce qui est utilisé quand vous essayez de vous connecter sur plusieurs sites en utilisant votre compte GAFAM).

Conclusion

Cette mise à jour de Symfony propose également une meilleure gestion des enums, une nouvelle commande de debug pour les serializer, des améliorations de performance, ainsi qu'une meilleure qualité de vie pour les développeurs (part1 part2 part3).

Une sacrée mise à jour avec plein de nouveautés que ce soit au niveau des composants (nouveaux ou existants), des performances ou même de la qualité de vie pour nous les développeurs !

Pour en savoir plus, retrouvez toutes les informations sur le site officiel de Symfony et suivez nous sur les réseaux sociaux.

Commentaires

Il n'y a actuellement aucun commentaire. Soyez le premier !

  • Couverture de l'article Event Bus : Le secret d'une architecture Symfony réellement découplée
    Event Bus : Le secret d'une architecture Symfony réellement découplée

    Il y a 1 mois

    Imaginez : votre utilisateur clique sur "Commander". En coulisses, le domaine Stock doit décrémenter les quantités, le domaine Facturation doit générer une facture, et le domaine Notification doit envoyer un email de confirmation. Trois domaines, une seule action... et un spaghetti de dépendances en perspective. 🍝

    Et si ces domaines pouvaient collaborer sans jamais se connaître ?

    C'est exactement ce que permet l'Event Bus. Mais avant de foncer tête baissée, une question se pose : Symfony propose déjà l'EventDispatcher pour gérer les événements. Alors pourquoi introduire un nouveau concept ?

    Spoiler : ce ne sont pas les mêmes outils, et les confondre peut vous coûter cher.

    Dans cet article, nous allons démystifier leurs différences et découvrir comment l'Event Bus de Symfony Messenger vous permet de construire une architecture réellement découplée.

    Ce que vous allez apprendre :

    • Les différences fondamentales entre EventDispatcher et Event Bus
    • Quand utiliser l'un plutôt que l'autre
    • Comment configurer un Event Bus avec Symfony Messenger
    • Créer une architecture événementielle découplée
  • Couverture de l'article CQRS avec Symfony Messenger : Domptez la complexité de vos applications
    CQRS avec Symfony Messenger : Domptez la complexité de vos applications

    Il y a 1 mois

    Vous êtes-vous déjà retrouvé face à un controller Symfony surchargé qui gère à la fois la validation, la logique métier, la persistence et les réponses HTTP ? Si oui, le CQRS est fait pour vous !

    Le CQRS (Command Query Responsibility Segregation) est un pattern architectural qui sépare clairement les opérations d'écriture (Commands) et de lecture (Queries). Combiné avec Symfony Messenger, il vous permet de :

    • Organiser votre code de manière claire et maintenable
    • Séparer les responsabilités pour respecter les principes SOLID
    • Valider vos données avant même qu'elles n'atteignent votre logique métier
    • Gérer les transactions de base de données de manière élégante
    • Préparer votre application pour l'asynchrone sans effort

    Dans cet article, nous allons explorer les Commands (écriture) et les Queries (lecture) à travers un exemple concret de gestion de bibliothèque.

  • Couverture de l'article Le BDD (Behavior Driven Development) avec Behat et Symfony
    Le BDD (Behavior Driven Development) avec Behat et Symfony

    Il y a 3 ans

    Le sujet des tests fonctionnels est une (longue) histoire chez Wanadev. Notre projet Kazaplan grossissant de plus en plus, nous sommes obligés d'adopter une politique de tests plus en plus stricte au fur et à mesure de l'avancement du projet.

  • Couverture de l'article Créer facilement une API REST avec Symfony & API Platform
    Créer facilement une API REST avec Symfony & API Platform

    Il y a 5 ans

    Au menu aujourd'hui, je vous ai concocté un petit article gourmand croquant, sur un framework devenu très populaire au sein de la communauté Symfony dans la mise en place d'API, j’ai nommé le fameux API Platform.

  • Couverture de l'article Symfony Messenger  : Gestion des Messages en file d’attente
    Symfony Messenger : Gestion des Messages en file d’attente

    Il y a 6 ans

    Messenger, apparu au printemps 2018 avec la version 4.1 de Symfony est devenu aujourd’hui un composant majeur du framework français. Et pour cause, Messenger répond parfaitement aux problématiques de performance que nos applications peuvent rencontrer. Comment ? Et bien c’est ce que nous allons découvrir à travers cet article qui couvrira les principales spécificités du composant Messenger.

  • Couverture de l'article Une API rapide et sans FOSRest
    Une API rapide et sans FOSRest

    Il y a 8 ans

    Avec une utilisation de plus en plus intensive des technologies frontend, les API sont les pierres angulaires du développement web moderne. Quels que soient les langages utilisés, de nombreuses solutions existent avec pour chacune un niveau de complexité et de fonctionnalités différentes. Leurs succès sont indéniables et dessinent l'avenir logique de beaucoup de frameworks backend.

    Dans l'écosystème Symfony, le bundle FosRest se taille la part du Lion des implémentations API. Mais est-il possible de faire sans ?