Quoi de neuf sous le soleil de la SymfonyWorld Online Summer Edition 2021 ?

L’écosystème Symfony propose de nombreuses conférences chaque année, localisées et mondiales. En fin de semaine dernière avait lieu SymfonyWorld Online 2021 Summer Edition, qui est donc une conférence internationale regroupant en un événement l’ensemble de la communauté.

Dans cet article, nous souhaitons vous résumer les meilleures « pépites » de ces deux jours, que vous ayez pu assister aux conférences ou non.

La keynote de Fabien Potencier

Cette année, Fabien a souhaité nous parler d’open-source et de comment se passe le développement de Symfony. L’idée générale est que chaque contribution compte et que l’effort commun permet d’atteindre un très haut niveau de qualité.

La core team travaille avec vous, et non pour vous.

La majorité des nouvelles fonctionnalités ne vient pas de la core team mais de contributeurs extérieurs, qui bénéficient du support et des relectures de la communauté – et c’est aussi comme cela que la core team évolue.

Sans parler de contribution en code ou en documentation, tester les nouvelles versions Bêta et RC est très important – cela aide beaucoup à garantir la qualité du framework.

L’autre sujet de cette keynote était la maintenance d’un projet basé sur Symfony. Autrefois, il était plutôt recommandé de privilégier les versions LTS (Long Term Support). Aujourd’hui, ce n’est plus le cas ; avec Flex et la politique de Back Compatibility, Fabien recommande plutôt de migrer de version tout au long de la vie du projet, pour éviter l’énorme chantier d’une migration de LTS vers LTS.

Symfony upgrading

Il est à noter que même s’il reste assez souvent simple de mettre à jour Symfony, il peut y avoir des blocages au niveau des dépendances de nos projets, il faut donc prendre cela en compte lors du lancement de votre migration : est-ce que les dépendances sont déjà compatibles ? Puis-je aider à rendre certaines compatibles ?

Enfin, Fabien recommande PostgreSQL pour les nouveaux projets : nous aussi !

« Symfony Forms: Advanced Use Cases » par Alexandre Salomé

Alexandre nous a présenté quelques cas avancés d’usage des formulaires, comme l’utilisation des extensions pour implémenter des comportements globaux.

Formulaire de connexion

Le premier exemple concerne le formulaire de connexion dans Symfony. Lorsque nous faisons make:auth, un formulaire en HTML est généré pour le login, ce qui n’est pas très cohérent avec tous les autres formulaires de notre application (il n’hérite pas du thème, entre autres).

Il est bien sûr possible de créer un véritable FormType pour le login mais cela demande un peu de travail.

Form naming

Par exemple, le nommage des champs de formulaire dépend du nom donné au moment de l’instanciation du FormType, il apparaît comme préfixe sur l’attribut name des champs HTML.

Il faut aussi mapper les erreurs du Firewall sur le formulaire, remettre le « last username », corriger l’identifiant du CSRF… Alexandre a su rendre toutes ces tâches très simples.

Héritage de données

Le deuxième cas nous a particulièrement plu car il exploitait une option peu connue : inherit_data. Elle permet de transmettre la donnée du parent à un enfant – permettant à un champ enfant d’avoir la data du parent.

Le FormType s’adapte à l’objet de donnée, pas l’inverse.

SortableType

Nous avons aussi beaucoup apprécié l’astuce sur la création de collection triable (« sortable »). Elle consiste à créer un SortableType héritant de CollectionType – cela permet de créer un block Twig sortable_row et d’indiquer ensuite à notre JavaScript un comportement à appliquer.

Avec Symfony UX et Stimulus, le JavaScript est d’ailleurs grandement simplifié.

Les exemples sont visibles sur Github.

« Cartographing Symfony » par Christian Flothmann & Denis Brumann

Christian et Denis ont pris le parti de diviser les nombreux composants qui composent Symfony, 28 nouveaux sont apparus depuis la version 2.0, en quatre catégories :

  • Les fonctionnalités fondamentales, ou core features, qui sont au cœur du framework, comme HttpFoundation, String, Filesystem, Intl, VarDumper, etc, qui sont finalement de fines surcouches de fonctionnalités de PHP.

  • Les fonctionnalités utilitaires, comme Console, Cache, Lock, EventDispatcher ou encore Runtime, qui n’ont pas ou peu de dépendances externes. La majorité des composants de Symfony se trouvent dans cette catégorie.

  • Les fonctionnalités d’application, comme Mailer, Translation, Serializer, Form, Notifier, HttpClient ou Validator, sont des fonctions de haut niveau et s’étendent à volonté selon chaque besoin. C’est là dessus que vont s’appuyer les fonctionnalités de l’application.

  • Les fonctionnalités de framework, comme Config, Routing, Security, HttpKernel, ou bien ErrorHandler, fournissent quant à elles de la configuration au niveau de l’application complète, se personnalisent et connectent tous les autres composants ensemble.

