SF
{ }
Symfony · Dependency Injection · autoconfigure · DI-Tags
autoconfigure in Symfony:
DI-Tags ohne manuelle Konfiguration

In Symfony bedeutete das Registrieren von Event Listenern, Voters, Commands und Twig-Extensions lange: Interface implementieren und separat in services.yaml einen Tag-Eintrag schreiben. Das autoconfigure-Feature erkennt bekannte Interfaces automatisch und vergibt die notwendigen DI-Tags, ohne dass ein einziger Tag manuell geschrieben werden muss.

16 Min. Lesezeit autoconfigure · registerForAutoconfiguration · PHP Attribute · DI-Tags Symfony 5.x / 6.x / 7.x · PHP 8.1+

1. Was ist autoconfigure und warum existiert es?

Symfony's Dependency Injection-Container arbeitet mit Tags: Ein Service bekommt einen Tag wie kernel.event_listener, twig.extension oder security.voter, und Symfony weiß dadurch, in welche interne Infrastruktur er einzuhängen ist. Historisch wurden diese Tags manuell in services.yaml eingetragen. Das führte dazu, dass das Implementieren eines Interfaces nicht ausreichte — man musste zusätzlich in YAML nachziehen, und jede vergessene Zeile führte zu einem Service, der keine Wirkung hatte, ohne Fehlermeldung.

autoconfigure löst dieses Problem durch automatische Tag-Erkennung. Symfony kennt für viele seiner eigenen Interfaces die zugehörigen Tags und vergibt sie automatisch, wenn ein Service dieses Interface implementiert. EventSubscriberInterface bekommt automatisch den Tag kernel.event_subscriber. VoterInterface bekommt security.voter. Command-Unterklassen bekommen console.command. Das bedeutet: Interface implementieren genügt — Symfony erledigt den Rest. Die services.yaml-Einträge für Tags werden überflüssig.

Seit Symfony 5.3 funktioniert autoconfigure auch mit PHP-Attributen: Klassen mit bestimmten Attributen — wie #[AsEventListener], #[AsCommand] oder #[AsTwigComponent] — erhalten ihre Tags ebenfalls automatisch. Das macht autoconfigure zum zentralen Mechanismus für die Zero-YAML-DI-Konfiguration in modernen Symfony-Projekten. Der Container-Kompiler verarbeitet alle erkannten Interfaces und Attribute und erzeugt die Tags intern — der Entwickler sieht nur sauberen PHP-Code.

2. autoconfigure aktivieren und verifizieren

In einem Standard-Symfony-Projekt ist autoconfigure global für alle Services aktiviert, die unter dem App\-Namespace fallen. Die relevante Konfiguration findet sich in config/services.yaml: Der Eintrag _defaults: autoconfigure: true gilt für alle Services im selben Block. Wer autoconfigure für einen einzelnen Service deaktivieren möchte, setzt autoconfigure: false direkt am Service-Eintrag — das ist selten nötig, aber möglich, wenn ein Service bewusst keinen automatischen Tag erhalten soll.

Zur Verifikation, welche Tags ein Service erhalten hat, dient bin/console debug:container --show-tags App\EventListener\MyListener. Der Befehl zeigt alle Tags des Services — sowohl manuell eingetragene als auch automatisch durch autoconfigure vergebene. Wer sehen möchte, welche Services für ein bestimmtes Tag registriert sind, nutzt bin/console debug:container --tag=kernel.event_listener. Beide Befehle sind unverzichtbar beim Debugging von DI-Konfigurationen in Projekten, die intensiv autoconfigure nutzen.

Ein wichtiger Aspekt bei der Aktivierung: autoconfigure wirkt nur auf Services, die im Container registriert sind. Klassen, die nicht explizit als Services eingetragen und nicht durch den App\-Glob-Import erfasst werden, profitieren nicht von autoconfigure. Das ist relevant für Klassen in Verzeichnissen, die vom Standard-Import ausgenommen sind — z.B. Klassen in src/DataFixtures oder Klassen, die nur als Argument, nicht als Service genutzt werden. Der Glob-Import in services.yaml bestimmt, welche Klassen überhaupt als Services registriert werden.


