Aller au contenu
AFUP AFUP Day 2025 Baromètre Planète PHP PUFA
 

Sortir du cadre

Description

Symfony, comme tout framework, vient avec un ensemble de lignes directrices et autre "defaults". Structure des répertoires, fichiers d'amorçage, packages installés, configuration automatique, conventions de codage... toutes ces choses que l'on obtient lorsque l'on crée un nouveau projet.

Quel est le but de ces defaults ? Doit-on s'y tenir ? Ou pouvons-nous nous en écarter ? Et si on choisit d'en dévier, à quel coût ? Est-ce que cela vaut encore la peine d'utiliser un framework ?

Dans cette présentation, nous passerons en revue les différents axes sur lesquels Symfony nous fournit des defaults, en passant par un voyage dans le temps visant à comprendre comment Symfony a évolué sur le sujet et comment il se positionne par rapport aux autres frameworks PHP.

A partir de cet état des lieux, nous verrons un ensemble de préconistations visant à déterminer quelles sont les lignes dont nous sommes susceptibles de nous écarter, pourquoi et à quel prix. Avec pour objectif d'exploiter au mieux notre framework de prédilection tout en mettant nos besoins métier au centre de nos applications.

Conférence donnée lors du Forum PHP 2022, ayant eu lieu les 13 et 14 octobre 2022.

Informations complémentaires

Vidéo

Le speaker

Robin CHALAS

Robin est un architecte logiciel et un développeur backend qui est impliqué dans l'écosystème PHP et Symfony depuis des années. Membre de la Core Team, il contribue à Symfony en corrigeant des bugs et des failles de sécurité, ainsi qu'en ajoutant de nouvelles fonctionnalités au Framework ou à certains packages OSS populaires qu'il maintient au quotidien tels que LexikJWTAuthenticationBundle. Il aime également aider et partager ses connaissances avec la communauté en étant actif sur les plateformes de support telles que StackOverflow, en examinant les contributions ou en prenant la parole lors de conférences techniques. Si il n'est pas sur son clavier, il est probablement en train de manger des burgers à Lyon, de caresser ses chats, de jouer à la pétanque ou encore de s'occuper de ses plantes.

Verbatim

_ La conférence va pouvoir commencer. Robin Chalas est un membre de Symfony et il s'occupe de la partie sécurité. Nous lui avons demandé de présenter cette conférence à quelques jours de ce Forum PHP. Il a accepté avec plaisir. Merci, Robin. Féru de DDD... Féru de Dédé et architecte à la coopérative Les Tilleuls, il va nous montrer comment sortir du cadre de Symfony. Robin, c'est à toi.

_ Forcément... OK. Bonjour à tous. Merci beaucoup de venir m'écouter aujourd'hui. C'est un immense plaisir que d'être là sur un event sur le Forum PHP. C'est un honneur. J'espère que vous n'êtes pas déçus que ce ne soit pas un rector parce qu'il n'a pas pu venir. On va faire en sorte que vous ne regrettiez pas d'être venus quand même. Aujourd'hui, on va parler des défauts de Symfony. Dès la création et tout au long du cycle de vie de nos projets, il y a une question qui revient souvent : est-ce que j'y touche ou pas ? C'est une question à laquelle on va essayer de répondre en couvrant un maximum d'éléments de ce que Symfony nous fournit. C'est parti. On m'a déjà présenté rapidement. Merci à l'équipe AFUP. Je suis Robin Chalas. Je suis développeur APHP. Je suis archi chez Les Tilleuls, une coopérative dédiée par ses salariés. On cherche du monde en permanence. On a un stand. N'hésitez pas à venir discuter. Je suis membre aussi de la core team Symfony. On vient de créer notre projet Symfony. On a une structure de dossier qui est la racine de notre projet, avec un tas de choses qui nous est fourni par Symfony. Je perds l'affichage... Mon ordinateur se met en veille. Désolé. Je règle ça. Désolé pour la perte de temps. Forcément, ce n'est plus la même version ! Je suis un fainéant. On recommence. Voilà la structure de notre projet Symfony par défaut. Tous les projets ont une raison d'être bien particulière. On va principalement se concentrer sur les deux principaux dossiers de notre projet, c'est-à-dire le dossier config et SRC. Cette structure par défaut, est-ce qu'elle est sujette à modification ? Est-ce qu'il est souhaitable de la changer ? Auquel cas, est-ce que c'est possible ?

