Von Symfony 2/3 zu Symfony 7: Eine schrittweise Migrationsstrategie

#Symfony 2 zu 7 Migration
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Gründer & Lead Developer

Experte für Softwareentwicklung und Legacy-Code-Optimierung

Eine Symfony-2- oder Symfony-3-Anwendung, die heute in Produktion laeuft, hat typischerweise Jahre voller Featureerweiterungen, Teamwechsel und angesammelter Workarounds hinter sich. Eine direkte Migration von Symfony 2 auf 7 ist keine Migration - es ist eine Neuentwicklung mit vertrauter Verzeichnisstruktur. Die Luecke zwischen diesen Versionen umfasst fuenf Major-Releases, vier PHP-Mindestanforderungen, eine komplett ersetzte Security-Schicht, die Einfuehrung von Symfony Flex und die Entfernung dutzender APIs, auf die dein Codebase fast sicher angewiesen ist.

Teams, die diese Art von Legacy-Symfony-Upgrade erfolgreich durchfuehren, tun dies inkrementell. Sie behandeln jeden LTS-Release als stabilen Haltepunkt, sichern die Fortschritte und machen weiter. Teams, die scheitern, versuchen den vollstaendigen Sprung und stellen fest, dass ihre 40.000 Zeilen umfassende Codebasis keine saubere Schnittlinie hat.

Dieser Leitfaden erklaert den realistischen Pfad: warum du bei 4.4 LTS und 6.4 LTS haltmachst, was an jeder Grenze bricht, und wie du die Arbeit so strukturierst, dass dein Produkt weiter ausgeliefert wird, waehrend die Migration laeuft.

Warum du nicht direkt zu Symfony 7 springen kannst

Symfonys Deprecation-Modell ist der Kerngrund dafuer. Jede Major-Version entfernt alles, was in der vorherigen Minor-Serie als deprecated markiert wurde. Symfony 3.4 ist der Deprecation-Spiegel von Symfony 4.0: Alles, was in 3.x deprecated wurde, wird in 4.0 entfernt. Symfony 4.4 ist der Spiegel von 5.0, und Symfony 6.4 ist der Spiegel von 7.0.

Wenn du auf Symfony 2.8 oder 3.4 bist, nutzt dein Codebase fast sicher APIs, die in Symfony 4.0 entfernt wurden. Der Service-Container, die Security-Firewall-Konfiguration, die Form-Extension-Architektur, der Event-Dispatcher, das YAML/XML-Routing-Format - all diese wurden zwischen Major-Versionen grundlegend veraendert. Du kannst diese nicht bei Symfony 7.0 beheben; du behebst sie an jeder Zwischengrenze, wo das Framework noch laeuft, aber laut mitteilt, was in der naechsten Major-Version brechen wird.

Der praktische Migrationspfad sieht so aus:

  • Symfony 2.x oder 3.x zu 3.4 LTS (alle Deprecations bereinigen, noch auf 3.x)
  • Symfony 3.4 zu 4.4 LTS (PHP 7.1+ erforderlich; Flex einfuehren, Bundle-Entfernung behandeln, DI-Konfiguration anpassen)
  • Symfony 4.4 zu 6.4 LTS (PHP 8.1+ erforderlich; neues Security-System, typisierte Properties, attributbasiertes Routing)
  • Symfony 6.4 zu 7.x (PHP 8.2+ erforderlich; alle 6.x-Deprecations entfernen, Typsignaturen verfeinern)

Jeder Schritt hat einen klar definierten Umfang. Jeder LTS-Release ist eine stabile Plattform, auf der du pausieren, in Produktion laufen und das Team erholen lassen kannst, bevor der naechste Abschnitt beginnt.

Schritt 1: Zuerst Symfony 3.4 LTS erreichen

Wenn du auf einem 2.x oder einem fruehem 3.x-Release bist, besteht die erste Aufgabe darin, 3.4 LTS zu erreichen - den letzten Release vor der 4.x-Grenze. Symfony 3.4 laeuft minimal mit PHP 5.5.9, obwohl Produktionsdeployments schon aus Performance- und Sicherheitsgruenden auf PHP 7.x laufen sollten.

Bei 3.4 aktivierst du den Deprecation-Handler in deiner Testumgebung. Symfony liefert einen DebugClassLoader und die Umgebungsvariable SYMFONY_DEPRECATIONS_HELPER fuer PHPUnit mit. Setze sie auf max[total]=0 und fuehre deine gesamte Testsuite aus. Jeder Deprecation-Hinweis ist ein Arbeitspaket fuer die 4.0-Grenze.