# config/services.yaml — standard Symfony project configuration
services:
  _defaults:
    autowire: true       # Constructor parameters resolved automatically
    autoconfigure: true  # Interface-based tags applied automatically
    public: false        # All services private by default

  App\:
    resource: '../src/'
    exclude:
      - '../src/DependencyInjection/'
      - '../src/Entity/'
      - '../src/Kernel.php'

# With autoconfigure: true, NO manual tags needed for:
#   - EventSubscriberInterface  → kernel.event_subscriber
#   - VoterInterface            → security.voter
#   - AbstractCommand           → console.command
#   - MessageHandlerInterface   → messenger.message_handler
#   - TwigExtension             → twig.extension

# Disabling autoconfigure for a single service (rare):
# App\Service\SpecialService:
#   autoconfigure: false

3. Bekannte Interfaces und ihre automatischen Tags

Symfony kennt eine umfangreiche Liste von Interfaces, für die autoconfigure automatisch Tags vergibt. Die wichtigsten: EventSubscriberInterface bekommt kernel.event_subscriber. VoterInterface bekommt security.voter. AbstractCommand-Unterklassen bekommen console.command. MessageHandlerInterface bekommt messenger.message_handler. TwigExtension bekommt twig.extension. EncoderInterface-Implementierungen, Constraint-Validator-Klassen und Form-Type-Erweiterungen folgen demselben Muster.

Ein konkretes Beispiel: Wer in Symfony einen neuen Messenger-Handler implementiert, schreibt die Klasse, implementiert MessageHandlerInterface oder nutzt das #[AsMessageHandler]-Attribut — und der Handler ist sofort im Messenger registriert. Kein Tag in services.yaml, keine Konfiguration in messenger.yaml für den Handler selbst. Das autoconfigure-Feature verbindet die Interface-Implementierung direkt mit der Symfony-Infrastruktur, die dieses Interface erwartet. Das ist der eigentliche Mehrwert: Weniger Stellen, an denen derselbe Sachverhalt ausgedrückt werden muss.

Nicht alle Drittanbieter-Bundles nutzen autoconfigure gleich konsequent. Ältere Bundles erfordern möglicherweise noch manuelle Tags für ihre Erweiterungspunkte. Wer ein modernes Bundle entwickelt oder nutzt, sollte prüfen, ob es autoconfigure-Unterstützung bietet — das ist mittlerweile ein Qualitätsmerkmal guter Bundle-Architektur. Symfony selbst setzt es konsequent um: Alle internen Extension-Punkte nutzen autoconfigure, damit Entwickler nicht in Framework-Interna recherchieren müssen, um einen Service korrekt zu registrieren.

4. PHP-Attribute als autoconfigure-Trigger

Seit Symfony 5.3 können PHP-Attribute als autoconfigure-Trigger registriert werden. Ein Attribut auf einer Klasse oder Methode kann denselben Effekt haben wie eine Interface-Implementierung: Es bewirkt die automatische Vergabe eines DI-Tags. Das ist der Mechanismus hinter #[AsEventListener], #[AsCommand], #[AsMessageHandler] und allen anderen Symfony-Attributen, die DI-Konfiguration in den PHP-Code verlegen. Intern registriert Symfony für jedes dieser Attribute eine autoconfigure-Regel, die beim Container-Kompilieren ausgewertet wird.

Das Attribut-basierte autoconfigure ist in einem entscheidenden Punkt flexibler als das Interface-basierte: Attribute können Parameter tragen. #[AsEventListener(event: KernelEvents::REQUEST, priority: 50)] übermittelt nicht nur den Tag-Namen, sondern auch Event-Name und Priorität — Informationen, die bei der Interface-basierten Registrierung ausschließlich über services.yaml-Tag-Parameter übermittelt werden konnten. Mit PHP-Attributen gehören diese Parameter direkt zum PHP-Code und sind damit versionierbar, grep-bar und refactoring-freundlich.