La réponse, c'est oui. On peut tout à fait modifier chacun des dossiers présents dans ce dépôt. Cependant, il se pose la question de l'intérêt que l'on aurait à faire ça. On peut vraiment tout modifier, ça passe du dossier de traduction jusqu'à la localisation du répertoire. La seule exception à ça, c'est le dossier config. Je ne sais pas la raison, mais ce n'est pas quelque chose que l'on peut changer. Le dossier config reste tel quel. C'est celui qui va contenir la configuration de nos services maison et les services des bundles. Globalement, est-ce qu'on a intérêt à changer ça ? De mon point de vue, de ma vision de développeur, c'est non. On a très peu de valeur ajoutée à changer le nom ou l'emplacement de ces répertoires. Ça n'a pas de sens. La raison est simple. Ça ne fait qu'ajouter des particularités à notre projet. Cette structure par défaut est standard. On a des noms qui sont très familiers dès que l'on bosse sous Linux. Ça a été consciencieusement réfléchi. Le nom de ces dossiers, c'est inspiré de Linux. Voilà pour la petite histoire. On a très peu d'intérêt à dévier pour ce qui est de ces répertoires par défaut. À l'exception de contraintes et de besoins infra. Si jamais il y a de rares cas, des fournisseurs d'hébergement qui existe aujourd'hui qui vont nous imposer d'avoir typiquement un public HTML à la racine de notre projet pour pouvoir compter dessus. En général, ça se contourne. Ça se voit de moins en moins. Globalement, sauf contraintes infra, il n'y a pas de raison de changer la structure standard. S'il faut la challenger, ce sera du côté de Symfony. Pour ce qui est de rajouter des dossiers et des fichiers supplémentaires, il n'y a pas de problème. Par exemple, j'ai souvent un dossier qui vient très rapidement dans le projet, qui va s'appeler Tools, et qui va me servir à isoler...

Ces outils dépendent souvent de composants Symfony. Souvent, c'est dans des versions qui sont potentiellement incompatibles avec celles du projet. Pour s'éviter les conflits de version, on va isoler ça. Il y a d'autres leviers pour répondre à cette problématique. Celle-ci, ça en est une facile à mettre en place, facile à maintenir. On peut imaginer plein d'autres raisons de rajouter des dossiers et des fichiers à la racine de ce projet. Par exemple tout ce qui est lié à l'infra, on va avoir tendance à mettre nos histoires de déploiement et de provisioning. On va avoir tout un tas de fichiers qui se rajoutent. Il n'y a pas de souci avec ça. La seule petite contrainte, c'est de faire attention à ne pas tomber dans l'excès. Chaque petit fichier qu'on vient rajouter, ça doit être bien pesé. Sinon, ça termine en foutoir. C'est aussi le but de cette conférence, d'éviter de finir avec des foutoirs. Du moment qu'on fait du custom, on documente. Si on a une pratique qui sort de l'usage commun, qui est spécifique à notre entreprise, ont le documente. Ça sera utile aux futurs collaborateurs et ça sera utile aussi à notre futur nous, le jour où l'on ne comprend plus ce qu'on a fait. Attention à bien garder de la cohérence entre vos projets. Ça peut paraître bête, mais c'est quelque chose qu'on a tendance à oublier. Il y a beaucoup de boîtes où chaque équipe va faire sa tambouille dans son coin. L'idée, ce n'est pas que ça aille dans l'extrême, mais de trouver le bon curseur, placé au bon endroit pour avoir une certaine cohérence. Ça marche aussi pour les fichiers que l'on va rajouter. Maintenant, on va rentrer dans le vif du sujet. On va aller jeter un œil aux dossiers principaux de notre projet. On va regarder leur contenu, leurs structures et voir s'il y a lieu de les remettre en question.

Le premier que l'on va regarder, c'est le dossier config. Il a deux gros sous-dossiers : packages et routes. Il a quatre fichiers. On va jeter un œil au premier dossier. C'est le dossier config packages. Si vous faites du Symfony, il contient la configuration des bundles core et des bundles tierces. Doctrine n'est pas un bundle Symfony. Ce dossier est complètement manager par Symfony Flex. C'est lui qui vient le remplir. Il va venir à l'agrémenter, remplir à chaque fois qu'on rajoute une dépendance sur notre projet. Ces fichiers et ce dossier sont voués à être modifié en accord avec les besoins du projet. C'est votre config. Elle dépend du besoin de l'application en question. Sa structure n'est pas vouée à être remise en question. La structure, c'est si potentiellement, on aurait envie de rajouter des sous-dossiers. Il n'y a pas vraiment de motivation concrète à faire ça. Avant, il y en avait une. On avait une séparation par environnement au sens symphonique. On avait un dossier dev, test, et tout ce qui était à la racine, c'était ce qui était considéré pour la prod. Ce n'est plus le cas. Depuis peu, on a une petite annotation qui est une syntaxe IML custom, qui nous permet de charger de la config pour un environnement. Ça simplifie pas mal les choses. On ne se pose plus la question, on ne se fait leurrer à regarder un fichier de config et à ne pas savoir sur quel environnement il s'applique. C'est fini. OK pour ce dossier packages. Ces fichiers sont posés quand on démarre le projet, ils sont posés quand on requiert une dépendance. Mais ils vont aussi être mis à jour par Flex. Vous allez les modifier.

