La migration d’une application de Symfony 5 à Symfony 6 peut sembler complexe, mais elle est essentielle pour bénéficier des dernières améliorations et optimisations. Symfony 6 apporte des fonctionnalités avancées, des performances accrues et des mises à jour de sécurité qui en font un choix incontournable pour les développeurs. Dans cet article, nous allons explorer les étapes clés de la migration de Symfony 5 à Symfony 6 et vous guider à travers les meilleures pratiques pour rendre cette transition aussi fluide que possible.

La première étape pour migrer votre application vers Symfony 6 consiste à mettre à jour toutes les bibliothèques Symfony vers la version 5.4. Heureusement, cette étape est simple : il s’agit principalement d’une mise à jour via Composer. En effet, Symfony 5.4 est la dernière version LTS (Long Term Support) de la série 5 et sert de base solide avant de passer à Symfony 6.

Dans votre fichier composer, assurez-vous de changer toutes les versions 5.0.* en 5.4.*. pour les packages qui débute par symfony/

en plus des packages eux-mêmes, nous devions également modifier la clé extra.symfony.require. Il s’agit d’une optimisation des performances apportée par Flex : cela garantit essentiellement que Flex ne prend en compte que les packages Symfony correspondant à cette version. Assurez-vous simplement de ne pas oublier de la mettre à jour

Maintenant il ne reste qu’à lancer la commande

symfony composer up 'symfony/*'

Cette commande garantit que toutes vos bibliothèques Symfony sont mises à jour à la version 5.4, préparant ainsi le terrain pour la migration vers Symfony 6. Une fois cette étape terminée, il est recommandé de vérifier le bon fonctionnement de votre application et de corriger toute dépréciation signalée par Symfony.

Maintenant que nous sommes sur Symfony 5.4, nous pouvons voir la liste complète des chemins de code dépréciés que nous rencontrons lors du rendu de cette page.

À ce stade, notre travail est simple mais pas forcément facile. Nous devons traquer chacune de ces dépréciations, comprendre quel code doit être modifié, puis effectuer cette modification. Certaines seront assez évidentes… et d’autres ne le seront pas.

Donc, avant même d’essayer de les traquer manuellement, faisons quelque chose de plus automatique. Nous sommes des développeurs, n’est-ce pas ? Utilisons un outil appelé Rector pour automatiser autant de modifications que possible dans notre code. C’est la prochaine étape.

Rendez-vous sur https://github.com/rectorphp/rector. C’est un outil en ligne de commande génial dont la mission est simple : automatiser toutes sortes de mises à jour dans votre code, comme le passage d’un code compatible avec Symfony 5.0 à un code compatible avec Symfony 5.4. Ou encore la mise à niveau de votre code pour qu’il soit compatible avec PHP 8. C’est un outil puissant.

symfony composer require rector/rector --dev
./vendor/bin/rector init

Cette commande crée un fichier rector.php à la racine du projet.

À l’intérieur de cette fonction de rappel, notre tâche est de configurer les types de mises à jour que nous souhaitons appliquer. Ces mises à jour sont appelées « rules », ou parfois « set lists ». Nous allons commencer par un ensemble de mises à jour Symfony.

<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Symfony\Set\SymfonySetList;

return RectorConfig::configure()
    ->withSymfonyContainerXml(__DIR__ . '/var/cache/dev/App_KernelDevDebugContainer.xml')
    ->withSets([
        SymfonySetList::SYMFONY_54,
        SymfonySetList::SYMFONY_CODE_QUALITY,
        SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION,
    ]);

Vous pouvez lancer la commande

vendor/bin/rector process src/

Voici un exemple de résultat

Rector a automatisé plusieurs changements dans notre application nécessaires pour supprimer les dépréciations sur Symfony 5.4. De plus, il a effectué un certain refactoring bonus, comme l’ajout du type de retour Response en option dans nos contrôleurs.

Mais aussi bien que cela soit, ce n’est pas parfait. Tous les noms de classes sont en ligne, au lieu d’avoir une déclaration use pour cela vous pouvez utiliser php-cs-fixer

./tools/php-cs-fixer/vendor/bin/php-cs-fixer fix

Comme vous le savez probablement, chaque fois que nous installons un nouveau package, ce package peut être accompagné d’une recette (recipes) qui effectue des actions telles que l’ajout de fichiers de configuration, la modification de certains fichiers comme .env, ou l’ajout d’autres fichiers. Au fil du temps, Symfony met à jour ces recettes. Parfois, ces mises à jour sont mineures… comme l’ajout d’un commentaire dans un fichier de configuration. Mais d’autres fois, elles sont plus importantes, comme le renommage de clés de configuration pour s’adapter aux changements dans Symfony lui-même. Et bien que vous ne soyez pas obligé de mettre à jour vos recettes, c’est un excellent moyen de garder votre application conforme aux standards d’une application Symfony. C’est aussi un moyen gratuit de mettre à jour le code obsolète. ===> symfony composer recipes

https://github.com/symfony/recipes