Drittanbieter-Bundles können eigene Attribute definieren und diese als autoconfigure-Trigger registrieren. Das ermöglicht Bundle-Entwicklern, eine ergonomische Developer Experience zu bieten: Anwender des Bundles schreiben ein Attribut auf ihre Klasse, und der Bundle-DI-Pass verarbeitet das Attribut und konfiguriert den Service vollständig. Ein Beispiel aus der Praxis: Ein Rate-Limiting-Bundle könnte ein #[RateLimited(maxRequests: 10, period: 60)]-Attribut definieren, das auf einem Controller die notwendigen Tags und Parameter für den Rate-Limiting-Middleware-Stack setzt.


<?php

declare(strict_types=1);

// Custom attribute that triggers autoconfigure in a bundle or project
// src/DependencyInjection/Attribute/AsDataTransformer.php

namespace App\DependencyInjection\Attribute;

use Attribute;

/**
 * Marks a class as a data transformer — triggers autoconfigure DI tag.
 * Apply on classes implementing DataTransformerInterface.
 */
#[Attribute(Attribute::TARGET_CLASS)]
final class AsDataTransformer
{
    public function __construct(
        public readonly string $supports,  // FQCN of the class this transformer handles
        public readonly int $priority = 0,
    ) {}
}

// --- Registering the attribute as autoconfigure trigger ---
// src/DependencyInjection/AppExtension.php (or a Compiler Pass)

use App\DependencyInjection\Attribute\AsDataTransformer;
use Symfony\Component\DependencyInjection\ContainerBuilder;

// In your bundle's build() method or a CompilerPass:
public function build(ContainerBuilder $container): void
{
    $container->registerAttributeForAutoconfiguration(
        AsDataTransformer::class,
        static function (
            ChildDefinition $definition,
            AsDataTransformer $attribute,
            \Reflector $reflector,
        ): void {
            $definition->addTag('app.data_transformer', [
                'supports'  => $attribute->supports,
                'priority'  => $attribute->priority,
            ]);
        }
    );
}

// --- Using the attribute on a transformer class ---
#[AsDataTransformer(supports: ProductDto::class, priority: 10)]
final class ProductDataTransformer implements DataTransformerInterface
{
    public function transform(mixed $value): mixed
    {
        // Transformation logic here
        return $value;
    }
}

5. Eigene autoconfigure-Regeln definieren

Symfony stellt die Methode ContainerBuilder::registerForAutoconfiguration() bereit, um eigene Interface-zu-Tag-Regeln zu definieren. Diese Methode nimmt einen Interface-Namen und eine Closure, die für jeden Service aufgerufen wird, der dieses Interface implementiert. In der Closure kann die Service-Definition modifiziert werden: Tags hinzufügen, Methoden-Calls konfigurieren oder Argumente setzen. Das ist der Einstiegspunkt für eigene Bundle- oder Projekt-Erweiterungen, die autoconfigure-Konventionen für ihre Interfaces einführen wollen.

Ein praktischer Anwendungsfall für Projekte ohne Bundle: Eine eigene PriorityHandlerInterface-Schnittstelle, die von mehreren Klassen implementiert wird. Mit registerForAutoconfiguration() in einem eigenen Compiler Pass erhalten alle Implementierungen automatisch einen Tag mit der Priorität aus einer Interface-Konstante. Das ersetzt eine lange Liste manueller Tag-Einträge in services.yaml durch eine einzige Konfigurationsregel. Wer eine neue Implementierung hinzufügt, schreibt die Klasse, implementiert das Interface — und der Tag erscheint automatisch.

Für die Integration von autoconfigure-Regeln in Projekten ohne eigenes Bundle ist ein Compiler Pass der saubere Weg. Die Klasse implementiert CompilerPassInterface und wird im Kernel::build()-Callback registriert: $container->addCompilerPass(new MyAutoconfigurePass()). Im process()-Callback des Passes ruft man $container->registerForAutoconfiguration() auf. Das hält die autoconfigure-Regeln getrennt von der Service-Logik und macht sie leicht auffindbar.

