Symfony 7 wurde im November 2023 veröffentlicht und markiert einen wichtigen Schnitt in der Symfony-Geschichte: PHP 8.2 ist jetzt Voraussetzung, zahlreiche Deprecations aus Symfony 5 und 6 wurden endgültig entfernt, und das Framework hat an vielen Stellen aufgeräumt. Wer heute noch auf Symfony 6.4 entwickelt, sollte die Migration zu Symfony 7 einplanen — Symfony 6.4 wird bis November 2027 mit Sicherheits-Updates versorgt, aber neue Features gibt es nur noch in Symfony 7.
Dieser Guide beschreibt die Migration anhand eines typischen Symfony-6-Projekts mit Doctrine ORM, API Platform und dem Symfony Security Bundle.
Voraussetzungen
Bevor man beginnt, müssen einige Grundvoraussetzungen erfüllt sein:
- PHP 8.2 oder höher ist Pflicht. Symfony 7 nutzt aktiv PHP-8.2-Features wie
readonlyProperties. - Das Projekt sollte auf Symfony 6.4 laufen (der letzten 6.x-Version), weil alle Symfony-7-Deprecation-Warnungen dort schon sichtbar sind.
- PHPStan oder Psalm helfen, versteckte Probleme zu finden.
Zunächst prüft man, ob alle Deprecation-Warnungen aus Symfony 6 bereinigt sind:
ddev exec bin/console debug:container --deprecations
Idealerweise ist diese Liste leer, bevor man den Versions-Sprung wagt.
Schritt 1: composer.json anpassen
Der eigentliche Upgrade beginnt in der composer.json. Man erhöht die Symfony-Versionsbeschränkungen auf ^7.0:
{
"require": {
"php": ">=8.2",
"symfony/framework-bundle": "^7.0",
"symfony/console": "^7.0",
"symfony/doctrine-bridge": "^7.0",
"symfony/security-bundle": "^7.0",
"symfony/twig-bundle": "^7.0",
"symfony/validator": "^7.0",
"symfony/form": "^7.0",
"symfony/mailer": "^7.0",
"symfony/messenger": "^7.0"
}
}
Anschließend führt man das Update durch:
ddev exec composer update "symfony/*" --with-all-dependencies
Composer wird dabei Konflikte melden, wenn andere Pakete noch inkompatible Symfony-Versionsanforderungen haben. Häufige Kandidaten sind ältere Bundles oder API-Platform in einer älteren Hauptversion.
Schritt 2: Entfernte Features und Breaking Changes
Symfony 7 hat alle Klassen, Methoden und Parameter entfernt, die in Symfony 6 als @deprecated markiert waren. Hier die häufigsten Breaking Changes:
AbstractController: getDoctrine() entfernt
In Symfony 6 konnte man noch $this->getDoctrine() im Controller nutzen. Das ist in Symfony 7 weg. Die Lösung: Den EntityManagerInterface oder das ManagerRegistry per Dependency Injection einbinden.
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Repository\ProductRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ProductController extends AbstractController
{
public function __construct(
private readonly ProductRepository $productRepository,
) {
}
#[Route('/products', name: 'product_list')]
public function list(): Response
{
$products = $this->productRepository->findAll();
return $this->render('product/list.html.twig', [
'products' => $products,
]);
}
}
Routing-Annotationen: @Route durch #[Route] ersetzt
Das symfony/routing Bundle hat die alten Doctrine-Annotations komplett gestrichen. Wer noch @Route("/path") verwendet, muss auf PHP-Attribute umstellen:
<?php
declare(strict_types=1);
namespace App\Controller;
// Vorher (Symfony 6, deprecated):
// use Symfony\Component\Routing\Annotation\Route;
// Nachher (Symfony 7):
use Symfony\Component\Routing\Attribute\Route;
class MyController
{
#[Route('/my-path', name: 'my_route', methods: ['GET'])]
public function index(): Response
{
// ...
}
}
Security: AuthenticationEntryPointInterface geändert
Das Security-Subsystem hat einige Interface-Änderungen erhalten. Wer eigene Authenticators implementiert hat, muss prüfen ob die Methoden-Signaturen noch stimmen.
Serializer: ContextAwareNormalizerInterface entfernt
Eigene Normalizer, die dieses Interface implementiert haben, müssen auf die neue Schreibweise umgestellt werden, bei der der $context-Parameter direkt in normalize() und supportsNormalization() übergeben wird.
Schritt 3: PHP-8.2-Features nutzen
Symfony 7 setzt PHP 8.2 voraus — das ist eine gute Gelegenheit, den eigenen Code zu modernisieren.
Readonly Properties und Readonly Classes
Readonly Properties wurden in PHP 8.1 eingeführt. PHP 8.2 erweitert dieses Konzept um Readonly Classes — damit werden alle Properties einer Klasse automatisch als readonly markiert:
<?php
declare(strict_types=1);
namespace App\Model;
final class Money
{
public function __construct(
public readonly int $amount,
public readonly string $currency,
) {
}
public function add(Money $other): self
{
if ($this->currency !== $other->currency) {
throw new \InvalidArgumentException('Currency mismatch');
}
return new self($this->amount + $other->amount, $this->currency);
}
}
Fibers für asynchrone Verarbeitung
PHP 8.1 führte Fibers ein — eine vollständige Implementierung für kooperatives Multitasking auf niedrigem Level. Fibers ermöglichen es, Funktionen an beliebigen Stellen zu unterbrechen und später fortzusetzen, was besonders für asynchrone I/O-Operationen nützlich ist. Frameworks wie ReactPHP und AMPHP nutzen Fibers als Grundlage für ihre Event-Loops.
Schritt 4: Symfony-Flex-Recipes aktualisieren
Symfony Flex verwaltet Konfigurationsdateien. Nach dem Versions-Upgrade prüft man, ob neue Recipes verfügbar sind:
ddev exec composer recipes:update
Das zeigt, welche Bundles neue Konfigurationsvorlagen bereitstellen. Nicht jede Änderung muss übernommen werden, aber es lohnt sich, die Diffs zu prüfen.
Schritt 5: Deprecation-Warnungen in Symfony 7 beheben
Nach dem Upgrade auf Symfony 7 tauchen neue Deprecations auf — das sind Hinweise auf Features, die in Symfony 8 entfernt werden. Den Überblick behält man mit dem Profiler im dev-Modus oder mit PHPStan und dem Symfony-PHPStan-Plugin:
ddev exec composer require --dev phpstan/phpstan phpstan/phpstan-symfony
ddev exec vendor/bin/phpstan analyse src
Rector: Upgrade automatisieren
Viele der beschriebenen Änderungen lassen sich mit Rector automatisieren. Rector ist ein PHP-Refactoring-Tool, das den Abstract Syntax Tree (AST) analysiert und Code-Transformationen regelbasiert durchführt.
Installation
ddev exec composer require rector/rector --dev
Das Paket rector/rector-symfony ist bereits enthalten und muss nicht separat installiert werden.
Konfiguration
Rector erkennt die installierte Symfony-Version automatisch aus vendor/composer/installed.json und wendet die passenden Regeln an. Eine rector.php im Projektroot:
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([
__DIR__ . '/src',
__DIR__ . '/tests',
])
->withSkip([
__DIR__ . '/src/Kernel.php',
])
->withComposerBased(
symfony: true,
doctrine: true,
phpunit: true,
)
->withAttributesSets(
symfony: true,
doctrine: true,
)
->withPreparedSets(
symfonyCodeQuality: true,
symfonyConstructorInjection: true,
);
Was Rector automatisch erledigt
Rector kennt die konkreten Symfony-Deprecations und kann viele davon automatisch umschreiben:
getDoctrine()entfernen und durch Constructor Injection ersetzen@Route-Annotations zu#[Route]-Attributes konvertieren$defaultName/$defaultDescriptionin Console Commands durch#[AsCommand]ersetzen$form->createView()in Render-Aufrufen vereinfachen- Return-Type-Deklarationen ergänzen, die Symfony 7 voraussetzt
- Umbenannte Klassen automatisch aktualisieren (z.B.
RequestMatcherzuChainRequestMatcher)
Workflow
# 1. Dry-Run: Zeigt geplante Änderungen ohne sie auszuführen
ddev exec vendor/bin/rector process --dry-run
# 2. Änderungen anwenden
ddev exec vendor/bin/rector process
# 3. Code-Style korrigieren (Rector arbeitet auf AST-Ebene, Formatierung kann leiden)
ddev exec vendor/bin/php-cs-fixer fix
# 4. Tests laufen lassen
ddev exec vendor/bin/phpunit
Was Rector nicht kann
Rector arbeitet nur mit PHP-Code. Folgendes muss manuell erledigt werden:
- YAML/XML-Konfiguration: Änderungen an
services.yaml,routes.yaml,security.yaml - Twig-Templates: Umbenannte Filter, geänderte Funktionen
- Drittanbieter-Bundles: Inkompatibilitäten mit weniger verbreiteten Bundles
- Verhaltensänderungen: Wenn sich die Semantik einer Methode geändert hat (z.B. Exceptions statt
false) - Komplexe Interface-Änderungen: Wenn eigene Implementierungen neue Signaturen erfordern
Rector deckt erfahrungsgemäß 60–80% der mechanischen Upgrade-Arbeit ab. Die verbleibenden Anpassungen sind dann die, die tatsächlich menschliches Urteilsvermögen erfordern.
Häufige Stolpersteine
Drittanbieter-Bundles: Nicht alle Bundles sind sofort mit Symfony 7 kompatibel. Besonders ältere Bundles ohne aktive Wartung können Probleme machen. Hier hilft composer outdated und ein Blick in die GitHub-Issues des jeweiligen Bundles.
API Platform 3.x: API Platform 2.x ist nicht mit Symfony 7 kompatibel. Wer API Platform verwendet, muss zunächst auf Version 3.x migrieren.
Doctrine-Bundles: doctrine/doctrine-bundle 2.x ist mit Symfony 7 kompatibel, aber man sollte die Release Notes prüfen.
Fazit
Die Migration von Symfony 6 auf 7 ist überschaubar, wenn man das Projekt bereits auf dem Stand von Symfony 6.4 hält und alle Deprecation-Warnungen behoben hat. Die größten Aufwände entstehen durch entfernte Legacy-APIs wie getDoctrine() und alte Annotation-Routen. Wer den Umstieg strukturiert angeht — PHP 8.2, Deprecations bereinigen, Drittanbieter prüfen — hat in der Regel in einem Tag ein kleineres Projekt migriert.
Für größere Projekte empfiehlt sich ein Feature-Branch mit automatisierten Tests als Sicherheitsnetz: ddev exec vendor/bin/phpunit sollte nach dem Upgrade grün bleiben.
Kommentare
Kommentare werden von Remark42 bereitgestellt. Beim Laden werden Daten an unseren Kommentar-Server übertragen.