Désactiver des routes Symfony en production
Dans certains cas, il peut être nécessaire d’avoir des actions qui ne sont disponibles que lorsqu’on développe en local, mais pas en production. On peut penser au styleguide ou à des pages de debug par exemple. Voyons ensemble comment implémenter cela.
Section intitulée la-solution-rapideLa solution rapide
Pour désactiver une route rapidement, vous pourriez être tenté d’utiliser l’attribut When('dev')
au niveau du contrôleur :
use Symfony\Component\DependencyInjection\Attribute\When;
// …
#[When('dev')]
class StyleguideController extends AbstractController
{
Bien que cela permette que l’action ne soit plus exécutable en environnement de prod, elle a plusieurs inconvénients. Déjà, cela désactive complètement tout le controleur, et pas juste une action (ce qui n’est peut-être pas un souci si vous utilisez le pattern ADR
). Mais surtout, cela désactive le service attaché au contrôleur, sans supprimer la route associée à l’action. Lorsque que l’on accède à notre styleguide en env de prod, cela va donc déclencher l’exception suivante :
Uncaught PHP Exception InvalidArgumentException: "The controller for URI "/styleguide/" is not callable: Controller "App\Controller\StyleguideController" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?
Ce qui provoque une erreur 500. Je vous propose donc une autre solution qui a le mérite d’être plus granulaire et de provoquer une erreur 404 bien plus adaptée.
Section intitulée une-solution-plus-eleganteUne solution plus élégante
Pour ce faire, nous allons utiliser le système de condition au niveau de nos routes. Si la condition n’est pas respectée, alors la route ne sera pas active et une erreur 404 sera retournée.
Ces conditions vont utiliser le moteur de l’Expression Language et permettent de faire des conditions assez poussées, comme le montre la documentation :
#[Route('/action', name: action, condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'",
// expressions can also include config parameters:
// condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'"
)]
Par défaut, il est possible d’accéder à des paramètres de la requête ou la route (variables request
, params
et context
), à des vars d’env (env('XXX')
) ou encore à des services (service('xxx')
).
Dans notre cas, on souhaite que la condition se base sur l’env Symfony, et plus précisément, quand le mode debug est activé (ce qui est le cas par défaut pour les environnements dev
et test
). Mais il semblerait qu’il ne soit pas possible d’accéder aux paramètres enregistrés dans le container (peut-être une idée de contribution dans Symfony ?), donc pas possible de faire une condition à base de %kernel.debug%
.
Pour palier ça, on va chercher à appeler la méthode isDebug()
du kernel directement :
#[Route(path: '/styleguide', name: 'styleguide', condition: "service('kernel').isDebug()")]
Mais si on fait ça, on se retrouve avec l’erreur suivante :
Uncaught PHP Exception Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: "Service "kernel" not found: the container inside "Symfony\Component\DependencyInjection\Argument\ServiceLocator" is a smaller service locator that is empty…" at ServiceLocator.php line 133"
En effet, la fonction service()
des conditions n’a pas accès au conteneur principal de l’application Symfony, mais à un container différent et plus petit dans lequel il faut explicitement définir les services présents. Heureusement, cela se fait facilement avec l’attribut AsRoutingConditionService
à ajouter sur le service qui doit être mis dans ce conteneur dédié. On modifie donc notre kernel, en définissant au passage l’alias (kernel
dans notre cas) que prendra le service dans le conteneur :
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
+#[AsRoutingConditionService('kernel')]
class Kernel extends BaseKernel
{
use MicroKernelTrait;
Voilà, on est maintenant capable de désactiver certaines routes facilement en fonction de l’environnement Symfony actuel.
À bientôt pour de nouvelles astuces 😉
Edit du 04/10/2024 :
Section intitulée la-vraie-bonne-solutionLa vraie bonne solution 😅
L’attribut Route
de Symfony dispose en fait d’une propriété env
depuis Symfony 5.3 qui permet de faire plus ou moins la même chose avec encore moins de code à écrire.
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
Ces clients ont profité de notre expertise
Groupama Épargne Salariale digitalise son expérience client en leur permettant d’effectuer leurs versements d’épargne salariale en ligne. L’application offre aux entreprises une interface web claire et dynamique, composé d’un tunnel de versement complet : import des salariés via fichier Excel, rappel des contrats souscrits et des plans disponibles, …
Nous avons réalisé différentes applications métier à l’aide de technologies comme Symfony2 et Titanium. Arianespace s’est appuyé sur l’expertise reconnue de JoliCode pour mettre en place des applications sur mesure, testées et réalisées avec un haut niveau de qualité.
JoliCode a formé l’équipe de développement d’Evaneos aux bonnes pratiques pour l’écriture de tests unitaires efficaces et utiles. Nous en avons également profité pour mettre en place une plateforme d’intégration continue pour accompagner l’évolution de la plateforme.