Image de 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 !