Die haeufigsten Symfony-3.x-Deprecations, die das 4.0-Upgrade blockieren, sind: die Nutzung der deprecated AbstractController-Elternkette, Bundle-Vererbung (ein in 4.0 entferntes Muster), Controller als Services ueber den alten container-Shortcut, Form-Event-Listener mit entfernten Optionsnamen und eigene Security-Voter, die noch nicht auf die moderne Signatur supports() und voteOnAttribute() umgestellt wurden. Behebe all das, bevor du die Versionseinschraenkung in der composer.json aenderst.

Schritt 2: Der Symfony-4.4-LTS-Sprung (der schwierigste Schritt)

Der Sprung von 3.4 auf 4.4 ist typischerweise der storendste Abschnitt einer Symfony-2-zu-7-Migration. Drei Veraenderungen verursachen die groesste Reibung: der Wechsel zu Symfony Flex, die Entfernung der Bundle-Vererbung und die Einfuehrung von Autowiring und Autokonfiguration als Standards.

Symfony Flex und die neue Verzeichnisstruktur. Symfony 4 fuehrte ein neues Application-Skeleton mit deutlich anderem Layout ein. Das Verzeichnis app/ ist verschwunden; die Konfiguration wandert nach config/, der Kernel nach src/, und die Bundle-Registrierung erfolgt ueber config/bundles.php statt AppKernel.php. Wenn du eine bestehende Anwendung statt eines frischen Skeletons upgradest, musst du diese Struktur manuell migrieren. Der Flex-Upgrade-Leitfaden in der offiziellen Dokumentation deckt die Verzeichniszuordnung Schritt fuer Schritt ab.

Bundle-Entfernung. Die Symfony Standard Edition buendelte SwiftmailerBundle, MonologBundle, TwigBundle und andere. Mit 4.x wurden mehrere davon entweder zu eigenstaendigen Paketen, wurden ersetzt oder erfordern explizite Installation. Noch wichtiger: Wenn deine Anwendung eine eigene Bundle-Hierarchie mit Bundle-Vererbung zum Ueberschreiben von Templates und Controllern nutzte, existiert dieses Muster in 4.x nicht mehr. Du musst ueberschriebene Templates nach templates/bundles/ verschieben und alle Controller-Ueberschreibungen umstrukturieren.

PHP-Versionsanforderung. Symfony 4.4 erfordert mindestens PHP 7.1.3. Wenn dein Server noch PHP 5.6 oder 7.0 betreibt, muss das PHP-Upgrade vor oder parallel zur Symfony-4.4-Migration erfolgen. Dies ist typischerweise eine Infra-Aufgabe, die parallel zur Deprecation-Bereinigung laeuft.

Die Veraenderungen am Security-Komponent. Symfony 4.x begann, die alten Authentication-Provider zugunsten des neuen Authenticator-basierten Systems zu deprecaten, das in 5.x zum Standard wurde. Wenn deine Anwendung ein eigenes Authentication-System hat - in Symfony-2/3-Anwendungen haeufig -, plane mindestens einen Sprint fuer Security-Refactoring in diesem Abschnitt ein.

Nach dem Erreichen von 4.4 pausierst du, deployest auf Staging und laesst die Anwendung mindestens einen Sprint-Zyklus in Produktion einweichen, bevor du weitermachst.

Schritt 3: Symfony 4.4 zu Symfony 6.4 LTS

Der Sprung von 4.4 auf 6.4 ist smoother als der vorherige, aber die PHP-Anforderung ist eine erhebliche Huerde. Symfony 6.x erfordert PHP 8.0 minimal; Symfony 6.4 LTS empfiehlt PHP 8.1 fuer den vollen Funktionsumfang einschliesslich Enums und Fibers. Wenn deine Anwendung noch auf PHP 7.x laeuft, ist das PHP-Upgrade der erste Meilenstein in diesem Abschnitt.

Die wirkungsvollste Veraenderung zwischen Symfony 4.x und 6.x ist das Security-System. Das Authenticator-basierte Security-Komponent ist in Symfony 6.x der Standard, und der alte form_login-, http_basic- und Custom-Provider-Ansatz wird entfernt. Wenn du eine Legacy-Security-Konfiguration betrieben hast, musst du sie in diesem Schritt neu schreiben. Die neue Architektur ist sauberer und besser testbar, aber die Migration ist nicht mechanisch - sie erfordert ein Verstaendnis der Authentication-Flows der Anwendung.

