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.
Section intitulée la-keynote-de-fabien-potencierLa 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.
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 !
Section intitulée quot-symfony-forms-advanced-use-cases-quot-par-alexandre-salome« 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.
Section intitulée formulaire-de-connexionFormulaire 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.
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.
Section intitulée heritage-de-donneesHé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.
Section intitulée sortabletypeSortableType
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.
Section intitulée quot-cartographing-symfony-quot-par-christian-flothmann-amp-denis-brumann« 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 !
Section intitulée quot-what-s-new-in-doctrine-2021-quot-par-benjamin-eberlei« 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
etfetchAll
dans la classeResult
, au lieu deStatement
- Ces méthodes sont dépréciées en faveur de
fetchAssociative
,fetchId
,fetchOne
-
executeQuery
retourne maintenant un objetResult
et non plus unStatement
- Dépréciation des méthodes
exec
,query
,executeUpdate
au profit de la méthodeexecuteStatement
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.
Section intitulée quot-you-re-not-in-it-quot-par-stefan-koopmanschap« 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…
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 🙂
Section intitulée quot-towards-digital-sustainability-quot-par-francois-zaninotto« 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.
Section intitulée quot-runtime-component-the-game-changer-quot-par-tobias-nyholm« 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.
Section intitulée quot-passwordhasher-component-a-simple-yet-powerful-password-hashing-library-quot-par-robin-chalas« 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.
Section intitulée quot-what-is-the-symfony-uid-component-quot-par-nicolas-grekas« 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.
Section intitulée quot-a-dynamic-frontend-with-twig-amp-zero-javascript-say-hello-to-quot-par-ryan-weaver« 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 ?
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)
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.
Section intitulée winter-is-comingWinter 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 !
Cet article porte sur la conférence SymfonyWorld Online Summer Edition 2021.
Commentaires et discussions
Nos formations sur ce sujet
Notre expertise est aussi disponible sous forme de formations professionnelles !
Symfony
Formez-vous à Symfony, l’un des frameworks Web PHP les complet au monde
Symfony avancée
Découvrez les fonctionnalités et concepts avancés de Symfony
Ces clients ont profité de notre expertise
Afin de soutenir le développement de son trafic, Qobuz a fait appel à JoliCode afin d’optimiser l’infrastructure technique du site et les échanges d’informations entre les composants de la plateforme. Suite à la mise en place de solution favorisant l’asynchronicité et la performance Web côté serveur, nous avons outillé la recherche de performance et…
À l’occasion de la 12e édition du concours Europan Europe, JoliCode a conçu la plateforme technique du concours. Ce site permet la présentation des différents sites pour lesquels il y a un appel à projets, et encadre le processus de recueil des projets soumis par des milliers d’architectes candidats. L’application gère également toute la partie post-concours…
Afin de poursuivre son déploiement sur le Web, Arte a souhaité être accompagné dans le développement de son API REST “OPA” (API destinée à exposer les programmes et le catalogue vidéo de la chaine). En collaboration avec l’équipe technique Arte, JoliCode a mené un travail spécifique à l’amélioration des performances et de la fiabilité de l’API. Ces…