Dans cet ordre là, les premières dépendent de peu d’autre composants, voire aucun ; tandis que les dernières dépendent de beaucoup de composants. Ainsi, les premières sont beaucoup plus simples à mettre en place. Les erreurs qu’il est possible de voir apparaître en utilisant chacun de ces composants séparément suivent la même logique.

Beaucoup se sont plaints de la difficulté d’utiliser les composants de Symfony en dehors du framework, justement parce que ces composants sont très couplés les uns avec les autres. Ceci dit, le rôle de la documentation est d’expliquer comment utiliser ces composants avec le reste du framework 🙂.

Ils rappellent aussi que si un composant n’est pas présent dans Symfony, quitte à le coder soi-même, il peut être très intéressant de le partager à la communauté, donnant ainsi encore plus de sens à son travail. Nous ajouterons que contribuer permet aussi de gagner en compétences en recevant les critiques d’autres développeurs expérimentés.

Leurs conseils dans la création d’un composant pour Symfony ? Commencer avec tout au même endroit, identifier les fonctionnalités partagées, faire évoluer les abstractions en fonction de cela et solidifier les contrats, extraire de tout cela des composants et les réutiliser !

« What’s new in Doctrine 2021? » par Benjamin Eberlei

Nous utilisons Doctrine sur l’immense majorité de nos projets Symfony, c’est une des dépendances les plus critiques et pourtant souvent mal utilisée, ce qui peut provoquer beaucoup de dégâts (performances amoindries, multiplication des requêtes, sous-exploitation du SGBD, …).

La conférence de Benjamin se concentre sur les nouveautés récentes du package DBAL qui arrive en version 3.0.

Au programme :

  • Déplacement des méthodes fetch et fetchAll dans la classe Result, au lieu de Statement
  • Ces méthodes sont dépréciées en faveur de fetchAssociative, fetchId, fetchOne
  • executeQuery retourne maintenant un objet Result et non plus un Statement
  • Dépréciation des méthodes exec, query, executeUpdate au profit de la méthode executeStatement qui peut exécuter n’importe quelle requête SQL
  • Re-connexion automatique ! 🎉 Bye bye MySQL has gone away !

Par défaut, les nombreux messages de dépréciation vont être désactivés en environnement de prod, nos logs s’en porteront d’autant mieux.

Côté ORM, pour le moment 95% des mappings sont possibles avec les Attributes de PHP 8.0, il faudra attendre les Nested Attributes (prévus pour PHP 8.1 normalement), pour pouvoir atteindre 100% de support des mappings.

Cependant, les annotations imbriquées pour faire un ManyToMany sont transposées en Attributes successifs, ce qui nous semble plus clair à la lecture.

<?php
use Doctrine\ORM\Mapping\ManyToMany;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\InverseJoinColumn;
use Doctrine\ORM\Mapping\JoinTable;

/** Owning Side */
#[ManyToMany(targetEntity: "Group", inversedBy: "features")]
#[JoinTable(name: "user_groups")]
#[JoinColumn(name: "user_id", referencedColumnName: "id")]
#[InverseJoinColumn(name: "group_id", referencedColumnName: "id")]
private $groups;

/** Inverse Side */
#[ManyToMany(targetEntity: "User", mappedBy: "groups")]
private $features;

En somme, les changements dans doctrine/dbal 3.0 ne nous impactent pas directement lorsque l’on utilise l’ORM, mais il est toujours bon de savoir comment fonctionne le cœur de nos outils.

« You’re not in IT » par Stefan Koopmanschap

Stefan nous fait voyager à travers les différentes étapes de sa vie de développeur comme s’il s’agissait d’un jeu vidéo où il passe des niveaux, gagne des objets qui lui permettront d’avancer, rencontre des personnages, débloque des succès…

Slide style RPG

Dans les premiers niveaux, il apprend le PHP, puis il contribue à l’Open Source, ré-apprend l’orienté objet alors qu’il pensait tout savoir… À chaque nouveau niveau, il nous démontre que le métier de développeur est avant tout une histoire d’apprentissage.