symfony composer recipes:update

Cette commande vérifie quelles recipes sont installées dans votre projet et recherche de nouvelles versions. Lorsque vous choisissez une recipe à mettre à jour, elle génère un diff entre la version originale et la nouvelle version puis applique ces changements.

Vous devriez toujours vérifier attentivement les différences pour éviter de perdre toute configuration personnalisée

Je vous conseille de commencer par le plus long : symfony/framework-bundle vous allez découvrir beaucoup de changement à ce niveau comme

  • L’introduction à la notion when@dev dans les fichiers de configuration (Symfony 5.3 …)
    • Vous pouvez désormais ajouter une configuration spécifique à l’environnement en utilisant cette syntaxe.
  • La suppression de fichier config/bootstrap.php
    • Component Runtime : symfony composer require symfony/runtime
  • La modification du public/index.php
  • config/preload.php File
    • Essentiellement, en production, si vous pointez votre php.ini et que vous définissez opcache.preload sur ce fichier, vous obtiendrez un gain de performance énorme. La seule autre chose à faire est de redémarrer votre serveur web à chaque déploiement ou PHP-FPM si vous utilisez cela

Vous pouvez continuer de la même façon

symfony composer recipes:update

par exemple symfony/console, symfony/twig-bundle, doctrine/doctrine-bundle

C’est à nous de gérer les conflits pour chaque bundle choisit.

Un point sur le bundle symfony/doctrine-bundle. la recipe a supprimé la ligne type: annotation

Quoi qu’il en soit, dans la configuration de DoctrineBundle, vous n’avez plus besoin de cette ligne type: annotation. Si elle n’est pas présente, le format correct sera détecté automatiquement. Si Doctrine voit des annotations, il chargera les annotations ! S’il voit des attributs PHP 8, il chargera ceux-ci. ===> ce qui permet à Doctrine de tout gérer automatiquement.

Notez bien aussi pour le bundle symfony/security-bundle l’activation du nouveau system de sécurité enable_authenticator_manager: false

Si on veut parler du bundle symfony/webpack-encore-bundle, Notez l’apparition du fichier assets/bootstrap.js. Son rôle est d’initialiser la bibliothèque JavaScript Stimulus et de charger tous les fichiers du répertoire controllers/ en tant que contrôleurs Stimulus.

Plus de détails sur Stimulus

Lors de la gestion des conflits, garder les dernières versions toujours "@symfony/stimulus-bridge": "^3.2.0", et remplacer @stimulus par @hotwired/stimulus

yarn install && yarn encore dev pour vérifier le comportement

Notre code devrait maintenant être prêt pour PHP 8. Il est maintenant temps de mettre à jour nos dépendances pour PHP 8. Dans le fichier composer.json, sous la clé require, il est actuellement indiqué que mon projet fonctionne avec PHP 7.4 ou 8. Je vais changer cela pour indiquer simplement "php": "^8.2.25" (version minimal pour Symfony 6.0 est bien PHP 8.0)

symfony composer  up

J’aimerais préciser que nous avons aussi changé symfony/flex vu qu’il utilise son propre système de version. "symfony/flex": "^1.3.1", tp "symfony/flex": "^2.1",

symfony composer up

Nous passons à Rector pour effectuer une adaptation du code applicatif.

vendor/bin/rector process src

Le changement le plus important s’appelle les « Promoted Properties« . Dans PHP 8, vous pouvez ajouter un mot-clé private, public ou protected directement devant un argument dans le constructeur. Cela crée automatiquement une propriété et lui attribue cette valeur s’il y en a. Plus besoin d’ajouter une propriété manuellement ou de l’initialiser ensuite.

avec git diff vous allez découvrir toutes les modifications appliqués

Maintenant que nous sommes sur PHP 8 passons à la conversion de nos annotations PHP en des attributs PHP 8. Vous pouvez tout faire manuellement sinon passer par Rector

Exemple de changement

Nous allons poursuivre notre migration en ajoutant des « Property Types » aux entités.

Il est important de faire attention à un détail : même si un champ est obligatoire dans la base de données, pensez toujours à déclarer la propriété avec un type nullable, comme ?VotreType $champs En effet, lors de l’instanciation de l’objet, la propriété ne sera pas immédiatement renseignée

Ajouter des types de propriété est vraiment pratique : cela rend notre code plus propre et plus précis. Mais il y a un autre avantage majeur : nous n’avons plus besoin de spécifier targetEntity Doctrine est maintenant capable de le deviner tout seul. Alors, supprimez cette ligne pour les relations ManyToOne

Un point à clarifier concernant les relations OneToMany : les propriétés qui utilisent ce type de relation sont souvent initialisées dans le constructeur.

#[ORM\OneToMany(targetEntity: Foo::class, mappedBy: 'bar')]
 private $foo;

public function __construct()
{
  $this->foo = new ArrayCollection();
}