Il y a un moment donné où il y a une nouvelle version du bundle qui sera. Cette nouvelle version du bundle, ça peut être un bundle Symfony, elle va contenir des changements qui impactent la configuration. Avant, c'était à vous de faire le boulot. Maintenant, c'est Flex qui le gère par le biais d'une commande qui s'appelle composer recipes updates. Elle est plutôt super cool. Elle existait déjà, mais elle a été grandement améliorée. Ça nous change plutôt pas mal la vie. Elle va nous permettre de mettre à jour nos fichiers de configuration. Elle va aller essayer de modifier tous les fichiers, un par un. Un fichier de config est égal à une recette. Les recettes Flex sont hébergées sur github. Des recettes se mettent à jour via cette commande. Elle va aller faire les recettes, une par une, voir s'il y a une nouvelle version. Si la nouvelle version matche avec la version que vous avez installée, cette commande va aller chercher une version de la recette 6.1, sans quoi elle ira chercher la version la plus récente et elle va essayer de patcher en essayant d'appliquer le patch. Si vous n'avez rien touché, les principaux ont tendance à bouger en fonction de notre setup, parfait, il n'y a rien à faire. Il va appliquer le patch, c'est magique. Cependant, dès qu'il y a une modif qui posent conflit, que cette commande n'arrive pas à appliquer le patch, on va vous filer un gif avec lequel vous pourrez vous débrouiller pour faire votre MAJ à la main. Moins il y en a, mieux on se porte.

Mon conseil pour faciliter les mises à jour de recettes, c'est d'éviter les changements cosmétiques, type je veux aligner les options, leurs valeurs, ou je ne veux pas utiliser des null. Ce sont des changements qui n'ont pas de valeur ajoutée. C'est purement subjectif. Ça va juste compliquer les updates. On passe au dossier suivant : routes. Il sert à référencer les fichiers de routine, et pareil pour ce qui est des bundles core et tierces. Il y en a quelques-uns qui se mettent au fur et à mesure qu'on va rajouter des dépendances au projet, il y en a d'autres qui vont se rajouter. Il est complètement managé par Symfony Flex. Il est sujet à recevoir des updates. Sa structure et son contenu ne sont pas voués à être modifiés. Il n'y a pas de raison d'y toucher. On passe au fichier. Ce fichier bundles.php sert à activer les bundles core et tierces. Depuis un certain temps, on ne fait plus de bundle maison. Ça sert à intégrer une librairie tierce dans Symfony. Symfony vous en fournit un certain nombre et la communauté nous en fournit d'autres. Ce fichier est aussi managé par Flex. Vous pouvez tout à fait venir le modifier pour spécifier qu'on le veut sur tel environnement et pas un autre. Cependant, normalement, les défauts sont plutôt bien faits. En théorie, pas de raison d'y toucher. Attention à ne pas se mettre une balle dans le pied. Ensuite, on a un fichier qui s'appelle config/preload.php. On peut arriver dans ce fichier et se demander ce que c'est. Concrètement, ce n'est pas fameux ce qu'on y trouve dedans. Ça va tirer parti, ça va permettra Symfony de tirer parti de la préloading et c'est dispo depuis PHP 7.4 et ça va permettra aussi de précharger nos classes. Vous n'avez pas à toucher ce fichier. Cependant, si vous voulez vraiment en tirer parti, c'est dans la doc de Symfony mai il va s'agir d'indiquer dans PHP de charger ce fichier pour le préloading. On est passé rapidement dessus. Il est éventuellement voué à disparaître. Je vous invite à aller voir et à vous préparer éventuellement ça.

