PHP Framework

Symfony

Symfony ist sowohl eine Sammlung wiederverwendbarer PHP-Komponenten als auch ein vollständiges Framework, das daraus aufgebaut wurde. Es setzt auf Struktur, Stabilität und langfristige Wartbarkeit bei großen Projekten anstelle von bloßer Bequemlichkeit.

advanced15 min readUpdated 16. Sept. 2026
src/Controller/BlogController.php
php
<?php
// src/Controller/BlogController.php

namespace App\Controller;

use App\Repository\PostRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class BlogController extends AbstractController
{
    #[Route('/blog/{slug}', name: 'blog_show')]
    public function show(string $slug, PostRepository $posts): Response
    {
        $post = $posts->findOneBy(['slug' => $slug]);

        if (!$post) {
            throw $this->createNotFoundException('Post not found');
        }

        return $this->render('blog/show.html.twig', [
            'post' => $post,
        ]);
    }
}
Veröffentlicht
2005
Erfinder
Fabien Potencier
Sprache
PHP
Architektur
Komponenten plus vollständiges Framework
ORM
Doctrine
CLI
bin/console
Konfiguration
YAML, XML oder PHP

Warum es wichtig ist

Warum Unternehmen Symfony wählen

Überall einsetzbare Komponenten

HttpFoundation, Routing, Console, Mailer und Serializer sind unabhängige Bibliotheken. Man kann sie auch ohne das Framework nutzen – was ein Großteil des PHP-Ökosystems bereits tut.

Stabilität und langfristiger Support

Symfony bietet Long-Term-Support-Releases mit einem dokumentierten Deprecation-Pfad, sodass Upgrades planbar sind und keine kompletten Neuentwicklungen erfordern.

Flex und Recipes

Symfony Flex konfiguriert Bundles und generiert die Konfigurationsdateien automatisch, sodass das Hinzufügen eines Features nur ein einziges 'composer require' erfordert.

Das Gesamtbild

Komponenten, Bundles, Container

Entkoppelte Bibliotheken für jeden Einsatzort, Bundles zur Paketierung von Features und ein Container, der alles miteinander verbindet.

Komponenten

Wiederverwendung

Standalone-Bibliotheken für HTTP, Routing, Console, Mail und Serialisierung, die in jedem PHP-Projekt funktionieren.

Bundles

Paketierung

Bundles gruppieren Routen, Services, Konfigurationen und Templates in einer distribuierten Einheit, die durch Flex-Recipes angebunden wird.

Container

Verdrahtung

Ein kompilierter Service-Container baut den Objektgraphen auf und erledigt das Autowiring von Konstruktor-Abhängigkeiten automatisch.

HTML5 auf einen Blick

Der Symfony-Werkzeugkasten

Komponenten

Standalone-Bibliotheken, die in jedem PHP-Projekt verwendet werden können, mit oder ohne Framework.

Bundles

Paketierte Features, die Routen, Services, Konfigurationen und Templates vereinen.

Flex-Recipes

Composer-Plugins, die Bundles konfigurieren und Konfigurationsdateien automatisch erstellen.

Attribute-Routing

Deklariere Routen direkt neben der Controller-Action mithilfe von PHP 8 Attributen.

Doctrine

Ein Data-Mapper ORM mit Entities, Repositories und Migrationen.

Console

bin/console generiert Code, führt Migrationen aus, leert den Cache und arbeitet Queues ab.

Eine kurze Geschichte

Ein Framework und die darunterliegenden Komponenten

  1. 2005

    Symfony 1

    Fabien Potencier veröffentlicht ein Framework für seine eigenen Projekte, inspiriert von Rails und Mojavi.

    05
  2. 2011

    Symfony 2

    Ein kompletter Rewrite, basierend auf entkoppelten Komponenten, die auch von anderen Projekten wiederverwendet werden können.

    11
  3. 2015

    Symfony 3

    Deprecations und eine sauberere API bereiten das Ökosystem auf zukünftige Major-Releases vor.

    15
  4. 2019

    Symfony 4 und Flex

    Flex-Recipes automatisieren das Bundle-Setup und machen Anwendungen dramatisch schlanker.

    19
  5. 2023

    Symfony 7

    PHP 8 Attribute, typisierte Properties und moderne Defaults im gesamten Framework.

    23
  6. Heute

    Der Maschinenraum von PHP

    Symfony-Komponenten treiben Laravel, Drupal, phpBB und tausende weitere Bibliotheken an.

    Heute

Der vollständige Leitfaden

Symfony: Alles was Sie wissen müssen

Was ist Symfony?