6. autoconfigure in eigenen Bundles nutzen

Bundle-Entwickler definieren ihre autoconfigure-Regeln in der build()-Methode der Bundle-Klasse oder in einem Compiler Pass, der im Bundle registriert ist. Die Methode $container->registerAttributeForAutoconfiguration() verknüpft ein PHP-Attribut mit einer Konfigurationsclosure — das ist der moderne Weg für attribute-basiertes autoconfigure. Alternativ nutzt man $container->registerForAutoconfiguration() für Interface-basierte Regeln. Beide Ansätze können kombiniert werden: Ein Interface setzt den Basis-Tag, ein Attribut fügt optionale Parameter wie Priorität oder Alias hinzu.

Ein gut konzipiertes Bundle dokumentiert seine autoconfigure-Interfaces und Attribute explizit in der README: Welche Interfaces implementiert werden müssen, welche Attribute optional verfügbar sind und welche Tags daraus resultieren. Das macht das Bundle für Anwender transparent ohne YAML-Konfiguration. Das Testen von autoconfigure-Regeln erfolgt am besten über einen funktionalen Container-Test, der prüft, ob eine Test-Klasse mit dem Interface den erwarteten Tag erhält — bevor das Bundle in einem echten Projekt eingesetzt wird.


<?php

declare(strict_types=1);

namespace App\Bundle\TransformerBundle;

use App\Bundle\TransformerBundle\DependencyInjection\Attribute\AsDataTransformer;
use App\Bundle\TransformerBundle\Contract\DataTransformerInterface;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

/**
 * TransformerBundle — registers autoconfigure rules for transformers.
 * Implementors of DataTransformerInterface get tagged automatically.
 */
final class TransformerBundle extends AbstractBundle
{
    /**
     * Register autoconfigure rules for this bundle's extension points.
     */
    public function build(ContainerBuilder $container): void
    {
        parent::build($container);

        // Interface-based autoconfigure: all DataTransformerInterface implementations are tagged
        $container->registerForAutoconfiguration(DataTransformerInterface::class)
            ->addTag('transformer.data_transformer');

        // Attribute-based autoconfigure: #[AsDataTransformer] adds tag with metadata
        $container->registerAttributeForAutoconfiguration(
            AsDataTransformer::class,
            static function (
                ChildDefinition $definition,
                AsDataTransformer $attribute,
            ): void {
                $definition->addTag('transformer.data_transformer', [
                    'supports'  => $attribute->supports,
                    'priority'  => $attribute->priority,
                ]);
            }
        );
    }
}

// Any class implementing DataTransformerInterface automatically gets the tag.
// No services.yaml entry needed in the consuming project.

7. Debugging: Tags und autoconfigure prüfen

Das wichtigste Debugging-Tool für autoconfigure ist bin/console debug:container. Ohne Argument listet er alle Services im Container. Mit dem Servicenamen als Argument zeigt er alle Details: Klasse, Argumente, Tags und ob autoconfigure aktiv ist. Mit --tag=mein_tag listet er alle Services, die diesen Tag tragen — das beantwortet sofort die Frage, ob autoconfigure für einen Service wirkt. Ein Service ohne erwarteten Tag deutet entweder auf fehlendes autoconfigure, eine falsche Namespace-Konfiguration oder ein nicht-implementiertes Interface hin.

Wenn autoconfigure unerwartet nicht wirkt, hilft der Workflow: Prüfen ob der Service im Container ist (debug:container App\MyClass), prüfen ob autoconfigure aktiv ist (Ausgabe zeigt autoconfigure: true), prüfen ob das Interface korrekt implementiert ist (implements MyInterface mit vollem Namespace). Der häufigste Fehler: Das Interface-use-Statement fehlt oder verweist auf einen falschen Namespace. Das führt dazu, dass PHP die Klasse als nicht-implementierend betrachtet, obwohl der Methodenname übereinstimmt — ein Fehler, den der statische Analyzer sofort erkennen würde.

