Que vous soyez un développeur débutant qui souhaite approfondir ses pratiques en s'appuyant sur un framework au goût du jour ou senior décidant de réactualiser ses compétences, Symfony est aujourd'hui l'outil incontournable à étudier, qui s'est imposé devant tous les autres concurrents, pourtant nombreux et talentueux. À quoi tient ce succès ? Le plus simple est sans doute de l'utiliser pour créer un nouveau projet.
Symfony a été créé en 2005 par Fabien Potencier au sein de l'agence web SensioLabs. Avec les années, il est devenu un choix de premier plan pour les développeurs PHP en raison de sa flexibilité, de sa performance et de sa communauté active. Symfony est un framework de développement web et un ensemble de composants qui peuvent être utilisés indépendamment les uns des autres. Pour ce hors-série, nous allons le considérer uniquement sous l'angle framework, sans étudier les composants dans leur autonomie, mais retenez que, même en dehors de ce contexte, Symfony peut vous rendre des services.
1. Installation
1.1 Prérequis
Pour utiliser la dernière version de Symfony, il vous faudra avoir installé PHP 8.1 avec les extensions suivantes, habituellement présentes sur les installations de PHP : Ctype, iconv, PCRE, Session, SimpleXML et Tokenizer. Vous pouvez faire une vérification rapide à l'aide de la commande qui liste les modules configurés :
Attention, il arrive que les configurations de PHP en ligne de commande et de PHP utilisé par le serveur web diffèrent significativement. En cas de doute, vous aurez tout intérêt à vérifier également votre configuration pour le Web à l'aide d'une page .php avec ce contenu :
Il faut également que vous ayez Composer [1] installé.
Pour Symfony CLI, dont nous parlerons au point suivant, il vous faudra également Git, Tar et Curl.
1.2 Symfony CLI
Il s'agit d'un outil compagnon de Symfony. Il n'est pas obligatoire d'en disposer, mais pourquoi se priver de ce confort ? Il vous assistera dans la création de nouveaux projets, l'exécution d'un serveur web de développement avec HTTPS, la vérification de vulnérabilités.
Vous l'aurez installé sur la plupart des distributions GNU/Linux à l'aide de la commande :
Si vous souhaitez une installation plus intégrée dans votre distribution et qui utilisera votre gestionnaire de packages préféré, la documentation officielle [2] vous renseignera sur les commandes à utiliser avec Homebrew ou encore les dérivés de Fedora, Debian et Alpine.
1.3 Initier un nouveau projet
À partir de là, lancer un nouveau projet Symfony est un véritable jeu d'enfant :
Où app est le nom du dossier dans lequel vous allez développer votre application. Sans l'option --webapp, l'installation sera plus légère, mais vous n'aurez pas le nécessaire pour réaliser une application web complète. Vous pourrez vous en passer pour une application en ligne de commande ou une API en ligne sans interface homme-machine.
D'autres options sont possibles, en particulier pour spécifier la version de Symfony que vous souhaitez utiliser :
installera la dernière version au support prolongé, jusqu'à trois ans pour les correctifs de sécurité. Mais si vous préférez vivre dangereusement, vous pouvez préférer la prochaine version, en cours de développement :
Ou encore, si vous tenez à une version particulière, il vous suffit de la préciser :
Mais qu'est-ce que Symfony nous a préparé pour commencer ?
Sept dossiers et cinq fichiers. Commençons par les dossiers.
Tout d'abord bin. Il s'agit du répertoire destiné à recevoir les utilitaires en ligne de commande liés à votre application. À ce moment, il ne contient que le fichier console, qui permet d'interagir avec l'application à l'aide des commandes qu'elle définit. Symfony en fournit déjà un certain nombre par défaut. Vous pouvez vous en faire un aperçu à l'aide de la commande bin/console list. Signalons parmi elles une des plus utiles pendant le développement : bin/console clear:cache qui peut être abrégée en bin/console c:c. Celle-ci permet de vider le cache applicatif, qui peut être la cause de certains bugs persistants malgré les corrections entreprises.
Un autre exemple pratique vous permettra d'obtenir quelques informations utiles sur votre instance :
Ne confondez pas avec la console Symfony, installée dans le dossier bin de votre projet et Symfony CLI, installé globalement : la première contient des commandes destinées à interagir spécifiquement avec votre projet, la seconde fournit des outils globaux, non liés à un projet. Vous aurez d'ailleurs tout loisir d'ajouter facilement vos propres commandes à la console, en PHP, depuis votre application.
Vient ensuite le dossier config. Celui-ci permet de stocker la configuration des différents composants de votre application. Nous aurons l'occasion d'y revenir.
Le dossier caché .git nous montre que tout projet Symfony est aussi un projet Git. Nous voyons d'ailleurs un peu plus loin qu'un fichier .gitignore est également fourni. Le projet est ainsi déjà préconfiguré pour ignorer les fichiers temporaires, de travail, ou d'environnement utilisés par Symfony. Jetons un rapide coup d’œil sur ce .gitignore. Notez que nous y trouverons certains dossiers listés plus haut.
La première section correspond aux éléments de base utilisés par Symfony et qui sont à ignorer du versioning. Les lignes 2 à 5 correspondent à des fichiers liés à l'environnement. Ligne 6 : /public contient les fichiers statiques accessibles directement aux visiteurs, tels que les images et les assets en général. Son sous-dossier bundles reçoit les éléments publics liés aux extensions de Symfony. En réalité, il ne s'agira que de liens symboliques vers les dossiers public des extensions elles-mêmes. /var est destiné à recevoir des fichiers de travail, tels que le cache applicatif ou les logs. Enfin, /vendor accueille les différentes librairies utilisées par votre projet et installées par Composer.
S'ajoute à cela, ligne 11 à 14, une section permettant d'ignorer la configuration locale de PhpUnit ainsi que ses résultats.
Revenons à la liste de nos fichiers et dossiers à la racine. Le répertoire suivant est /migrations. Celui-ci contiendra les fichiers permettant à Symfony de gérer les mises à jour des structures de bases de données. Nous n'en sommes pas encore là, mais sachez que Symfony gère la structure de base de données sans que vous ayez à intervenir autrement que pour la créer et paramétrer les accès de Symfony. De simples commandes permettent de créer l'ensemble des tables ou de mettre à jour leurs structures sans qu'aucune intervention manuelle ne soit nécessaire. Pour peu que vous utilisiez des outils de déploiement continu, cette commande sera à intégrer dans les scripts de déploiement. Fini le cauchemar des interventions manuelles sur MySQL lors des mises en production ! Tout cela peut maintenant être fait sans risque inutile.
Vient ensuite le répertoire /src. C'est là que se trouvera le cœur de votre application, au moins en ce qui concerne le backend. Nous aurons l'occasion de nous y pencher longuement par la suite.
Le dossier /templates contient les fichiers de template, c'est-à-dire les modèles de pages HTML à utiliser pour y injecter le contenu des traitements avant de l'afficher à l'utilisateur. Symfony propose pour cela son propre langage, Twig, auquel nous consacrerons un article plus loin.
Un répertoire /tests est destiné aux fichiers des tests unitaires réalisés avec PhpUnit. Il y a d'ailleurs un peu plus loin le fichier phpunit.xml.dist établissant la configuration de cet outil.
Le dossier /translations contiendra les traductions nécessaires pour l'internationalisation de votre application.
Passons maintenant aux fichiers. composer.json, composer.lock et symfony.lock fonctionnent de concert. Le premier définit les dépendances et les suivants les versions des librairies utilisées, symfony.lock ciblant spécifiquement les composants Symfony, pour définir un état du code consistant sur toutes les instances et écarter le risque de comportements divergents causés par des versions différentes de telle librairie ou de tel composant. Ces fichiers doivent être versionnés, sans quoi ils perdraient leur utilité.
Si vous utilisez Docker et que Symfony parvient à le détecter, vous trouverez en plus les fichiers docker-compose.yml et docker-compose.override.yml. Avec leur aide, Symfony vous propose de simplifier la mise en œuvre de cet outil avec votre application [3].
Enfin, il nous reste les fichiers .env et .env.test qui permettent de définir des configurations liées seulement à telle ou telle instance. On y trouvera par exemple les clés de chiffrement des données sensibles, ou les paramètres de connexion à une base de données.
Vérifions maintenant si notre projet a été correctement initialisé en lançant le serveur de développement :
Et ouvrons notre navigateur à l'URL http://localhost:8000 :
Si vous voyez cet écran, c'est que vous pouvez passer à l'étape suivante.
2. Création de la première page
Symfony suit l'architecture qui s'est popularisée sous le nom de MVC, acronyme tiré des initiales de ses trois principaux motifs de conception, à la savoir le Modèle, la Vue et le Contrôleur. La plupart du temps, et, en particulier avec une application Symfony, il faut leur ajouter le Routeur.
Pour mémoire, en programmation orientée objet, le Modèle, la Vue, le Contrôleur et le Routeur sont des motifs de conception (design patterns en anglais), c'est-à-dire des solutions standardisées à des problèmes fréquents. Il en existe plusieurs centaines, mais ces quatre-là sont les plus répandus.
Le Modèle permet de manipuler les enregistrements d'une base de données comme des objets sans agir directement avec elle, permettant ainsi de garantir le respect d'une logique métier dans le modèle qu'il peut être difficile, voire impossible à mettre en œuvre au niveau de la base. Les modèles spécifiques à Symfony sont appelés Entités.
La Vue représente l'affichage fait à l'utilisateur : la plupart du temps, elle prend la forme d'un template dans lequel sont injectées des données issues d'un modèle ou d'une liste de modèles.
Le Contrôleur, lui, prend en charge la logique applicative : c'est lui qui va recevoir les actions de l'utilisateur pour les appliquer aux modèles avant d'envoyer les résultats à la Vue pour qu'elle les affiche.
En principe, un contrôleur ne prend en charge qu'une seule classe de modèles. Il est donc nécessaire d'avoir plusieurs contrôleurs pour réaliser une application complète. Pour déterminer quel contrôleur appeler, l'application fait appel au Routeur qui permet de transférer les demandes de l'utilisateur au bon contrôleur. Dans le cas d'une application web, le routeur utilise plusieurs éléments pour fonctionner, dont les principaux sont la requête et en particulier, l'URL appelée et la méthode (GET, POST...) utilisée, ainsi que la route. La route est la configuration du routeur qui lui permet de faire le lien entre la requête et une méthode du contrôleur.
Le minimum que nécessite Symfony pour afficher une page est une route, un contrôleur et un template. Le routeur est implémenté dans le noyau de l'application.
2.1 Annotations et routes
Symfony propose plusieurs syntaxes équivalentes pour écrire une route. La plus pratique est sans aucun doute d'utiliser des annotations : il s'agit de commentaires spéciaux utilisés pour déclarer des configurations ou des informations supplémentaires sur les classes, les propriétés ou les méthodes, qui sont ensuite utilisées par des outils ou des composants Symfony pour effectuer des tâches spécifiques. Symfony utilise pour cela les capacités de réflexion de PHP [4] afin de lire les commentaires qui se trouvent dans le code.
Ici, elles nous permettent de déclarer la route permettant d'accéder à une méthode d'un contrôleur directement dans le commentaire documentaire qui précède cette dernière. Cela rend autant l'écriture, la lecture et le débogage de routes bien plus facile et plus rapide. L'alternative est de passer par un fichier de configuration centralisé contenant toutes les routes décrites dans l'un des trois formats YAML, XML ou PHP.
Mais les annotations ne font pas partie du cœur de Symfony et nécessitent d'être installées. Pour cela, il suffit d'exécuter la commande suivante à la racine de votre projet :
Vous êtes peut-être surpris de la brièveté du nom du paquet. En effet, le nom des paquets Composer comprend toujours deux parties, la première correspondant à son éditeur.
Mais un projet Symfony inclut Flex, qui est un plug-in de Composer. Celui-ci définit un certain nombre d'alias, permettant d'installer plus facilement les paquets les plus fréquents avec une application Symfony. Le véritable nom du paquet installé ici est sensio/framework-extra-bundle.
Flex permet également aux paquets de définir une recette (recipe) qui est exécutée au moment de leur installation : cette recette leur permet de réaliser un certain nombre de tâches comme ajouter des fichiers de configuration, créer des dossiers, mettre à jour des .gitignore ou ajouter de nouvelles directives à votre fichier .env. Les configurations de l'application et du paquet après l'installation d'un paquet en sont ainsi considérablement simplifiées, souvent même jusqu'à disparaître.
Il y a toutefois une difficulté posée par les routes dispersées dans le code : il est difficile d'avoir une vue d'ensemble, ce qui est pourtant nécessaire pour arriver à déboguer un conflit. Un conflit se produit lorsqu'une URL peut être gérée par deux routes différentes : la première définie l'emporte et le contrôleur défini par la deuxième ne sera jamais appelé ; un bug sérieux est alors constaté, une fonctionnalité étant appelée dans le contexte d'une autre. Il peut être laborieux de rechercher les deux routes dans le code pour examiner comment elles sont définies. Heureusement, Symfony vous fournit un outil de débogage qui, en une commande, vous permet d'afficher toutes les routes définies dans l'ordre de leur interprétation. Voici un exemple tiré de la documentation officielle :
Nous voyons ainsi les principaux composants d'une route : tout d'abord un nom unique qui permet d'identifier la route ; la méthode HTTP qui lui est associée ; le protocole (appelé ici scheme) que l'on pourra utiliser pour rendre le HTTPS obligatoire afin d'accéder à certaines parties de l'application ; il est possible aussi d'indiquer un nom d'hôte, mais on le fera que pour des applications bien spécifiques ; enfin vient le chemin, le path, qui est la partie la plus élaborée de la route. En effet, le path permet d'une part de vérifier que le chemin indiqué dans l'URL correspond bien au contrôleur associé à la route, mais aussi de déterminer quels sont les paramètres à extraire de l'URL pour les fournir au contrôleur ; tout cela à l'aide d'expressions régulières. Celles-ci peuvent être très élaborées, peu lisibles pour le lecteur qui n'y est pas accoutumé et, par conséquent, sources de conflits dans les routes.
La même commande, en lui passant comme paramètre un nom de la route, vous fournira des informations plus détaillées à son sujet :
Enfin, vous pouvez également vérifier si un chemin correspond à une route sans quitter votre console à l'aide de la commande router:match :
2.2 Twig et le premier template
Twig, le langage de templates proposé par Symfony n'est pas installé par défaut avec Symfony. Il faut donc le faire soi-même :
Les fichiers de template Twig prennent l'extension .twig. Pour le moment, nous n'allons pas aborder la syntaxe ni les possibilités offertes par Twig. Pour commencer, disons simplement qu'un fichier Twig est d'abord un fichier HTML destiné à recevoir des données dont il prend en charge l'affichage. Cela n'est pas tout à fait exact, et nous verrons plus tard que Twig peut être utilisé pour d'autres formats. Les données reçues sont des variables PHP de tout type, y compris tableaux et objets. Le langage Twig permet de décrire la manière dont l'affichage doit être modifié pour refléter ces données. En aucun cas, un fichier de template ne doit recevoir de logique applicative : les données sont reçues, manipulées éventuellement, mais jamais modifiées.
Notre premier template sera celui de notre page d'accueil. Autant commencer par le début. Ajoutons donc le fichier templates/index.html.twig avec ce contenu (le dossier templates est créé automatiquement lors de l'installation de Twig) :
2.3 Le premier contrôleur et la première route
Maintenant que nous avons notre template, il ne reste plus qu'à l'utiliser et, pour cela, créer notre premier contrôleur. Il prend sa place dans le dossier src/Controller et s'appelle IndexController.php. Voici son contenu :
Il ne reste plus qu'à recharger la page http://localhost:8000 pour voir notre magnifique première page s'afficher. Voyons un peu les généralités concernant les contrôleurs. Tout d'abord, à la ligne 2, nous plaçons notre code dans l'espace de noms App\Controller. Des lignes 4 à 6, nous appelons les composants dont nous aurons besoin. Tous les contrôleurs doivent hériter de AbstractController (ligne 4). Nous avons besoin des annotations pour les routes (ligne 5). Et enfin, comme notre application est un site web, il nous faut pouvoir renvoyer des réponses HTTP, ce qui nous sera possible à l'aide du composant Response (ligne 6).
Arrivent ensuite la déclaration de notre classe IndexController (ligne 8), puis sa première méthode index (ligne 11). Mais arrêtons-nous un instant sur la ligne 10 : il s'agit de l'annotation qui définit la route. C'est la plus simple possible.
Nous suivons le parti pris de la documentation de Symfony en parlant d'annotations. Mais, en réalité, il s'agit d'attributs PHP natifs [5], fonctionnalité introduite avec PHP 8. Le fait est que Symfony utilise les attributs PHP comme les annotations utilisées avec les versions précédentes de PHP, celles-ci suivant alors une syntaxe légèrement différente, s'appuyant sur les commentaires. Ainsi, pour des versions antérieures de Symfony et de PHP, la ligne 10 aurait été :
Le début de l'annotation est ici marqué par un dièse #. L'annotation est placée entre crochets []. Suivent ensuite les paramètres définissant la route. '/' indique l'URL à laquelle la méthode répond, ici, la demande de la page d'index du dossier racine. Et l’on termine par name: 'index', qui donne le nom index à notre route.
Conclusion
Voilà, l'architecture est en place, l'application fonctionne, même si, pour le moment, elle ne nous permet pas de faire mieux qu'un site statique. Au prochain article, nous allons voir comment dynamiser tout cela et communiquer avec une base de données.
Références
[2] https://symfony.com/download
[3] Vous trouverez un squelette de projet Symfony préparé avec Docker sur GitHub (non testé par nos soins) : https://github.com/dunglas/symfony-docker