L’importance de réapprendre est ici soulignée. Réapprendre un sujet qu’on croit connaître par cœur, avoir des mentors, faire du pair programming, regarder un talk sur un sujet qui vous semble inutile sont autant de manières d’apprendre au cours de sa vie de développeur (ou autre métier d’ailleurs !).

Il faut aussi avoir un regard bienveillant sur ses propres erreurs et leur accorder du temps : il est finalement plus avantageux de perdre un peu de temps à apprendre, pour en perdre moins en faisant des erreurs, par exemple, en prenant une journée pour faire un POC.

Devenir soi-même mentor, écrire des articles ou donner des conférences, bref, communiquer son propre savoir aux autres est également une très bonne manière de devenir incollable sur un sujet, et chez JoliCode, nous appliquons beaucoup ces conseils !

Learn, build and share : ça résonne beaucoup avec nos valeurs 🙂

« Towards Digital Sustainability » par François Zaninotto

Réduire nos émissions de CO2, ce n’est plus à prouver, il s’agit d’une nécessité urgente. Et il se trouve que nous, développeurs, pouvons y faire quelque chose, puisque nous sommes ceux qui créent les programmes que les gens utilisent lorsqu’ils créent des émissions CO2. C’est notre job de changer ça.

Comment ? Nous pouvons commencer par monitorer les émissions carbone produites par nos applications.

La production d’un appareil est responsable de 45% des émissions CO2, les 55% restants proviennent de l’utilisation. Mais en réalité ça n’est pas si rigide et dépend vraiment de l’appareil : un serveur consomme bien plus qu’un ordinateur ou un smartphone.

Il existe plusieurs sites qui se targuent de pouvoir analyser les émissions CO2 d’un site donné. Nous ne les citerons pas, puisque leurs analyses sont incomplètes, biaisées ou pertinentes pour un cas et pas pour d’autres.

François a donc décidé de mesurer tout ça lui-même avec un container Docker équipé d’un Cypress pour lancer un browser dans le container. Il récupère la consommation de cette machine virtuelle avec docker stats puis utilise le coût de production de l’électricité nécessaire.

Les chiffres de François et son équipe ne sont peut-être pas parfaits, mais ce projet leur a permis de prouver que les changements effectués dans le code de leur projet ont un impact direct sur les émissions CO2. Imaginez un outil qui, à chaque commit, souligne l’augmentation ou la diminution des émissions CO2 dues à ce code, et des validations basées là dessus !

On ne rappellera jamais assez la quantité d’autres paramètres à prendre en compte : quel que soit l’impact d’un site, l’écran du visiteur émet un même CO2. En revanche, les serveurs ne consomment que 5% du total.

De plus, pour une vraie optimisation, il faut réduire sa consommation au niveau du client (qui calcule les JavaScript et CSS minifiés, qui produit les animations, affiche les images…) et du réseau avant de se pencher sur le côté serveur.

Quelques astuces :

  • Écrire moins de code ! Et cela n’est pas sans nous rappeler Frédéric Bouchery au forum PHP 2019 : « le meilleur code c’est l’absence de code » !
  • Pour permettre de garder les vieux appareils, programmer en les prenant en compte. On peut restreindre le CPU d’un browser récent pour tester ce que notre code donnerait sur un vieux navigateur. Si nos programmes ne fonctionnent que sur les appareils récents, nous ne faisons qu’encourager l’obsolescence programmée ;
  • Choisir des hébergeurs verts pour héberger ses sites, c’est-à-dire des hébergeurs ayant recours à une électricité à provenance renouvelable. Les « gros » hébergeurs connus font du Green Washing et François nous encourage à nous regrouper pour leur demander des preuves de leurs actes écologiques.

« Runtime component: The game changer » par Tobias Nyholm

Le nouveau composant Runtime, introduit dans Symfony 5.3, rend nos applications non dépendantes de leur environnement. Ou plus exactement, il permet de rendre exécutables nos applications en fonction d’un contexte commun.

Prenons l’exemple d’une application Symfony traditionnelle (PHP, Nginx, PostgreSQL). Le front controller de Symfony dépend directement des super-globales fournies par PHP ($_GET, $_POST, $_SERVER, …).

Maintenant, prenons l’exemple d’une application Symfony destinée à être exécutée dans une Lambda AWS. Là, le front controller devra être modifié pour ne plus dépendre des super-globales (qui n’existent pas dans ce contexte).