Symfony 6.x setzt auch staerker auf PHP-8-Attribute fuer Routing und Dependency Injection. Du bist nicht gezwungen, sie zu nutzen, aber wenn deine Controller noch @Route-Annotations tragen, wirst du in 6.4 Deprecation-Warnungen sehen, die auf die attributbasierten Alternativen hinweisen. Dies ist ein guter Zeitpunkt fuer den Wechsel, besonders wenn der Codebase bereits auf PHP 8.1 laeuft.

Doctrine ORM ist ein weiterer Bereich zum Pruefen. Symfony 6.4 funktioniert mit ORM 2.x und 3.x, aber die Mapping-Konfiguration und Query-Builder-API haben sich weiterentwickelt. Wenn deine Entities XML-, YAML- oder Annotation-Mapping verwenden, validiere deren Kompatibilitaet mit der Ziel-doctrine/orm-Version, bevor du Symfony upgradest.

Nach dem Erreichen von 6.4 LTS fuehre erneut die vollstaendige Deprecation-Pruefung aus:

bin/console debug:container --deprecations

Jeder Eintrag in dieser Ausgabe ist eine konkrete Aufgabe fuer den letzten Schritt. Symfony 6.4 ist genau aus diesem Grund die vorgesehene Bruecke zu 7.0: Es legt alle 7.0-Entfernungen als Warnungen offen, waehrend die Anwendung voll funktionsfaehig bleibt.

Schritt 4: Symfony 6.4 zu Symfony 7.x

Der Sprung von 6.4 auf 7.0 ist der mechanischste der vier Abschnitte, vorausgesetzt du hast das 6.4-Deprecation-Log bereinigt. PHP 8.2 ist erforderlich; PHP 8.3 wird empfohlen. Wenn du waehrend des 6.4-LTS-Lebenszyklus durchgehend Deprecations behoben hast, ist dieser letzte Schritt typischerweise ein Composer-Versions-Bump gefolgt von einer gezielten Analyse von Typsignatur-Abweichungen.

Die haeufigsten Probleme an dieser Grenze sind: strengere Methodensignaturen in erweiterten Framework-Klassen (Voter, Event-Subscriber, Form-Types), die Entfernung der letzten Legacy-Security-Aliases und strengere Constructor-Promotion-Anforderungen bei einigen Symfony-Komponenten. Die offizielle Datei UPGRADE-7.0.md im Symfony-Repository listet jede Veraenderung auf; sie vor dem Versions-Bump zu lesen ist die beste Zeitinvestition in diesem Abschnitt.

Fuehre die Testsuite unmittelbar nach dem Versions-Bump aus und bearbeite Fehler nach Schweregrad. Integrationstests, die vollstaendige Request-Zyklen abdecken, fangen die realistischsten Probleme auf. Unit-Tests einzelner Klassen bestehen oft, auch wenn die Verbindungen zwischen ihnen nicht funktionieren.

Features waehrend einer mehrjaehrigen Migration weiter ausliefern

Der haeufigste Fehler bei Legacy-Symfony-Upgrade-Projekten ist das Stoppen der Feature-Entwicklung, waehrend die Migration laeuft. Bei einem Codebase jeder bedeutenden Groesse ist das nicht akzeptabel. Die Migration muss parallel zum Produkt laufen.

Der praktische Ansatz ist das Strangler-Fig-Pattern, angewendet auf Symfony-Anwendungsebene. Neue Features werden so entwickelt, dass sie mit der aktuellen und der Ziel-Framework-Version kompatibel sind. Ein Service, der fuer Symfony 4.4 entwickelt wurde, kann auf 6.4 laufen, wenn er deprecated APIs vermeidet. Ein neuer Controller, der mit Attributen statt Annotations geschrieben wurde, funktioniert auf beiden, wenn die Symfony-Version beide unterstuetzt.

In der Praxis bedeutet das eine strikte Regel waehrend der Migrationsperiode: Kein neuer Code darf deprecated APIs verwenden. Die Migrationsarbeit - das Beheben alter deprecated Nutzungen - ist ein separater Track von Feature-Arbeit. Beide Tracks werden geschaetzt und geplant. Keiner blockiert den anderen.

Fuer groessere Teams funktioniert ein Branch-basierter Ansatz: Ein lang laufender symfony-migration-Branch, der Cherry-Picks von Nicht-Migrations-Features erhalt und die Migrationsarbeit inkrementell zurueckmergt. Fuer kleinere Teams geben Feature-Flags an neuen Services - beide Implementierungen hinter einem Toggle laufen - einen risikoaermeren Mechanismus, um migrierten Code zu testen ohne harte Umstellung.

Wenn der Sprung zu weit ist: Parallele Framework-Strategien

