Symfony-7-Upgrade-Leitfaden: Was sich geändert hat und wie du migrierst
Öffne irgendeine Symfony-6.4-Anwendung, die schon eine Weile in Produktion läuft, und führe bin/console debug:container --deprecations aus. Die Ausgabe ist meist unangenehm: Dutzende Deprecation-Hinweise, die auf längst vergessene Bundles, veraltete Service-Definitionen und APIs zeigen, vor denen das Framework seit zwei Release-Zyklen warnt. Diese Ausgabe ist die Vorschau auf dein Symfony-7-Upgrade. Jede Deprecation in 6.4 ist ein harter Bruch in 7.0.
Symfony 7 ist kein disruptiver Rewrite. Es ist eine disziplinierte Aufräumaktion: alles entfernen, was in der 6.x-Linie veraltet wurde, das Typsystem verschärfen, den Dependency-Injection-Container vereinfachen und einige Komponenten verfeinern, die Inkonsistenzen angesammelt hatten. Für Teams, die ihre 6.4-Codebasis sauber gehalten haben, dauert das Upgrade ein paar Tage. Für Teams, die Deprecation-Fixes aufgeschoben haben, ist es ein fokussiertes, mehrwöchiges Engineering-Projekt. Dieser Leitfaden behandelt, was sich geändert hat, wie du dich vorbereitest und welche praktischen Migrationsschritte das Risiko unter Kontrolle halten.
Was sich in Symfony 7 tatsächlich geändert hat
Die Schlagzeile des Symfony-7-Upgrades ist einfach: Jede in 6.x veraltete öffentliche API wurde entfernt. Es gibt keine neuen Syntax-Überraschungen, keine radikalen Abweichungen vom Symfony-Programmiermodell und keinen erzwungenen Wechsel zu einer anderen Komponentenarchitektur. Wenn deine 6.4-Anwendung läuft, ohne Deprecation-Warnungen auszulösen, wird sie mit minimalen Änderungen mit ziemlicher Sicherheit auf 7.x laufen.
Die substanziellen Änderungen fallen in vier Kategorien.
Erstens ist das Typsystem strenger. Viele Komponentenmethoden, die zuvor gemischte oder implizit typisierte Parameter akzeptierten, verlangen jetzt explizite Skalar- oder Enum-Typen. Rückgabetypen bei internen Überschreibungen müssen exakt übereinstimmen - PHPs LSP-Prüfungen werden aggressiver durchgesetzt. Eigene Voter, Event-Subscriber und Form-Type-Extensions brauchen oft aktualisierte Methodensignaturen, um den neuen Verträgen der Elternklassen zu entsprechen.
Zweitens ist der Dependency-Injection-Container schlanker. Mehrere veraltete Service-IDs und Aliase wurden entfernt. Konfiguration, die sich auf Autowiring über den Klassennamen durch nicht-standardmäßige Aliase verließ, muss explizit gemacht werden. Die Container-Kompilierung ist schneller, aber weniger nachsichtig gegenüber nachlässiger Service-Verdrahtung.
Drittens erfuhren Security und HttpFoundation die größte Komponentenbereinigung. Das alte, in 5.4 entfernte Authentifizierungssystem ist vollständig verschwunden, einschließlich jedes Übergangshelfers, der eingeführt wurde, um alte und neue Authenticatoren zu überbrücken. Die Signatur von HttpFoundation Request::create() hat sich geändert, und mehrere Session-Handler wurden zugunsten der cache-basierten Alternativen entfernt.
Viertens haben einige Bundles neue Mindestanforderungen an PHP. Symfony 7.0 verlangt mindestens PHP 8.2. Symfony 7.1 empfiehlt PHP 8.3. Der Betrieb auf einer älteren PHP-Laufzeit ist keine Option - die Features auf Sprachebene, die Symfony 7 nutzt (readonly Klassen, First-Class-Callable-Syntax, Enum-Typen in Signaturen), lassen sich nicht per Polyfill nachbilden.
Das offizielle UPGRADE-7.0.md im Symfony-Repository listet jeden BC-Break nach Komponente auf. Es vor dem Start von Anfang bis Ende zu lesen, ist die wertvollste Stunde, die du in dieses Projekt stecken kannst.
Deine Codebasis vorbereiten: Grundlagenarbeit von Symfony 6 zu 7
Die Migration von Symfony 6 zu 7 ist dramatisch einfacher, wenn du von einer sauberen 6.4-LTS-Codebasis mit null Deprecation-Warnungen startest. Symfony 6.4 ist die ausgewiesene Upgrade-Brücke - alle 7.0-Deprecations sind in 6.4 als Warnungen verfügbar, was bedeutet, dass du sie beheben kannst, während du auf dem LTS-Release bleibst, und die Composer-Version erst umstellst, sobald die Warnungen weg sind.
Die praktische Abfolge ist: zuerst auf 6.4 upgraden, falls du noch nicht dort bist, Deprecation-Logging in Dev und Staging aktivieren, die Warnungen triagieren, sie im 6.4-Branch beheben, dann den eigentlichen 7.x-Versionssprung als nahezu leere Composer-Änderung durchführen.
Aktiviere Deprecation-Logging in der services.yaml:
services:
Symfony\Component\ErrorHandler\ErrorHandler:
arguments:
$logger: '@logger'
monolog:
channels: ['deprecation']
handlers:
deprecation:
type: stream
path: '%kernel.logs_dir%/deprecation.log'
channels: ['deprecation']
level: info
Führe deine vollständige Testsuite, deine CI-Integrationstests und, wenn möglich, ein Staging-Umfeld unter produktionsähnlichem Verkehr für ein bis zwei Tage aus. Das Deprecation-Log ist dein Migrations-Backlog. Einträge mit Stack-Traces, die in Vendor-Code zeigen, lösen sich meist auf, wenn du ein Bundle aktualisierst. Einträge, die in deinen eigenen Code zeigen, sind die, die Arbeit erfordern.
Ein besonders nützlicher Befehl für das Triaging des Backlogs:
bin/console debug:container --deprecations
Dieser listet nur Deprecations auf, die mit der Service-Konfiguration zusammenhängen, und ist oft der Ort, an dem die größten Gewinne liegen - das Entfernen eines veralteten Service-Alias oder die Aktualisierung einer Bundle-Konfiguration kann auf einen Schlag Dutzende einzelner Warnungen verstummen lassen.
Häufige Symfony-Deprecations, die du zuerst angehen solltest
Einige Symfony-Deprecations tauchen in fast jeder 6.x-Anwendung auf und sind eine Priorisierung wert, weil sie den Rest des Upgrades freischalten.
Die Deprecations der alten Security-Klasse. Jeder Controller oder Service, der noch Symfony\Component\Security\Core\Security typhintet, muss auf Symfony\Bundle\SecurityBundle\Security umstellen (anderer Namespace, gleiches Interface für gängige Methoden). Die Methoden getUser(), isGranted() und getToken() sind unverändert; nur die Import-Zeile ändert sich.
Veraltete Authenticator-Interfaces. Wenn deine Codebasis noch AuthenticatorInterface aus dem alten Security-System implementiert, migriere zur Basisklasse AbstractAuthenticator ab 5.4. Das ist eine größere Änderung, weil sich die Methodensignaturen unterscheiden, aber sie ist erforderlich - das alte Interface ist in 7.0 entfernt.
Annotationen zu Attributen. @Route, @Security, @ParamConverter und ähnliche Annotationen wurden zugunsten von PHP-8-Attributen (#[Route], #[IsGranted], #[MapEntity]) abgekündigt. Doctrine-Annotationen für Entities folgen demselben Muster - Attribute sind in 7.0 die verpflichtende Form. Eine Bulk-Rector-Regel kann 90 % dieser Umwandlung automatisch erledigen.
Die Konstruktorsignatur von Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository. Die alte Signatur ist weg. Repositories müssen den neuen Eltern-Konstruktor explizit mit der Registry und dem Entity-Klassennamen aufrufen.
Die Test-Assertions assertResponseIsSuccessful() und verwandte haben strengere Signaturen. Browser-Kit-Tests, die lose typisierte Erwartungen durchgehen ließen, schlagen fehl, bis sie aktualisiert werden.
Flex-Rezepte: Konfigurationsdrift und Rezept-Updates
Symfony-Flex-Rezepte haben sich zwischen 6.0 und 7.x erheblich weiterentwickelt. Viele Anwendungen sind im Laufe der Zeit von der rezeptgelieferten Konfiguration abgewichen - händische Änderungen an framework.yaml, security.yaml, routes.yaml - und dieser Drift ist der Ort, an dem Upgrade-Reibung auftaucht.
Nachdem du die Symfony-Versionsbeschränkungen in der composer.json aktualisiert hast, führe aus:
composer symfony:sync-recipes --force --verbose
Dies wendet die neuesten Rezeptversionen an. Flex zeigt für jede geänderte Datei ein Diff und fragt vor dem Überschreiben nach. Das Schlüsselwort ist fragt - akzeptiere die Änderungen niemals blind. Prüfe jedes Diff, gleiche deine Anpassungen mit den neuen Rezept-Standardwerten ab und committe die Ergebnisse als kleine, überprüfbare Änderungen.
Die Flex-Rezepte, die am wahrscheinlichsten eine Abstimmung erfordern, sind:
config/packages/framework.yaml - die Session-Handler-Konfiguration hat sich geändert, die Standard-CSRF-Einstellungen haben sich verschoben, und der Standard für http_method_override wurde umgekehrt.
config/packages/security.yaml - die Syntax für access_control und Firewall wurde strenger; die Behandlung anonymer Nutzer wurde vollständig entfernt.
config/packages/doctrine.yaml - die Standardwerte der ORM-Konfiguration haben sich geändert, und das auto_mapping-Verhalten für Bundles ist konservativer.
.env-Standardwerte - mehrere Umgebungsvariablen wurden umbenannt oder zusammengeführt.
Für jedes Rezept, das du stark überschreibst (ein häufiges Muster bei security.yaml), ziehe in Betracht, deine Anpassungen in eine separate Datei zu verlagern, die das Rezept nicht verwaltet - zum Beispiel config/packages/security_custom.yaml - und die rezeptverwaltete Datei so nah wie möglich am Upstream zu halten. Das zahlt sich bei jedem zukünftigen Upgrade aus.
Werkzeuge: Rector, PHPStan und der Symfony-Upgrade-Pfad
Ein Symfony-7-Upgrade ohne automatisierte Werkzeuge ist um eine Größenordnung schmerzhafter als eines mit ihnen. Drei Werkzeuge tragen den Großteil der Last.
Rector mit dem Symfony-Regelsatz automatisiert die mechanischen Refactorings: Annotation-zu-Attribut-Umwandlung, Umbenennungen veralteter Methoden, Interface-Migrationen und Aktualisierungen von Typsignaturen. Eine typische Rector-Konfiguration für dieses Upgrade:
// rector.php
use Rector\Config\RectorConfig;
use Rector\Symfony\Set\SymfonySetList;
use Rector\Symfony\Set\SymfonyLevelSetList;
use Rector\Set\ValueObject\LevelSetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/src',
__DIR__ . '/tests',
]);
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_82,
SymfonyLevelSetList::UP_TO_SYMFONY_70,
SymfonySetList::SYMFONY_CODE_QUALITY,
SymfonySetList::ANNOTATIONS_TO_ATTRIBUTES,
]);
};
Führe es gegen einen Test-Branch aus, prüfe das Diff in kleinen Häppchen und committe nach Regelsatz, damit die Git-Historie dokumentiert, was sich geändert hat und warum.
PHPStan mit der Symfony-Extension (phpstan/phpstan-symfony) fängt Container- und Service-Fehler ab, die Rector nicht erkennen kann. Es vor dem Upgrade auf Level 6 oder höher laufen zu lassen und dasselbe Level danach beizubehalten, ist die sauberste Validierung, dass die Service-Verdrahtung weiterhin funktioniert.
Symfonys eigene Befehle symfony check:security und symfony console lint:container validieren die Konfiguration auf Framework-Ebene und sollten Teil der CI-Pipeline für den Upgrade-Branch sein.
Das Upgrade durchführen und validieren
Das eigentliche composer update auf 7.x ist unspektakulär, wenn die Grundlagenarbeit getan ist. Eine typische Abfolge:
# 1. Symfony-Versionsbeschränkung aktualisieren
composer require 'symfony/*:^7.0' --no-update
# 2. Bundles aktualisieren, die einen passenden Major-Versionssprung verlangen
composer require 'doctrine/doctrine-bundle:^2.11' --no-update
composer require 'twig/twig:^3.8' --no-update
# 3. Vollständigen Abhängigkeitsgraphen auflösen
composer update --with-all-dependencies
# 4. Aktualisierte Flex-Rezepte anwenden
composer symfony:sync-recipes --force
# 5. Cache neu aufbauen und Linter ausführen
bin/console cache:clear
bin/console lint:container
bin/console debug:router
# 6. Testsuite ausführen
bin/phpunit
Der Schritt, der die meisten Probleme verursacht, ist composer update --with-all-dependencies - Drittanbieter-Bundles ohne 7.x-Unterstützung blockieren das Update. Die Lösung ist entweder das Upgrade auf eine neuere Bundle-Version, der Wechsel zu einer Alternative oder das vorübergehende Forken des Bundles und das Anpassen seiner Constraints. Das Ökosystem hat zu Beginn von 2026 weitgehend aufgeholt, aber Nischen-Bundles hinken manchmal hinterher.
Auf der Validierungsseite sollten einige Prüfungen bestehen, bevor du das Upgrade als abgeschlossen betrachtest. Die Anwendung bootet im Prod-Modus mit aktiviertem OPcache. Die vollständige Testsuite besteht ohne übersprungene Tests. Unter realistischem Verkehr erscheinen keine Deprecation-Warnungen im Log. Kritische Nutzerpfade funktionieren in einem Staging-Umfeld, das die Produktionskonfiguration spiegelt. Die Performance-Benchmarks sind gleich oder besser als 6.4 - Symfony 7 ist dank Container-Optimierungen typischerweise 5 bis 10 % schneller, also deutet eine Regression auf einen falsch konfigurierten Service oder ein Bundle hin, das nicht sauber upgegradet wurde.
Ein pragmatischer Weg nach vorn
Für die meisten Engineering-Teams in Berlin und der EU, die Symfony betreiben, lohnt sich das Upgrade auf 7.x eher bedacht als dringend. Symfony 6.4 LTS wird bis Ende 2027 unterstützt, was bedeutet, dass Teams ein echtes Zeitfenster haben, um die Migration um andere Prioritäten herum zu planen, statt sie zwischen Quartalen hineinzuquetschen.
Die Teams, die erfolgreich upgraden, teilen ein Muster: Sie behandeln "null Deprecations in Symfony 6.4" als erstklassiges Engineering-Ziel, investieren in Rector und PHPStan, bevor sie den Versionssprung beginnen, und teilen die Abstimmung der Flex-Rezepte in kleine, überprüfbare PRs auf. Die Teams, die sich abmühen, sind meist die, die versucht haben, das gesamte Upgrade in einem einzigen Branch durchzuziehen, während sie gleichzeitig Features ausgeliefert haben.
Wenn dein Team ein Symfony-7-Upgrade plant - oder auf einer Symfony-4- oder -5-Codebasis sitzt und sich fragt, wie der realistische Weg zu modernem Symfony aussieht - dann ist das genau die Art von Arbeit, bei der Wolf-Tech hilft. Wir haben diese Upgrades für Symfony-Anwendungen in ganz Deutschland und der EU durchgeführt, und die Kombination aus einem Code-Quality-Audit und gestaffelter Legacy-Code-Optimierung verwandelt ein angsteinflößendes Mehrmonatsprojekt in der Regel in ein planbares Engineering-Programm. Kontaktiere uns unter hello@wolf-tech.io oder besuche wolf-tech.io für eine kostenlose Beratung zu deinem Upgrade-Pfad.