Autre exemple avec une application en ligne de commande. Là encore, notre front controller (bin/console ici) n’a pas accès aux super-globales et dépend d’autres variables, comme les options passées en ligne de commande.

Le composant Runtime permet à notre application d’être exécutée à l’identique peu importe le contexte. Elle abstrait les dépendances du front controller en encapsulant l’exécution dans une fonction anonyme à laquelle est passée une variable unique $context acceptée par Request::createFromGlobals().

Symfony n’invente rien ici, puisque la PSR 15 Runtime standardise ce que doit faire le front controller, notamment par la méthode getRunner en charge de retourner l’application initialisée à partir du $context fourni en argument.

Le composant Runtime ne requiert aucune configuration pour être utilisé, ce qui le rend plus facile à appréhender.

Au final, n’importe quelle application qui utilise ce composant peut être exécutée dans n’importe quel contexte, sans besoin de changement (tant qu’elle ne fait pas appel aux superglobales dans son code, bien sûr).

Laravel Octane propose le même paradigme, mais avec une glue déjà présente pour supporter Swoole and RoadRunner, ce qui le rend moins interopérable que symfony/runtime. Mais le but recherché n’est pas le même que Runtime, la comparaison s’arrête donc là.

PHP n’est pas fait pour tourner exclusivement sur des environnements traditionnels (derrière nginx/apache), mais aussi sur des infrastructures modernes comme des FaaS et Symfony se doit d’être exécutable sur ces infrastructures de plus en plus répandues.

« PasswordHasher Component: A simple yet powerful password hashing library » par Robin Chalas

Pour la version 5.3 de Symfony, un composant PasswordHasher a été extrait du composant Security et réécrit. Robin nous dit pourquoi et ce qu’apporte cette nouvelle version.

Dans l’interface originale, on trouvait trois méthodes qui vont encoder, vérifier la validité d’un mot de passe, et vérifier si un hash doit être renouvelé. Le problème ? Le nom : la méthode encodePassword() n’encode rien, elle hache. Beaucoup de tickets ont été ouverts sur GitHub concernant ce nommage trompeur.

Aujourd’hui, c’est un composant séparé, utilisable indépendamment du composant Security. Il ne dépend que de PHP et plus de security-core, il peut ainsi être utilisé dans n’importe quel projet PHP.

Ce composant garde la même PasswordHasherInterface, et les trois méthodes qui ont le même but, mais avec les bons noms : hash(), verify() et needsRehash().

Concernant les Hashers utilisés, nous trouvons les suivants :

  • NativePasswordHasher basé sur la fonction PHP password_hash(),
  • SodiumPasswordHasher basé sur l’extension Sodium de PHP et qui utilise l’algorithme Argon2.
  • Pbkdf2PasswordHasher basé sur hash_pbkdf2() qui est assez vieux, et où le développeur doit fournir le « salt » contrairement aux fonctions plus récentes.
  • MessageDigestPasswordHasher utilise aussi une fonction de PHP : hash().
  • PlaintextPasswordHasher ne doit évidemment pas être utilisé en prod, mais uniquement pour le dev ou les tests.

La cryptographie n’est pas votre fort ? Pas de panique, le bon hasher sera choisi automatiquement si vous ne le renseignez pas.

Robin nous montre plusieurs exemples de code utilisant le composant hors du framework, notamment comment migrer tous les mots de passe de sa base de données d’un hash à un autre, car de meilleurs algorithmes de hash arrivent régulièrement et les anciens sont vite peu sûrs et obsolètes. Nous utilisons la classe MigratingPasswordHasher() à laquelle nous passons le nouveau Hasher et le Hasher legacy. Avec les mêmes fonctions verify, needsRehash et hash, nous pouvons effectuer cette migration très simplement.

« What is the Symfony UID component? » par Nicolas Grekas

Après avoir rappelé ce qu’est un identifiant unique et les avantages de la génération programmatique (comparé aux séquences ou auto-incrément d’une base de données par exemple), Nicolas a présenté le composant UID.

Pour le standard UUID :

  • v1 fait fuiter l’adresse Mac de votre machine et la date de génération ;
  • v6 est une version améliorée de v1 sans adresse Mac ;
  • v3 permet d’hasher un nom / une donnée, mais utilise md5 ;
  • v5 remplace md5 par sha1 dans v3 ;
  • v4 utilise uniquement de l’aléatoire.