Einige Anwendungen haben Schichten, die schlicht inkompatibel mit den Zwischensymfony-Versionen sind. Ein stark angepasstes Bundle, das interne Symfony-APIs nutzt, die in 4.0 entfernt wurden, ein Custom-Authentication-Provider mit tiefen Kernel-Event-Hooks, oder eine Form-Extension, die gegen Symfony-2-era-Interfaces gebaut wurde - diese koennen den inkrementellen Pfad teurer machen als eine alternative Strategie.

In diesen Faellen ist ein paralleler Framework-Ansatz einen Blick wert: Die alte Symfony-Anwendung parallel zu einem sauberen Symfony-7-Skeleton betreiben und Endpoints einzeln ueber einen Reverse-Proxy oder eine gemeinsame Datenbankschicht migrieren. Neue Arbeit erfolgt in der modernen Anwendung; Legacy-Routen werden nach Prioritaet migriert und die alte Anwendung wird ausser Betrieb genommen, sobald die letzte Route bestaetigt ist.

Diese Strategie kostet mehr im Aufbau, vermeidet aber das Szenario, in dem ein inkompatibles Bundle die gesamte Migration monatelang als Geisel haelt. Sie funktioniert am besten, wenn die Anwendung klare Bounded Contexts hat - eine Public-API, ein Admin-Backend, ein Background-Job-System - die unabhaengig migriert werden koennen.

Eine Anmerkung zu PHP-Versionen und technischen Schulden

Eine Symfony-2- oder 3-Anwendung, die noch nie modernisiert wurde, laeuft fast sicher auf einer PHP-Version, die ihr End-of-Life erreicht hat. PHP 5.6 erreichte EOL im Dezember 2018. PHP 7.4 beendete den Support im November 2022. Das Laufen auf einer nicht unterstuetzten PHP-Version ist ein Sicherheitsrisiko, unabhaengig von der Symfony-Version.

Plane das PHP-Upgrade als Voraussetzung fuer die Symfony-Migration, nicht als Nachgedanken. Die sicherste Reihenfolge ist: zuerst PHP-Upgrade (mit einer Symfony-Version, die sowohl die alte als auch die neue PHP-Version unterstuetzt), dann Symfony-Upgrade. Symfony 3.4 auf PHP 7.4 zu betreiben ist ein gueltiger Zwischenzustand, der die PHP-technischen Schulden abbaut, bevor die Framework-Arbeit beginnt.

Fuer Anwendungen, bei denen eine vollstaendige Migration noch nicht finanziert ist, ist der minimal vertretbare Zustand PHP 8.1 und Symfony 6.4 LTS, das bis November 2027 Sicherheitsfixes erhaelt. Das gibt eine bedeutende Startbahn, um die 7.x-Migration zu planen, ohne die Anwendung auf einer nicht unterstuetzten Plattform zu belassen.

Was eine Legacy-Symfony-Migration zum Erfolg fuehrt

Nach vielen dieser Migrationen sind die Muster, die Erfolg zuverlaessig voraussagen, genauso organisatorisch wie technisch. Teams, die erfolgreich sind, behandeln die Migration als Produktinitiative mit expliziten Meilensteinen, nicht als Hintergrundingenieurarbeit, die erledigt wird, wenn Kapazitaet vorhanden ist. Sie definieren "fertig" fuer jeden LTS-Schritt - null Deprecation-Warnungen, vollstaendige Testsuite besteht, in Produktion deployed - bevor sie den naechsten Abschnitt beginnen.

Der Symfony-Codebase wird sich nicht selbst migrieren. Aber der Pfad ist klar definiert, die LTS-Releases sind zuverlaessige Haltepunkte, und der Aufwand ist vollstaendig vorhersehbar, wenn du von einem klaren Deprecation-Inventar an jeder Grenze startest.

Wenn du auf eine Symfony-2- oder 3-Codebasis schaust und fragst, wo du anfangen sollst, lautet die Antwort immer gleich: Fuehre den Deprecation-Report aus, quantifiziere die Arbeit, um 3.4 sauber zu erreichen, und mache das zum ersten Meilenstein. Alles andere ergibt sich daraus.


Wenn dein Team eine Legacy-Symfony-Migration vor sich hat und erfahrene Unterstuetzung benoetigt - ob fuer den vollstaendigen Mehrversions-Pfad oder eine spezifisch schwierige Grenze - melde dich unter hello@wolf-tech.io oder besuche wolf-tech.io/services/legacy-code-optimization, um mehr ueber unseren Ansatz zur Legacy-Modernisierung zu erfahren.