Cette fois-ci, nous pouvons typer la propriété en tant que Collection au lieu de ArrayCollection, tout en maintenant son initialisation dans le constructeur. (Collection est une interface, penser aux principses SOLID) . Nous devons utiliser Collection, car lorsque nous récupérons une donnée depuis la base de données et accédons ensuite à la propriété concernée. Doctrine la remplacera par un objet différent : un PersistentCollection. Ainsi, cette propriété peut être une ArrayCollection ou une PersistentCollection mais dans tous les cas, elle implémentera l’interface Collection.

Utiliser les types de propriétés permettra d’avoir un code plus précis et moins de configuration Doctrine

Si vous avez une entité User, alors ouvrez le fichier concerné. En plus de UserInterface, ajoutez une seconde interface : PasswordAuthenticatedUserInterface. Jusqu’à récemment, UserInterface contenait de nombreuses méthodes, y compris getPassword(). Dans Symfony 6, la méthode getPassword() a été retirée de UserInterface. Vous devez donc toujours implémenter UserInterface, mais la méthode getPassword() et l’interface PasswordAuthenticatedUserInterface deviennent optionnelles.

De même dans Symfony 6 la méthode getUsername() a été renommée en getUserIdentifier()

Mais le plus grand changement dans le système de sécurité de Symfony se trouve dans le fichier config/packages/security.yaml. C’est la clé enable_authenticator_manager

Cette ligne nous permet de passer de l’ancien système de sécurité au nouveau. Cela signifie que tous les mécanismes d’authentification – comme un authentificateur personnalisé, form_login ou http_basic commenceront soudainement à utiliser un tout nouveau système en coulisses

Si vous utilisez l’un des systèmes d’authentification intégrés, comme form_login ou http_basic, vous ne remarquerez probablement aucun changement. Vous pouvez activer le nouveau système en définissant cette option sur true et tout fonctionnera exactement comme avant. Le nouveau système de sécurité est une refonte interne destinée à rendre le code central plus lisible et à offrir plus de flexibilité

Cependant, si vous avez des authentificateurs personnalisés basés sur Guard vous devrez les convertir au nouveau système d’authentification.

Vous allez remarquer que votre Authenticator étend d’une autre classe AbstractLoginFormAuthenticator

Dans l’ancien système Guard, l’authentification était répartie en plusieurs méthodes : getCredentials(), où nous récupérions certaines informations, getUser(), où nous trouvions l’objet Utilisateur, et checkCredentials(), où nous vérifions le mot de passe. Désormais, ces trois étapes sont combinées dans la méthode authenticate()

Donc nous sommes passés de ceci

à cela

N’oubliez pas d’activer le nouveau système dans le fichier config/security.yaml

enable_authenticator_manager: true

custom_authenticator:
    - App\Security\LoginFormAuthenticator

À l’origine, nous avions des encoders. Cela indiquait à Symfony quel algorithme utiliser pour hacher les mots de passe. Cela a été renommé en password_hashers.

password_hashers:
     Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'

Notez aussi que dans Symfony 6, IS_AUTHENTICATED_ANONYMOUSLY a été remplacé par PUBLIC_ACCESS. Vous devez donc l’utiliser à ces deux endroits.

access_control:
    - { path: ^/logout, role: PUBLIC_ACCESS }
    - { path: ^/admin/login, roles: PUBLIC_ACCESS }
    - { path: ^/admin, roles: ROLE_ADMIN }

Dans votre fichier composer, assurez-vous de changer toutes les versions 5.4.* en 6.0.*. pour les packages qui débute par symfony/

symfony composer up 'symfony/*' or symfony composer up

Si vous rencontrez des problèmes lors de l’exécution de la commande, vous pouvez vérifier avec.

symfony composer outdated

Il faut vérifier le comportement de l’application en vidant le cache, ça peut y arriver que vous lancez d’autres symfony composer require package/package

Maintenant que nous sommes passés de Symfony 5.4 à 6.0, il est possible que certaines recipes aient de nouvelles versions disponibles pour une mise à jour.

symfony composer recipes:update

C’est à vous de gérer les conflits et vérifier le comportement de l’application

Enfin, maintenant que nous sommes sur Symfony 6, nous pouvons supprimer un peu de code qui était uniquement nécessaire pour assurer la compatibilité avec Symfony 5

Si vous allez sur symfony.com/blog, les nouveautés y sont très bien documentées. Cliquez sur « Living on the Edge ». Vous y trouverez des articles de blog classés par version

Dans Symfony 5.3, il y a une nouvelle méthode pratique lorsque vous rendez un template avec un formulaire ! Au lieu d’utiliser render(), vous pouvez utiliser renderForm(). La seule différence supplémentaire est que vous pouvez supprimer l’appel à ->createView()

Cela entraîne également un autre changement, moins visible. Si une erreur de validation se produit, votre contrôleur renverra désormais une réponse avec un code de statut 422

Ce n’est pas encore terminé ! Nous vous donnons rendez-vous pour un autre tutoriel dédié à Symfony 7.

Plus de détails ici : https://symfonycasts.com/screencast/symfony6-upgrade