Depuis PHP 8.1, il n'y a plus d'intérêt à faire du préloading. L'effort a été mis sur d'autres choses. Il se trouve qu'il n'y a plus tellement de valeur ajoutée. En plus, ça pose pas mal de problèmes du point de vue framework. Ce sont des histoires assez compliquées. C'est notamment pour la petite histoire, la motivation, le fait que Symfony demandait PHP 8,1 minimum. Cela a été fait sur une version mineure, 6.1. On n'a jamais bumpé ainsi une version de PHP. On essaye de préserver nos utilisateurs et nos développeurs. On serait plus contents si toutes les infras du monde étaient sur une dernière version de PHP. Ce n'est pas notre rôle que de pousser ou de voir punis les développeurs qui n'ont pas assez de bande passante pour se mettre à jour au niveau de leur version de PHP. Ensuite, on part sur du yaml. On a le fichier routes.yaml. Il est sujet à modification. On va expliquer pourquoi un peu plus tard. C'est en fonction de l'archi de votre code. On touche un peu au vrai sujet de cette conférence. Il permet que tout marche par défaut. Tout va bien, vous créez un contrôleur, vous déclarez votre route, ça marche. Ensuite, on a le fichier services.yaml. Celui-là, il est très joli quand il arrive. Il est un peu complexe. Globalement, on a eu de grosses améliorations là-dessus avec le temps. Avant, c'était à vous. Par défaut, il y avait juste écrit "services :". Entre-temps, il y a eu énormément de choses côté Symfony qui se sont passées. On se retrouve avec un fichier qui va grosso modo, de son comportement par défaut, aller chercher toutes nos places dans le dossier SRC, essayer de les enregistrer en tant que service. On voit que c'est basé sur un chemin. C'est une découverte basée sur PSR4. En plus, on va lui donner quelques petites règles d'exclusion. Juste en dessous, on a SRC/entity. Nos entités ne sont pas des services. On l'exclut de ça. Et le dernier, c'est le kernel. Ce fichier, sa structure et son contenu, les deux sont voués à évoluer plus ou moins significativement en fonction de comment vous sortez du cadre. Tôt ou tard, vous devrez déclarer un service. Il faudra mettre la main à la pâte.

Il va aussi y avoir une notion des performances qui rentrent en compte. On a pas mal de projets où la configuration automatique atteint ses limites. Et significativement aussi parce qu'en fonction de l'architecture du code. C'est basé sur le chemin. Ça fait beaucoup de suppositions sur la manière dont votre code est structuré. Les suppositions vont marcher par défaut, mais à un moment donné, vous sortez un peu de ce qui est attendu. Ce qui nous amène à la partie la plus intéressante : le dossier SRC. Il ressemble à ça. Si on a déjà bossé avec du Symfony et avec Doctrine, globalement, on s'y retrouve. Ça se prête très bien pour une config par défaut. On a juste à poser notre contrôleur, créer nos entités. Tout va bien, tout marche. Tout est automatique. Il ne faut pas avoir peur de casser la structure de ce dossier. Tout ce qui est dedans vous appartient. Symfony Flex n'y fait presque rien. Kernel.php va quand même vous proposer des changements. Il est voué à être touché, le kernel.php. Il y a des cas où c'est nécessaire, notamment quand on va sur des archi un peu compliquées. Kernel.php va être un levier de rajouter des règles d'autoconfiguration. La motivation de ce talk est partie de ce tweet. On a un monsieur qui fait du Laravel et qui propose à la communauté une alternative qi est similaire à celle de Symfony. On a des dossiers qui sont très axés techniques. On va avoir des dossiers modèles qui correspondent à nos entités de Doctrine. On a une structure par défaut qui marche pour démarrer.

Ce monsieur a proposé une archi un peu plus avancée, un peu différente et qui sort de ce que Laravel fournit par défaut. Un autre monsieur, qui est un évangéliste Laravel et qui a dit : non, pourquoi faire ça ? Il va très loin. Il dit que si on fait ça, ça ne vaut plus la peine d'utiliser un framework. Je n'attaque pas la personne. C'est certainement un bien meilleur développeur que moi. Par contre, c'est juste non. Son propos, ça ne va pas. Globalement, avant d'être des développeurs Laravel ou Symfony, on est des développeurs. C'est juste un outil, le framework. On ne doit pas adapter nos besoins au framework, c'est lui qui s'adapte à nous. Ce propos est déconnant et dangereux. Il a un impact très mauvais sur les développeurs. Surtout de par quelqu'un qui a une telle notoriété. Donc j'ai réagi. Ce n'est pas dans mes habitudes. Pour le coup, je n'ai pas pu m'empêcher... Désolé, je vous spoile mes slides. J'ai répondu au mec que je n'ai pas envie de réécrire toutes les features. Le framework doit avoir de la valeur ajoutée. En théorie, on devrait être capable de faire des choses qui sont établies et reconnues propres et fonctionnelles au sein d'un projet basé sur un framework. Le conseil est de ne pas laisser le framework est empiété sur vos besoins métiers. Vos besoins métiers ont potentiellement besoin d'architecture un peu plus compliquée que celle qui vous est fournie par défaut par votre framework. Il y a des archi et des structures qui sont reconnues et qui fonctionnent. On ne va pas s'empêcher de les utiliser parce qu'on fait du Symfony ou du Laravel. Ce serait dommage. Les frameworks se doivent de fournir une structure par défaut qui générique et qui fonctionne pour 80 % des cas. Cela permet de prototyper très rapidement. Ce n'est pas quelque chose qui est gravé dans le marbre. Il faut pouvoir en sortir. Plus tôt on l'anticipe, mieux on le vit. Allez voir les architectures plus évoluées, du moment où vous avez plus...

