Le 26 février 2026, par Maëva Grondin.
Le CompilerPass de Symfony
Lorsque vous créez un Bundle, vous avez souvent besoin de définir une configuration pour un service contenu dans un bundle, mais cette configuration dépend de l'application. C'est le rôle d'un CompilerPass.
Le cycle de compilation d'une application Symfony
Tout d'abord, il est intéressant de comprendre comment se construit une application Symfony. Ce processus se déclenche principalement lorsque le cache applicatif est vide.
Lorsqu'une requête arrive, le Kernel se charge de construire les services à partir des bundles et des configurations, puis de les mettre en cache. Le Kernel est le point d'entrée.
Lors de la phase de construction du conteneur, le Kernel injecte à chaque bundle le ContainerBuilder grâce à leur méthode build(), et ces mêmes bundles vont ajouter la liste de leurs CompilerPass au ContainerBuilder.
Durant cette phase, rien n'est instancié, Symfony se contente de recueillir la liste des définitions des différents services.
Puis, vient la phase de compilation. La cohérence des définitions est vérifiée, c'est à ce moment-là qu'interviennent les CompilerPass, et que peuvent se produire des erreurs si deux services s'appellent circulairement. Les CompilerPass vont modifier les définitions recueillies précédemment.
Enfin, ces services compilés dans un ServiceContainer sont mis en cache pour des performances maximales, généralement dans "var/cache/".

Le ServiceContainer (ou conteneur d'injection de dépendances)
Nous avons évoqué le ServiceContainer dans la partie précédente. Son rôle est de centraliser la création et la distribution de tous les objets PHP de l'application.
Ainsi, il connait la liste des services, et se charge de les instancier lorsqu'ils sont appelés par l'application.
Le CompilerPass
Le CompilerPass permet d'intervenir sur la configuration des services de manière dynamique, juste avant que le ServiceContainer ne soit compilé.
Cette configuration est généralement définie via un fichier yaml.
Où le ranger ?
Dans l'arborescence de votre Bundle Symfony, rendez-vous dans src > DependencyInjection > Compiler :
IDCI/
├─ DummyBundle/
│ ├─ src/
│ │ ├─ DependencyInjection
│ │ │ ├─ Compiler
│ │ │ │ ├─ DebugDataCompilerPass.php
│ │ ├─ DummyBundle.php
│ ├─ composer.json
Vous pouvez créer autant de CompilerPass que vous voulez.
Comment l'utiliser ?
Dans les exemples qui suivent, nous permettrons d'ajouter autant de lignes que désiré dans l'onglet HelloWorldBundle de la barre de debug grâce à une configuration statique, et nous créerons un DummyBundle pour ajouter ou modifier des lignes via un CompilerPass.
D'abord, faisons évoluer HelloWorldBundle pour lui permettre d'afficher des données additionnelles.
<?php
namespace IDCI\Bundle\HelloWorldBundle\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
class HelloWorldDataCollector extends DataCollector
{
public function __construct(
...
private array $extraData,
) {
}
public function collect(Request $request, Response $response, \Throwable $exception = null): void
{
...
$this->data['hello_world.extra_data'] = $this->extraData;
}
...
public function getExtraData(): array
{
return $this->data['hello_world.extra_data'];
}
public function addExtraData(array $data): void {
$key = array_search($data['label'], array_column($this->extraData, 'label'));
if (false !== $key) {
$this->extraData[$key] = $data;
return;
}
$this->extraData[] = $data;
}
}
Ajoutons maintenant des informations dans le fichier de configuration au niveau de l'application :
hello_world:
app:
name: 'test'
version: 'v0'
additional_datas:
- { label: 'Message', value: 'Hello World !' }
- { label: 'Actif', value: true }
- { label: 'Temps', value: 10 }
Et voici le résultat :

Maintenant, passons au DummyBundle, un CompilerPass doit implémenter la CompilerPassInterface. Cette interface ne définit qu'une seule méthode : process(ContainerBuilder $container).
<?php
namespace IDCI\Bundle\DummyBundle\DependencyInjection\Compiler;
use IDCI\Bundle\HelloWorldBundle\DataCollector\HelloWorldDataCollector;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AddExtraDebugDataCompilerPass implements CompilerPassInterface
{
public const EXTRA_DATA = [
[
'label' => 'Dummy',
'value' => 'v0',
],
[
'label' => 'Message',
'value' => 'Hello from Dummy !'
]
];
public function process(ContainerBuilder $container): void
{
if (!$container->has(HelloWorldDataCollector::class)) {
return;
}
$helloWorldDataCollectorDefinition = $container->findDefinition(HelloWorldDataCollector::class);
foreach (self::EXTRA_DATA as $data) {
$helloWorldDataCollectorDefinition->addMethodCall('addExtraData', [$data]);
}
}
}
Pour que notre CompilerPass soit pris en compte, il faut l'appeler dans la classe DummyBundle, dans le dossier src/ du Bundle.
<?php
namespace IDCI\Bundle\DummyBundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
use IDCI\Bundle\DummyBundle\DependencyInjection\Compiler\AddExtraDebugDataCompilerPass;
class DummyBundle extends AbstractBundle
{
public function build(ContainerBuilder $container): void
{
parent::build($container);
$container->addCompilerPass(new AddExtraDebugDataCompilerPass());
}
}
N'oublions pas de vider notre cache avant de visualiser le résultat. On peut constater que la valeur de l'entrée "Message" a été modifiée par le CompilerPass :

Vous pouvez visualiser sur ce schéma l'intervention du CompilerPass :

Voir HelloWorldBundle.
Voir DummyBundle.
Pour aller plus loin
L'injection de dépendances de Symfony est bien plus puissante que l'exemple qui illustre cet article où nous nous contentons d'injecter des données statiques.
Nous aurions aussi pu utiliser un système de tag afin d'ajouter des services qui calculeraient des informations dynamiques à afficher dans la barre de debug.
Un CompilerPass peut également servir à décorer des services, c'est-à-dire leur ajouter des logiques sans modifier leur code source, ou encore modifier la définition de services en fonction de l'environnement.
Conclusion
Dans cet article, nous aurons décortiqué les trois fondations d'une application Symfony.
D'abord, le cycle de compilation, activé lorsque le cache applicatif est vide. Le Kernel injecte le ContainerBuilder à tous les bundles qui, eux-mêmes, fournissent la liste de leurs CompilerPass au ServiceContainer. Puis, la cohérence de toutes les définitions de services est vérifiée, avec les modifications de services apportées par les CompilerPass. Enfin, tout est mis en cache.
Ensuite, le ServiceContainer (ou conteneur de services) qui connait toutes les définitions de services et les instancie au fil des besoins.
Enfin, un exemple d'usage des CompilerPass pour la modification dynamique de services.