Pour les ULID :

  • triable ;
  • fuite de la date de génération ;
  • même taille qu’un UUID.

Côté base de données, l’intégration avec Doctrine est déjà incluse via DoctrineBundle, ce qui est très pratique.

Nicolas insiste aussi sur le fait qu’il ne faut pas utiliser ces identifiants comme clé primaire sur vos tables. En effet les performances sont très négativement impactées par l’espace mémoire qu’occupe ces longs identifiants et le fait qu’il ne soient pas toujours triables.

Comme vous le savez sans doute, ce composant a fait un peu polémique à son arrivée dans l’écosystème, car plusieurs librairies dessinent déjà un état de l’art acceptable, notamment ramsey/uuid. Nicolas a terminé sa conférence sur une comparaison avec cette librairie, en expliquant que c’est surtout son architecture très abstraite et ses objets qui ne sont pas des Value Object qui a fini de les convaincre de créer ce nouveau composant.

« A Dynamic Frontend with Twig & Zero JavaScript? Say Hello to… » par Ryan Weaver

Depuis quelques mois et notamment avec l’arrivée de l’initiative Symfony UX, la traditionnelle application PHP+Twig prend un petit coup de vieux. Elle reste « boring » comme le disait Fabien Potencier au SymfonyLive Paris Online. L’arrivée de l’intégration de Stimulus et Turbo dans Symfony donne accès à des applications dont le frontend est plus dynamique sans trop d’efforts côté JavaScript.

Mais, est-ce que pour certains besoins ponctuels, Twig ne ferait toujours pas l’affaire ? Et si le côté composable des controllers Stimulus (ou des composants React) s’invitait dans Twig ?

🎉 TwigComponent

Le principe est de définir dans une classe PHP, l’objet unique qui sera utilisable dans le template de notre composant. Voyons ça comme une sorte de contrat pour le template : « tu dépends de cette classe, donc voici à quoi tu as accès ».

Le Maker Bundle propose une commande pour créer le nécessaire au développement d’un TwigComponent : php bin/console make:component

Concrètement, une classe de composant étend ComponentInterface, contient nos propriétés et nos méthodes qui seront disponibles dans le template avec la syntaxe suivante : {{ this.foo }} {{ this.bar() }}.

Chaque classe de composant est un service, donc l’injection de dépendances est disponible ! Nous pouvons tout à fait imaginer un composant d’interface pour remonter les 3 derniers commentaires d’un article de blog. Ou encore une barre de recherche qui remontre les articles au fur et à mesure que l’on tape, avec les Live Components (symfony/ux-live-component) !

Le principe est le même, mais dans le template, nous utilisons un controller Stimulus pour le côté dynamique. Le composant sera exposé sur une URL, dont les paramètres en lecture seule seront sécurisés par un hash calculé côté serveur. Si un paramètre est éditable, il faut l’indiquer explicitement dans l’Attribute LiveProp(writable=true)

LiveProp example

Ces nouveautés fonctionnent d’entrée avec le composant Form de Symfony, ce qui présage peut-être de bonnes choses pour le futur d’EasyAdmin ? Qui sait ?

Notons tout de même que cette nouveauté est expérimentale, et que nous ne l’avons pas encore testée. Sur le papier cela se rapproche de ce que nous faisions déjà depuis quelques temps, à savoir composer nos templates en fonction de classes PHP. Nous sommes donc ravis que Symfony prenne cette direction, et nous ne manquerons pas de vous faire un retour sur nos premiers tests des TwigComponents ici même !

Ryan a publié une démo des possibilités de ce nouveau composant.

Winter is coming

Encore une fois nous avons appris et découvert plein de choses durant cette conférence, à laquelle vous avez pu voir Bastien et Baptiste partager leur expérience sur des sujets qu’ils affectionnent particulièrement, la performance pour Bastien, l’AutoMapper pour Baptiste.

Nous vous donnons rendez-vous lors du prochain événement de l’écosystème Symfony, en décembre pour la conférence SymfonyWorld Online Winter Edition. Ça sera l’occasion pour toute la communauté de fêter l’arrivée de la version 6.0 !

Nos formations sur le sujet

  • Logo Symfony

    Symfony

    Formez-vous à Symfony, l’un des frameworks web PHP les plus connus au monde

  • Logo Symfony avancée

    Symfony avancée

    Décou­vrez les fonc­tion­na­li­tés et concepts avan­cés de Symfo­ny

blog comments powered by Disqus