Praxisleitfaden fuer Symfony-Templates
Twig 3 teilt die CoreExtension in kleinere Bausteine auf, aendert das Escaping-Verhalten fuer eigene Filter und entfernt lange als veraltet markierte Funktionen. Wer diese Breaking Changes vor dem Composer-Update kennt und Deprecations gezielt sucht, migriert Symfony-Templates ohne boese Ueberraschungen in der Produktion.
Inhaltsverzeichnis
- 1. Warum die Migration von Twig 2 auf 3 ansteht
- 2. Breaking Changes im Ueberblick
- 3. Namespace-Aenderungen und PHP-Mindestanforderungen
- 4. CoreExtension-Aufteilung und eigene Extensions
- 5. Whitespace-Control und veraendertes Escaping
- 6. Deprecations vor der Migration finden
- 7. Rector und twig-cs-fixer fuer automatisierte Migration
- 8. Migrationsstrategie ueber mehrere Minor-Versionen
- 9. Twig 2 vs. 3 im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum die Migration von Twig 2 auf 3 ansteht
Twig 3 ist seit einigen Jahren die einzige aktiv gepflegte Major-Version, und Twig 2 erhaelt keine Sicherheitsupdates mehr. Fuer Symfony-Projekte, die Templates ausschliesslich ueber die Twig-Bridge einbinden, ist die Migration meist unproblematischer als bei Doctrine oder anderen tief integrierten Komponenten, weil Twig als Template-Engine eine klar abgegrenzte Verantwortung hat. Trotzdem gibt es genug Breaking Changes, dass ein blindes Composer-Update in groesseren Projekten zu Fehlern in einzelnen Templates fuehrt, die erst beim tatsaechlichen Rendern sichtbar werden.
Der zweite Grund fuer die Migration ist die Qualitaet der Fehlermeldungen und die Performance: Twig 3 hat die interne Compiler-Architektur ueberarbeitet, was zu praeziseren Fehlermeldungen bei Syntaxfehlern in Templates fuehrt und in vielen Faellen eine spuerbar schnellere Kompilierung grosser Template-Baeume ermoeglicht. Fuer Teams mit hunderten Twig-Templates in einem Symfony-Projekt macht sich das direkt in kuerzeren Cache-Warmup-Zeiten bemerkbar.
Dieser Artikel zeigt, welche Breaking Changes bei der Migration von Twig 2 auf 3 in Symfony-Projekten am haeufigsten auftreten, wie man Deprecations vorab findet, und wie man eigene Twig-Extensions an die neue, in kleinere Bausteine aufgeteilte CoreExtension anpasst.
2. Breaking Changes im Ueberblick
Die wichtigste strukturelle Aenderung bei der Migration von Twig 2 auf 3 betrifft die interne CoreExtension, die in Twig 2 noch eine einzige monolithische Klasse mit allen Standardfiltern und -funktionen war. In Twig 3 wurde diese Klasse in mehrere kleinere Extensions aufgeteilt, etwa EscaperExtension, StringLoaderExtension und SandboxExtension, die unabhaengig voneinander registriert werden koennen. Fuer die meisten Symfony-Projekte, die Twig ausschliesslich ueber die Standard-Bridge nutzen, ist diese Aufteilung transparent, betrifft aber direkt jeden Code, der bisher explizit gegen Twig\Extension\CoreExtension geprueft oder diese Klasse manuell instanziiert hat.
Ein zweiter zentraler Bruch betrifft entfernte, seit Twig 1.x als deprecated markierte Filter und Tags, etwa der alte {% spaceless %}-Tag, der zugunsten des spaceless-Filters entfernt wurde, sowie einige selten genutzte Escape-Strategien, deren Namen sich geaendert haben.
<?php
declare(strict_types=1);
// Twig 2.x: checking against the monolithic CoreExtension
use Twig\Extension\CoreExtension;
final class LegacyTwigInspector
{
public function hasCoreExtension(\Twig\Environment $twig): bool
{
return $twig->hasExtension(CoreExtension::class);
}
}
// Twig 3.x: functionality is split across focused extensions
use Twig\Extension\EscaperExtension;
use Twig\Extension\StringLoaderExtension;
final class ModernTwigInspector
{
public function hasEscaper(\Twig\Environment $twig): bool
{
return $twig->hasExtension(EscaperExtension::class);
}
public function hasStringLoader(\Twig\Environment $twig): bool
{
return $twig->hasExtension(StringLoaderExtension::class);
}
}
Fuer die uebergrosse Mehrheit der Symfony-Templates selbst, also die .twig-Dateien mit Filtern wie |upper oder Funktionen wie path(), aendert sich durch die CoreExtension-Aufteilung nichts, weil Symfony die neuen Extensions automatisch ueber die Twig-Bridge registriert. Betroffen sind fast ausschliesslich PHP-Klassen, die Twig direkt und ohne die Symfony-Integration nutzen.
3. Namespace-Aenderungen und PHP-Mindestanforderungen
Twig 3 erhoeht die Mindestanforderung auf PHP 7.2 in den fruehen 3.x-Releases und in aktuellen Versionen auf PHP 8.1, was in Kombination mit einem Symfony-Upgrade meist ohnehin erfuellt ist. Wichtiger fuer die tatsaechliche Migration sind Namespace-Verschiebungen innerhalb von Twig selbst: Klassen wie Twig_Environment aus der alten, unternamensraumten Twig-1-Konvention wurden endgueltig entfernt, nachdem sie in Twig 2 bereits nur noch als Alias fuer Twig\Environment existierten.
Projekte mit sehr alter Twig-1-Historie, die diese Umstellung nie vollstaendig nachvollzogen haben, muessen vor dem Sprung auf Twig 3 zunaechst sicherstellen, dass keine Referenzen auf die alten Twig_*-Klassennamen mehr existieren. Eine projektweite Suche nach Twig_ als Praefix in PHP-Dateien ist der schnellste Weg, verbliebene Altlasten zu finden, bevor das eigentliche Composer-Update beginnt.
<?php
declare(strict_types=1);
// Removed since Twig 2, definitively gone in Twig 3: old unnamespaced classes
// class CustomTwigExtension extends Twig_Extension { }
// Correct: fully namespaced since Twig 2, mandatory in Twig 3
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
final class CustomTwigExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
new TwigFilter('shout', $this->shout(...)),
];
}
private function shout(string $value): string
{
return strtoupper($value) . '!';
}
}
Wer bereits in Twig 2 sauber mit den namensraumbasierten Klassen gearbeitet hat, muss an dieser Stelle in der Regel nichts aendern. Der Aufwand konzentriert sich auf Projekte, die ueber viele Jahre gewachsen sind und einzelne Alt-Klassen nie vollstaendig aktualisiert haben.
4. CoreExtension-Aufteilung und eigene Extensions
Eigene Twig-Extensions, die neue Filter oder Funktionen registrieren, sind von der CoreExtension-Aufteilung in der Regel nicht direkt betroffen, weil die oeffentliche AbstractExtension-API unveraendert geblieben ist. Betroffen sind Extensions, die intern auf spezifische Methoden der alten monolithischen CoreExtension zugegriffen haben, etwa um eine bestehende Escape-Strategie zu erweitern, statt eine neue eigene Filter-Funktion zu registrieren.
Fuer Symfony-Projekte mit vielen eigenen Twig-Extensions lohnt sich eine bewusste Trennung nach fachlicher Zustaendigkeit, aehnlich der Aufteilung, die Twig selbst intern vorgenommen hat. Statt einer einzigen grossen AppTwigExtension-Klasse mit zwanzig Filtern fuer unterschiedliche Zwecke ist eine Aufteilung in fokussierte Extensions pro Fachbereich wartbarer und macht Tests kleiner und gezielter.
<?php
declare(strict_types=1);
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
// Focused extension for currency formatting, registered as a Symfony service
final class CurrencyExtension extends AbstractExtension
{
public function __construct(
private readonly string $defaultCurrency = 'EUR',
) {
}
public function getFilters(): array
{
return [
new TwigFilter('money', $this->formatMoney(...)),
];
}
public function getFunctions(): array
{
return [
new TwigFunction('default_currency', fn (): string => $this->defaultCurrency),
];
}
private function formatMoney(int $amountCents, ?string $currency = null): string
{
$amount = $amountCents / 100;
return number_format($amount, 2) . ' ' . ($currency ?? $this->defaultCurrency);
}
}
Da Symfony Twig-Extensions ueber das twig.extension-Tag automatisch dem Twig-Environment hinzufuegt, aendert sich an der Registrierung durch die CoreExtension-Aufteilung nichts. Der einzige Bereich, der Aufmerksamkeit braucht, sind Extensions, die tatsaechlich versuchen, Kernverhalten von Twig selbst zu ueberschreiben, statt neue Filter und Funktionen additiv zu ergaenzen.
5. Whitespace-Control und veraendertes Escaping
Twig 3 praezisiert das Verhalten der automatischen Escaping-Strategie fuer benutzerdefinierte Filter, die als "safe" markiert Ausgaben erzeugen. In Twig 2 gab es hier einige Randfaelle, in denen ein Filter mit is_safe in Kombination mit verschachtelten Funktionsaufrufen inkonsistent escaped wurde. Twig 3 behebt diese Inkonsistenzen, was in seltenen Faellen dazu fuehrt, dass zuvor unescaped ausgegebener HTML-Code jetzt korrekterweise escaped wird, wenn ein Filter faelschlich als sicher markiert war.
Fuer Templates, die auf dieses zuvor fehlerhafte Verhalten angewiesen waren, etwa um HTML aus einem eigenen Filter ungeprueft auszugeben, ist nach der Migration eine visuelle Pruefung der betroffenen Seiten notwendig. Der korrekte Weg, HTML-Ausgabe bewusst zu markieren, ist weiterhin |raw oder eine explizite is_safe-Deklaration in der Filter-Definition, niemals ein zufaelliges Zusammenspiel mehrerer Filter, das sich mit jeder Twig-Version anders verhalten kann.
<?php
declare(strict_types=1);
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
final class MarkdownExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
// Explicit is_safe declaration: Twig 3 respects this consistently,
// no accidental escaping bypass through filter chaining
new TwigFilter(
'markdown_to_html',
$this->renderMarkdown(...),
['is_safe' => ['html']],
),
];
}
private function renderMarkdown(string $markdown): string
{
// Simplified: a real implementation delegates to a Markdown parser
return '<p>' . htmlspecialchars($markdown) . '</p>';
}
}
Ein sinnvoller Test nach der Migration: alle Templates, die eigene Filter mit HTML-Ausgabe nutzen, gezielt in einem Browser oeffnen und pruefen, ob HTML-Tags korrekt gerendert oder als Text escaped angezeigt werden. Automatisierte Snapshot-Tests der gerenderten HTML-Ausgabe fangen Regressionen an dieser Stelle zuverlaessiger ab als reine Unit-Tests der Twig-Extension-Klassen.
6. Deprecations vor der Migration finden
Twig protokolliert Deprecations ueber denselben Mechanismus wie PHP selbst, kombiniert mit dem symfony/twig-bridge-Deprecation-Handler, der in der Symfony-Testsuite aktiviert werden kann. Vor dem eigentlichen Upgrade auf Twig 3 lohnt sich ein Testlauf mit aktivierter Deprecation-Erfassung auf der aktuellen Twig-2-Version, weil viele der spaeter entfernten Features bereits vorher als deprecated markiert waren und im laufenden Betrieb Warnungen erzeugt haben, die in Produktionslogs meist ignoriert werden.
Eine gezielte Suche im Template-Bestand nach {% spaceless %}, veralteten Datumsfilter-Optionen und direkten Zugriffen auf interne Twig-Klassen deckt die meisten Stellen ab, die vor dem Upgrade angepasst werden muessen. Fuer sehr grosse Template-Baeume mit mehreren hundert Dateien ist ein einfaches Grep-Kommando oft effizienter als das Warten auf Laufzeitfehler in einzelnen, selten aufgerufenen Templates.
# Find deprecated {% spaceless %} tag usage across all templates
grep -rn "{% spaceless %}" templates/
# Find any remaining references to old unnamespaced Twig 1 classes
grep -rn "Twig_" src/ templates/
# Run the test suite with Symfony's deprecation helper enabled
SYMFONY_DEPRECATIONS_HELPER=max[self]=0 bin/phpunit
Diese drei Kommandos zusammen decken die haeufigsten Migrationsrisiken ab: veraltete Tag-Syntax in Templates, verbliebene alte Klassennamen in PHP-Code, und generelle Deprecations, die von symfony/twig-bridge gemeldet werden. Wer diese Suche vor dem eigentlichen Composer-Update durchfuehrt, reduziert die Zahl der Ueberraschungen im spaeteren Testlauf erheblich.
7. Rector und twig-cs-fixer fuer automatisierte Migration
Fuer PHP-seitigen Code, der Twig-Extensions definiert, helfen Rector-Regeln fuer allgemeine PHP-Modernisierung dabei, veraltete Konstruktor-Muster in modernere Constructor-Property-Promotion umzuwandeln, waehrend die eigentliche Twig-spezifische Migration meist manuell bleibt, weil Rector keine spezifischen Regeln fuer Twig-Extension-APIs mitbringt. twig-cs-fixer hingegen prueft die Template-Syntax selbst auf Konsistenz und kann in der CI-Pipeline als Linting-Schritt eingebunden werden, um zu verhindern, dass neue Templates veraltete Muster einfuehren, waehrend die Migration noch laeuft.
<?php
declare(strict_types=1);
// twig-cs-fixer.php: lints all templates for consistent, modern syntax
use TwigCsFixer\Config\Config;
use TwigCsFixer\Ruleset\Ruleset;
$ruleset = new Ruleset();
$ruleset->addStandard(new \TwigCsFixer\Standard\Twig());
$config = new Config();
$config->setRuleset($ruleset);
$config->setFinder(
(new \TwigCsFixer\Finder\TemplateFinder())->in(__DIR__ . '/templates')
);
return $config;
Eine Kombination aus twig-cs-fixer in der CI-Pipeline und einem einmaligen, manuellen Durchlauf durch die im vorherigen Abschnitt genannten Grep-Suchen deckt in der Praxis die grosse Mehrheit der Migrationsaufgaben ab, ohne dass ein Team ein eigenes Migrationsskript schreiben muss.
8. Migrationsstrategie ueber mehrere Minor-Versionen
Die sicherste Strategie fuer die Migration von Twig 2 auf 3 fuehrt nicht direkt von der aeltesten Twig-2-Version zur neuesten Twig-3-Version, sondern zunaechst auf die letzte Twig-2-Minor-Version, die bereits alle Deprecation-Warnungen fuer in Twig 3 entfernte Features ausgibt. Erst wenn diese Warnungen vollstaendig behoben sind, folgt der eigentliche Sprung auf Twig 3, was das Risiko unerwarteter Laufzeitfehler drastisch reduziert, weil jede Aenderung isoliert getestet werden kann, statt mehrere Migrationsschritte gleichzeitig zu verantworten.
In der Praxis bedeutet das: zuerst composer require twig/twig:^2.15 als letzte Twig-2-Version, Testsuite mit aktivierter Deprecation-Erfassung laufen lassen, alle gemeldeten Deprecations beheben, und erst danach composer require twig/twig:^3.0 ausfuehren. Dieser Zwischenschritt kostet zusaetzliche Zeit, verhindert aber, dass mehrere Kategorien von Breaking Changes gleichzeitig debuggt werden muessen.
9. Twig 2 vs. 3 im direkten Vergleich
Die folgende Tabelle fasst die wichtigsten Unterschiede zwischen Twig 2 und Twig 3 fuer Symfony-Projekte zusammen.
| Bereich | Twig 2.x | Twig 3.x | Migrationsaufwand |
|---|---|---|---|
| CoreExtension | Eine monolithische Klasse | Mehrere fokussierte Extensions | Niedrig fuer Standard-Templates |
| {% spaceless %}-Tag | Vorhanden | Entfernt, Filter-Ersatz noetig | Niedrig, projektweite Suche |
| Escaping bei is_safe | Inkonsistent in Randfaellen | Konsistent und vorhersehbar | Mittel, visuelle Pruefung noetig |
| Kompilierungs-Performance | Baseline | Messbar schneller | Kein Aufwand, direkter Gewinn |
Fuer die meisten Symfony-Projekte ueberwiegt der Nutzen deutlich: schnellere Kompilierung, konsistenteres Escaping und praezisere Fehlermeldungen bei Template-Syntaxfehlern, bei ueberschaubarem Migrationsaufwand, solange keine tiefen Eingriffe in interne Twig-Klassen vorgenommen wurden.
Mironsoft
Symfony-Template-Migrationen und Twig-Extension-Entwicklung
Twig-Templates sicher auf Twig 3 migrieren?
Wir pruefen euren Template-Bestand auf Deprecations, passen eigene Twig-Extensions an die neue CoreExtension-Struktur an und begleiten den Rollout mit visuellen Regressionstests fuer betroffene Seiten.
Deprecation-Suche
Vollstaendige Analyse von Templates und Twig-Extension-Klassen
Extension-Anpassung
Eigene Filter und Funktionen an die aufgeteilte CoreExtension angepasst
Visuelle Regressionstests
Escaping-Verhalten vor und nach der Migration systematisch vergleichen
10. Zusammenfassung
Die Migration von Twig 2 auf 3 ist fuer die meisten Symfony-Projekte weniger riskant als andere Major-Upgrades, weil Twig als Template-Engine klar abgegrenzt ist. Die Aufteilung der monolithischen CoreExtension in fokussierte Bausteine betrifft vor allem PHP-Code, der Twig direkt nutzt, waehrend Standard-Templates ueber die Symfony-Bridge meist unveraendert funktionieren. Entfernte Tags wie {% spaceless %} und konsistenteres Escaping-Verhalten sind die sichtbarsten Aenderungen fuer den Template-Bestand selbst.
Der sicherste Migrationsweg fuehrt ueber die letzte Twig-2-Minor-Version mit vollstaendiger Deprecation-Erfassung, gefolgt von der Behebung aller gemeldeten Warnungen und erst danach dem eigentlichen Sprung auf Twig 3. Grep-Suchen nach veralteten Tags und Klassennamen, kombiniert mit twig-cs-fixer in der CI-Pipeline, decken die meisten Migrationsaufgaben ab, ohne dass ein Team ein eigenes Migrationsskript schreiben muss.
Twig 2 auf Twig 3 migrieren — Das Wichtigste auf einen Blick
CoreExtension aufgeteilt
Mehrere fokussierte Extensions statt einer Klasse, betrifft vor allem eigene PHP-Integrationen.
{% spaceless %} entfernt
Projektweite Grep-Suche vor dem Upgrade findet betroffene Templates zuverlaessig.
Escaping konsistenter
Visuelle Pruefung von Templates mit eigenen is_safe-Filtern nach der Migration empfohlen.
Zweistufiger Umstieg
Erst letzte Twig-2-Version mit Deprecations bereinigen, dann erst auf Twig 3 wechseln.