Interface / Attribut Automatischer Tag Symfony-Komponente Manuelle Alternative
EventSubscriberInterface kernel.event_subscriber EventDispatcher YAML-Tag-Eintrag
VoterInterface security.voter Security YAML-Tag-Eintrag
#[AsCommand] console.command Console YAML oder configure()
MessageHandlerInterface messenger.message_handler Messenger YAML-Tag mit handles-Attribut
AbstractExtension (Twig) twig.extension TwigBundle YAML-Tag-Eintrag

8. Grenzen von autoconfigure

autoconfigure ist kein Allheilmittel — es gibt Szenarien, in denen manuelle DI-Konfiguration notwendig bleibt. Tags mit komplexen Attributen, die zur Compile-Zeit nicht aus dem PHP-Code ableitbar sind, können nicht durch autoconfigure vergeben werden. Beispiel: Ein Tag, dessen Attributwert aus einer Umgebungsvariable stammt, die zur Compile-Zeit noch nicht bekannt ist. In solchen Fällen bleibt services.yaml die einzige Option.

Ein weiteres Limit betrifft den Scope: autoconfigure wirkt auf die gesamte Service-Definition, nicht auf einzelne Methoden. Attribut-basiertes autoconfigure kann zwar auf Methoden angewendete Attribute lesen (wie bei #[AsEventListener] auf Methoden), aber das erfordert eine dedizierte registerAttributeForAutoconfiguration()-Registrierung, die Reflection nutzt. Komplexe Registrierungslogik — z.B. Services, die sich gegenseitig kennen müssen — benötigt weiterhin Compiler Passes. autoconfigure ist ein Komfort-Feature für häufige Muster, kein Ersatz für die volle DI-Flexibilität.

9. Vergleich: Manuelle Tags vs. autoconfigure

Der Unterschied zwischen manueller Tag-Konfiguration und autoconfigure zeigt sich am deutlichsten in größeren Projekten: Wer zwanzig Event Subscriber hat, braucht mit autoconfigure keinen einzigen Tag-Eintrag in services.yaml. Wer dasselbe ohne autoconfigure abbildet, hat zwanzig Tag-Einträge — und jede Umbenennung einer Klasse erfordert eine Änderung in YAML. Das ist die tatsächliche Kosten-Reduktion: Nicht nur weniger Code beim Erstellen, sondern weniger Pflegeaufwand bei jeder Refaktorierung.

Auf der anderen Seite ist die Explizitheit von YAML-Tags manchmal ein Vorteil: Man sieht auf einen Blick, welche Services für welche Tags registriert sind, ohne den Code lesen zu müssen. Mit autoconfigure verteilt sich diese Information über PHP-Klassen. bin/console debug:container --tag=mein_tag liefert dieselbe Übersicht — aber es erfordert das aktive Abfragen. Für Teams, die Transparenz über Registrierungen bevorzugen, ist diese Umgewöhnung eine Investition, die sich durch reduzierte YAML-Pflege langfristig auszahlt.

Mironsoft

Symfony Dependency Injection, Bundle-Entwicklung und DI-Architektur

Symfony DI-Konfiguration modernisieren und YAML-Ballast loswerden?

Wir analysieren bestehende services.yaml-Konfigurationen, identifizieren manuelle Tags, die durch autoconfigure ersetzbar sind, und migrieren schrittweise zu modernem Attribut-basiertem DI — mit vollständiger Verifikation und Tests.

DI-Audit

Analyse aller manuellen Tags in services.yaml und Identifikation der autoconfigure-Migrationskandidaten

Migration

Schrittweise Umstellung auf autoconfigure und PHP-Attribute mit Verifikation über debug:container

Bundle-Entwicklung

Eigene autoconfigure-Regeln und PHP-Attribute für wiederverwendbare Bundle-Komponenten entwerfen

10. Zusammenfassung

Das autoconfigure-Feature in Symfony eliminiert manuelle DI-Tag-Einträge für alle bekannten Extension-Punkte des Frameworks. Interface-basierte Regeln vergeben Tags automatisch bei der Container-Kompilierung — EventSubscriberInterface, VoterInterface, MessageHandlerInterface und viele andere müssen nie mehr in YAML mit Tags versehen werden. PHP-Attribute wie #[AsEventListener] oder #[AsCommand] erweitern diesen Mechanismus um Parameter und machen die gesamte DI-Konfiguration zu typsicherem PHP-Code.

Eigene autoconfigure-Regeln lassen sich über registerForAutoconfiguration() und registerAttributeForAutoconfiguration() definieren — in Compiler Passes für Projekte und in der build()-Methode für Bundles. Das macht autoconfigure zu einem universellen Mechanismus für Zero-YAML-DI in modernen Symfony-Projekten. bin/console debug:container bleibt das zentrale Werkzeug zur Verifikation und zum Debugging.

Symfony autoconfigure — Das Wichtigste auf einen Blick

Aktivierung

autoconfigure: true in _defaults von services.yaml — in Standard-Symfony-Projekten bereits aktiv. Verifikation via debug:container.

Interface-Regeln

Bekannte Interfaces wie EventSubscriberInterface und VoterInterface erhalten automatisch ihre Tags — kein YAML nötig.

Eigene Regeln

registerForAutoconfiguration() und registerAttributeForAutoconfiguration() in Compiler Passes oder Bundle-build() definieren eigene Regeln.

Debugging

bin/console debug:container --tag=tag_name zeigt alle Services mit diesem Tag. debug:container App\MyClass zeigt alle Tags eines Services.

11. FAQ: autoconfigure in Symfony

1Was macht autoconfigure?
Vergibt DI-Tags automatisch basierend auf Interfaces und PHP-Attributen beim Container-Kompilieren. Eliminiert manuelle Tag-Einträge in services.yaml für alle bekannten Symfony-Extension-Punkte.
2Wie aktivieren?
autoconfigure: true in _defaults von services.yaml — in Symfony seit 4.4 Standard. Einzelne Services mit autoconfigure: false ausschließen möglich.
3Welche Interfaces erhalten Tags?
EventSubscriberInterface → kernel.event_subscriber. VoterInterface → security.voter. MessageHandlerInterface → messenger.message_handler. AbstractExtension → twig.extension. Und viele weitere.
4Eigene Regeln definieren?
registerForAutoconfiguration() in Compiler Pass oder build(). Für Attribute: registerAttributeForAutoconfiguration() mit Closure. Closure erhält ChildDefinition, Attribut-Instanz und Reflector.
5Tag-Vergabe prüfen?
bin/console debug:container App\MeinService zeigt alle Tags. debug:container --tag=mein_tag listet alle Services mit diesem Tag. Zentrales Debugging-Tool für autoconfigure-Fragen.
6PHP-Attribute unterstützt?
Ja — seit Symfony 5.3. #[AsEventListener], #[AsCommand], #[AsMessageHandler] sind autoconfigure-Trigger. Parameter im Attribut (Priorität, Event-Name) werden an den Tag weitergegeben.
7Grenzen von autoconfigure?
Keine Tag-Vergabe mit Werten aus Umgebungsvariablen (Compile-Zeit). Komplexe Service-Beziehungen brauchen weiterhin Compiler Passes. Kein vollständiger services.yaml-Ersatz.
8Probleme in Tests?
Nein — autoconfigure ist nur ein Compile-Feature. Services in Tests haben dieselben Tags wie in Produktion. Test-Services können autoconfigure: false erhalten wenn YAML-Overrides nötig sind.
9services.yaml komplett ersetzen?
Nein — nur Tag-Einträge für bekannte Interfaces werden überflüssig. Argumente, Parameter, Aliase und Factory-Definitionen bleiben in services.yaml. Ziel ist Eliminierung von Tag-Duplikation.
10Eigene Regeln testen?
Container-Integrationstest: ContainerBuilder erstellen, Bundle registrieren, Compiler-Passes ausführen, dann Tag-Vorhandensein prüfen. Alternativ: KernelTestCase + debug:container für manuelle Verifikation.