Symfony ist zwei Dinge zugleich. Es ist ein Set aus wiederverwendbaren PHP-Komponenten — HttpFoundation, Routing, Console, Mailer, Serializer und Dutzende weitere — und es ist ein vollständiges Web-Framework, das aus diesen Komponenten zusammengesetzt ist. Diese duale Natur erklärt fast alles an Symfony: Das Framework ist stabil, weil seine Einzelteile stabil sind, und diese Teile sind auch unabhängig voneinander nützlich.

Symfony wurde 2005 von Fabien Potencier erstellt, wird von SensioLabs gewartet und ist zum Maschinenraum des modernen PHP geworden. Laravel basiert auf Symfony-Komponenten, Drupal und phpBB hängen von ihnen ab, und Tausende von Bibliotheken nutzen sie, ohne jemals das vollständige Framework zu verwenden.

Symfony verlangt mehr von dir als ein „Batteries-included“-Framework. Im Gegenzug erhältst du ein System, das logisch nachvollziehbar ist und das du über Jahre hinweg erweitern und aktualisieren kannst.

Komponenten: das eigentliche Produkt

Die Komponenten sind das Herzstück des Projekts. Jede einzelne löst ein spezifisches Problem und ist unabhängig vom Framework.

  • HttpFoundation — objektorientierte Request- und Response-Klassen.
  • Routing — Zuordnung von URLs zu Controllern mittels Attributen, YAML oder PHP.
  • Console — Erstellung von Command-Line-Tools mit Argumenten und Optionen.
  • Mailer — Versand von E-Mails über Transports und templated messages.
  • Serializer — Konvertierung von Objekten in JSON, XML und zurück.
  • EventDispatcher — Entkopplung von Code durch Events und Listener.

Du kannst jede dieser Komponenten direkt installieren:

composer require symfony/http-foundation
use Symfony\Component\HttpFoundation\Request;

$request = Request::createFromGlobals();
$page = $request->query->getInt('page', 1);

Diese Portabilität ist der Grund, warum Symfony-Komponenten überall auftauchen, selbst in Projekten, die niemals das vollständige Framework einsetzen würden.

Der HTTP-Kernel und Controller

In einer vollständigen Anwendung fließt jede Anfrage durch den HttpKernel. Dieser löst ein Event aus, matcht eine Route, ruft einen Controller auf und wandelt den zurückgegebenen Response in eine Ausgabe um. Controller erweitern AbstractController, um praktische Helper-Methoden zu erhalten.

class BlogController extends AbstractController
{
    #[Route('/blog/{slug}', name: 'blog_show')]
    public function show(string $slug, PostRepository $posts): Response
    {
        $post = $posts->findOneBy(['slug' => $slug]);

        if (!$post) {
            throw $this->createNotFoundException('Post not found');
        }

        return $this->render('blog/show.html.twig', ['post' => $post]);
    }
}

Ein Controller muss einen Response zurückgeben. $this->render() erstellt einen solchen aus einem Twig-Template, $this->json() gibt JSON zurück und $this->redirectToRoute() gibt einen Redirect zurück. Da das Response-Objekt explizit ist, sind Testing und middleware unkompliziert.

Twig-Templates erweitern ein Basis-Layout und füllen Blöcke aus; die Ausgabe wird automatisch escaped, sofern man sich nicht explizit dagegen entscheidet:

{# templates/blog/show.html.twig #}
{% extends 'base.html.twig' %}

{% block body %}
    <article>
        <h1>{{ post.title }}</h1>
        <p>{{ post.publishedAt|date('F j, Y') }}</p>
        {{ post.body|raw }}
    </article>
{% endblock %}

Attribut-Routing

Routes werden mithilfe von PHP 8 Attributen direkt neben der Action deklariert, die sie verarbeitet.

#[Route('/blog', name: 'blog_index', methods: ['GET'])]
#[Route('/blog/{slug}', name: 'blog_show', methods: ['GET'])]

Das Routing kann alternativ auch in config/routes.yaml definiert werden, wenn Sie es lieber separat halten möchten. Attribute sind in modernem Symfony der Standard, da die Route und die Methode so zusammenbleiben. Sie können jederzeit prüfen, was registriert ist, mit:

php bin/console debug:router

Der Service Container und Autowiring

Fast alles in Symfony ist ein Service, und der Container baut diese auf und verbindet sie miteinander. Wenn Autowiring aktiviert ist, müssen Sie lediglich den Typ im Konstruktor angeben (Type-Hinting).

class PostPublisher
{
    public function __construct(
        private EntityManagerInterface $em,
        private MailerInterface $mailer,
    ) {}
}

config/services.yaml weist den Container an, Ihre Klassen per Autowiring zu verbinden:

services:
    _defaults:
        autowire: true
        autoconfigure: true

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

Wenn zwei Implementierungen eines Interfaces existieren, binden Sie die richtige explizit. Für die Produktion wird der Container in reines PHP kompiliert, weshalb er trotz der dynamischen Schreibweise so schnell ist.

Bundles und Flex-Rezepte

Ein Bundle verpackt Funktionen in eine distribuiertbare Einheit. Funktionalitäten von Drittanbietern – wie Doctrine, Security, Mailer oder Admin-Panels – werden als Bundle bereitgestellt, und auch das Framework selbst setzt sich aus Core-Bundles zusammen.

Die Installation erfolgt mit einem einzigen Befehl:

composer require symfony/orm-pack

Symfony Flex ist das Composer-Plugin, das diesen Prozess vereinfacht. Wenn ein Paket über ein Rezept verfügt, erstellt Flex die Konfigurationsdateien, registriert das Bundle und fügt häufig Umgebungsvariablen oder einen docker-compose-Service hinzu. Aus diesem Grund verfügen moderne Symfony-Apps über eine kleine, explizite Konfiguration anstelle von seitenlangem Boilerplate-Code.

Doctrine ORM

Doctrine ist die standardmäßige Persistence-Layer. Entities sind einfache PHP-Klassen, die mit Mapping-Attributen annotiert sind, während Repositories die Queries kapseln.

#[ORM\Entity(repositoryClass: PostRepository::class)]
class Post
{
    #[ORM\Id, ORM\GeneratedValue, ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    private string $title;

    #[ORM\Column(type: 'text')]
    private string $body;

    #[ORM\Column(type: 'datetime_immutable', nullable: true)]
    private ?\DateTimeImmutable $publishedAt = null;
}

Queries werden in der Regel über die Repository-API oder DQL anstelle von SQL-Strings ausgedrückt:

$latest = $posts->findBy([], ['publishedAt' => 'DESC'], limit: 10);

Schema-Änderungen werden mit Doctrine Migrations verwaltet. Diese vergleichen Ihr Mapping mit der Datenbank und generieren versionierte Migrationsdateien, die Sie vor der Ausführung prüfen können.

Konfiguration und Umgebungen

Symfony trennt die Konfiguration vom Code und wendet sie pro Umgebung an. APP_ENV wählt die Umgebung aus, und die Dateien unter config/ werden hierarchisch aufeinander aufgesetzt.

  • .env enthält lokale Standardwerte und Umgebungsvariablen.
  • config/packages/ konfiguriert Bundles für jede Umgebung.
  • config/packages/dev/ und config/packages/prod/ überschreiben diese pro Umgebung.
  • config/routes.yaml und config/services.yaml sind die eigenen Einstellungen der App.

Die dev-Umgebung aktiviert den Profiler und Debug-Tools, test nutzt eine separate Datenbank und prod kompiliert alles für maximale Geschwindigkeit. Das Leeren des Caches nach Konfigurationsänderungen ist ein normaler Teil des Workflows:

php bin/console cache:clear

Messenger und asynchrone Verarbeitung

Die Messenger-Komponente verschiebt die Verarbeitung aus dem Request-Zyklus heraus. Sie senden eine Nachricht an einen Bus und ein Handler verarbeitet diese – entweder sofort oder über einen Worker.

final class UserRegistered
{
    public function __construct(public readonly int $userId) {}
}

#[AsMessageHandler]
final class SendWelcomeEmail
{
    public function __invoke(UserRegistered $message): void
    {
        // send the email here
    }
}

Senden Sie die Nachricht von überall aus und arbeiten Sie die Queue in einem separaten Prozess ab:

$bus->dispatch(new UserRegistered($user->getId()));
php bin/console messenger:consume async

Als Transports können Doctrine, Redis, AMQP oder ein Drittanbieter-Service genutzt werden. Fehlgeschlagene Nachrichten können erneut versucht oder gespeichert werden, wodurch die Hintergrundverarbeitung zu einem zentralen Bestandteil der Architektur wird.

Testen mit PHPUnit

Symfony wird mit PHPUnit und einer WebTestCase Basisklasse ausgeliefert, die den Kernel bootet und echte HTTP requests ausführt.

class BlogControllerTest extends WebTestCase
{
    public function testShowPost(): void
    {
        $client = static::createClient();
        $client->request('GET', '/blog/hello-world');

        $this->assertResponseIsSuccessful();
        $this->assertSelectorTextContains('h1', 'Hello world');
    }
}

Da Services injiziert werden, können Unit-Tests eine Abhängigkeit durch einen Stub oder einen Mock ersetzen, ohne den Container zu verändern. Nutzen Sie Integration-Tests für den HTTP layer und Unit-Tests für die Services, die die Business-Logik enthalten.

Best Practices

  • Bevorzugen Sie Constructor Injection gegenüber dem Abrufen von Services aus dem Container.
  • Halten Sie Controller schlank; verschieben Sie die Logik in Services.
  • Nutzen Sie Repositories für Abfragen und halten Sie SQL aus den Controllern heraus.
  • Deklarieren Sie Routen als Attribute direkt neben der Action, die sie aufrufen.
  • Überlassen Sie Flex die Verwaltung der Bundle-Konfiguration, anstatt Boilerplate-Code zu schreiben.
  • Trennen Sie die Konfiguration nach Umgebung und speichern Sie Secrets in Umgebungsvariablen.
  • Lagern Sie zeitintensive Aufgaben in den Messenger aus, anstatt sie direkt im Request zu verarbeiten.
  • Nutzen Sie den Profiler, wenn etwas langsam ist oder unerwartete Ergebnisse liefert.

Häufige Fehler

  • Aufruf von $container->get() anstatt Dependency Injection zu nutzen.
  • Platzierung von Business-Logik in Controllern und Entities.
  • Vergessen, ein Interface zu registrieren oder zu binden, und sich anschließend fragen, was der Container aufgelöst hat.
  • Committen von .env Secrets, anstatt sie aus der Versionsverwaltung fernzuhalten.
  • Bearbeiten von generierten Migrationen, nachdem diese bereits ausgeführt wurden.
  • Willkürliches Mischen von Konfigurationsstilen über YAML, XML und PHP hinweg.
  • Ignorieren von Deprecation-Hinweisen bis zum nächsten Major-Upgrade.

Wie geht es weiter?

Symfony bietet Ihnen die Komponenten und die Disziplin, um zukunftssichere Systeme zu bauen, und seine Konzepte finden sich in Frameworks im gesamten Ökosystem wieder. Lesen Sie den Laravel-Guide, um zu sehen, wie diese Komponenten für eine schnelle Anwendungsentwicklung genutzt werden, oder vergleichen Sie die Philosophie mit dem minimalistischen Ansatz von Express. Um die von Ihnen bereitgestellten APIs zu entwerfen und weiterzuentwickeln, fahren Sie mit REST und API-Versionierung fort.

Service abrufen

Lass den Container injizieren, was du benötigst. Das manuelle Herausziehen von Services aus dem Container verbirgt Abhängigkeiten und erschwert das Testen der Klasse.

Bevorzugt
class NewsletterService
{
    public function __construct(
        private MailerInterface $mailer,
    ) {}
}
Vermeiden
class NewsletterService
{
    public function send(): void
    {
        $mailer = $this->container->get('mailer');
        $mailer->send(/* ... */);
    }
}

Entity finden

Repositories drücken die Absicht aus und geben Objekte zurück. Rohes SQL in einem Controller koppelt die HTTP-Schicht an das Schema und umgeht das Mapping von Doctrine.

Bevorzugt
$post = $posts->findOneBy(['slug' => $slug]);
if (!$post) {
    throw $this->createNotFoundException();
}
Vermeiden
$conn = $this->getDoctrine()->getConnection();
$post = $conn->fetchAssociative(
    'SELECT * FROM posts WHERE slug = ?',
    [$slug],
);

Abwägungen

Ist Symfony das richtige PHP-Framework?

Symfony tauscht sofortige Gratifikation gegen Struktur, Stabilität und Wiederverwendbarkeit. Dieser Tausch lohnt sich vor allem bei langlebigen, komplexen Systemen.

Strengths

  • Wiederverwendbare Komponenten

    Da das Framework aus Bibliotheken zusammengesetzt ist, kannst du die benötigten Teile in jedes Projekt übernehmen – und kennst sie bereits, wenn du mit Drupal oder Laravel arbeitest.

  • Stabilität und planbare Upgrades

    LTS-Releases, dokumentierte Deprecations und eine strikte Abwärtskompatibilitäts-Policy bedeuten, dass Upgrades geplante Arbeit und kein Rewrite sind.

  • Gebaut für komplexe Domänen

    Der Container, der Event Dispatcher, Messenger, Workflow- und Form-Komponenten bewältigen Anwendungen, die aus einem einfacheren Stack herauswachsen.

Trade-offs

  • Mehr Konzepte zu Beginn

    Kernel, Container, Bundles, Environments und Recipes sind viel Lernstoff, bevor man das erste Feature schreibt. Plane Zeit für das mentale Modell ein.

  • Konfigurationslastig

    Selbst mit Flex verlangt Symfony, dass Services, Routing und Pakete explizit konfiguriert werden, was mächtig, aber wortreich ist.

  • Langsamer beim Prototyping

    Für eine kleine CRUD-App bringt ein 'Batteries-included'-Framework schneller zu einem funktionierenden Screen. Symfonys Struktur gewinnt, sobald die Domäne wächst.

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Symfony zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Symfony — mit Quizzen und echtem Code, den Sie im Browser ausführen können.