Je fais référence à un bouquin sur le DDD, à partir du moment où vous avez plus de 30 use cases métiers, il va falloir aller sur quelque chose de plus compliquée. Pareil pour vos entités. Un autre tweet que j'ai vu il n'y a pas longtemps. On m'a dit que je devais faire ce talk il y a 10 jours. Les choses se sont faites sur le tas. On a quelque chose qui rage clairement sur une structure de répertoire. J'essaye d'en réparer tous les jours. Globalement, c'est un bordel sans nom. Nous n'avons que des choses qui sont très orientées techniques au départ, dans la majorité. Et on a un espèce de mix. Ce monsieur a démontré, mais je suis d'accord avec lui dans l'idée, c'est le bordel. On ne veut pas ça. On ne veut pas le réparer et le remettre en question. La structure par défaut marche pour une certaine typologie de projets et elle marche jusqu'à à un certain moment, mais après il faut se poser la question d'en sortir. Autrement, vos projets vieilliront et grossiront mal. Un autre tweet. Les gens s'accrochent trop aux conventions du framework et ils ne sortent pas de cette bulle. J'étais comme ça il n'y a pas longtemps, moi aussi. Je souhaite à tout le monde de lever un peu les yeux. On n'est pas des développeurs Symfony, mais des développeurs tout court. Encore ce monsieur, détracteur de l'architecture différente de son framework préféré. Il a laissé tomber. Pour lui, ça veut dire faire une croix complètement sur son framework.

Alors pourquoi Laravel, dans ce cas ? Il est fort probable que côté Laravel ce sera plus compliqué de sortir des structures par défaut pour la simple et bonne raison que les conventions vont rendre les choses compliquées. Symfony est connu pour être très flexible. Ça ramène de la complexité pour le développeur. Laravel est plus fort sur la DX. Cependant, quand il s'agit de sortir un peu du cadre, ça devient apparemment beaucoup plus compliqué. Je ne jette pas la pierre. C'est une façon de voir les choses. Au-delà de ça, il n'y a pas si longtemps, côté Symfony, on n'était pas vraiment mieux lotis. Il fut un temps où on devait pousser les commandes dans un dossier commande. Elles devaient être suffixées par commandes.php. Heureusement, on en revient. On a énormément avancé sur tout ce qui est autoconfiguration basée sur les types. On est capables de faire ce qu'on veut tout en faisant des choses réutilisables. C'est une très bonne chose. J'ai une slide qui a sauté. Je voulais vous dire qu'il y a un bouquin que j'ai lu il n'y a pas longtemps, que j'ai beaucoup aimé parce qu'il est disponible aussi en e-book. Il s'appelle "Recettes pour découpler". C'est très détente. C'est tout public. Ça va juste amener des notions de comment réduire l'adhérence à nos frameworks et donc se donner la possibilité d'aller sur des archi un peu plus évoluées. Allez voir ça. Il y a plein de lecture, même sur le DDD. Je vous encourage à y aller. Prenez les choses telles qu'elles sont. Il ne s'agit même pas forcément de parler de DDD. Il s'agit de parler de découpler son code du framework. Ça a des bénéfices qui sont conséquents. Allez vous renseigner. Chez Les Tilleuls, on a fait des confins sur les DDD. Ça reste du Symfony, c'est relativement similaire. Allez voir. Venez nous voir aussi. On est sympas. On a un stand. Si vous voulez parler un peu des techniques, si vous souhaitez parler boulot, on est tout à fait ouverts à la discussion. Voilà. Et également si vous souhaitez de l'aide pour monter sur ce genre de pattern ou d'architecture un peu plus avancée, ce sera avec plaisir qu'on vous accompagnera. N'hésitez pas. Merci !