PHP Framework

Symfony

Symfony es tanto un conjunto de componentes PHP reutilizables como un framework completo construido a partir de ellos. Prioriza la estructura, la estabilidad y la mantenibilidad a largo plazo a escala sobre la conveniencia inmediata.

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,
        ]);
    }
}
Lanzamiento
2005
Creador
Fabien Potencier
Lenguaje
PHP
Arquitectura
Componentes más framework completo
ORM
Doctrine
CLI
bin/console
Configuración
YAML, XML o PHP

Por que importa

Por qué las empresas eligen Symfony

Componentes que puedes usar en cualquier lugar

HttpFoundation, Routing, Console, Mailer y Serializer son librerías independientes. Puedes adoptar una sin necesidad del framework, y gran parte del ecosistema PHP ya lo hace.

Estabilidad y soporte a largo plazo

Symfony ofrece versiones con soporte a largo plazo (LTS) y una ruta de deprecación documentada, por lo que las actualizaciones son predecibles en lugar de requerir reescrituras.

Flex y recetas

Symfony Flex configura los bundles y genera los archivos de configuración por ti, haciendo que añadir una funcionalidad sea tan simple como un composer require.

La imagen completa

Componentes, bundles y contenedor

Librerías desacopladas que puedes usar en cualquier lugar, bundles que empaquetan funcionalidades y un contenedor que lo conecta todo.

Componentes

Reutilizar

Librerías independientes para HTTP, enrutamiento, consola, correo y serialización que funcionan en cualquier proyecto PHP.

Bundles

Empaquetar

Los bundles agrupan rutas, servicios, configuración y plantillas en una unidad distribuible, conectada mediante recetas de Flex.

Contenedor

Conectar

Un contenedor de servicios compilado construye tu grafo de objetos y realiza el autowiring de las dependencias del constructor automáticamente.

HTML5 de un vistazo

La caja de herramientas de Symfony

Componentes

Librerías independientes que puedes usar en cualquier proyecto PHP, con o sin el framework.

Bundles

Funcionalidades empaquetadas que reúnen rutas, servicios, configuración y plantillas.

Recetas de Flex

Plugins de Composer que configuran bundles y crean los archivos de configuración automáticamente.

Enrutamiento por atributos

Declara rutas junto a la acción del controlador utilizando atributos de PHP 8.

Doctrine

Un ORM de tipo data-mapper con entidades, repositorios y migraciones.

Consola

bin/console genera código, ejecuta migraciones, limpia la caché y consume colas.

Una breve historia

Un framework y los componentes que lo sustentan

  1. 2005

    Symfony 1

    Fabien Potencier lanza un framework para sus propios proyectos, inspirado en Rails y Mojavi.

    05
  2. 2011

    Symfony 2

    Una reescritura completa basada en componentes desacoplados que otros proyectos pueden reutilizar.

    11
  3. 2015

    Symfony 3

    Deprecaciones y una API más limpia preparan el ecosistema para futuras versiones mayores.

    15
  4. 2019

    Symfony 4 y Flex

    Las recetas de Flex automatizan la configuración de bundles y hacen que las aplicaciones sean drásticamente más ligeras.

    19
  5. 2023

    Symfony 7

    Atributos de PHP 8, propiedades tipadas y valores predeterminados modernos en todo el framework.

    23
  6. Hoy

    El motor de PHP

    Los componentes de Symfony impulsan Laravel, Drupal, phpBB y miles de librerías.

    Hoy

La guia completa

Symfony: Todo lo que necesitas saber

¿Qué es Symfony?

Symfony es dos cosas a la vez. Es un conjunto de componentes de PHP reutilizables —HttpFoundation, Routing, Console, Mailer, Serializer y docenas más— y es un framework web completo ensamblado a partir de esos componentes. Esta naturaleza dual explica casi todo sobre él: el framework es estable porque sus partes son estables, y sus partes son útiles por sí solas.

Creado por Fabien Potencier en 2005 y mantenido por SensioLabs, Symfony se ha convertido en el motor del PHP moderno. Laravel está construido sobre componentes de Symfony, Drupal y phpBB dependen de ellos, y miles de librerías los utilizan sin llegar a tocar el framework completo.

Symfony te exige más que un framework “batteries-included”. A cambio, te ofrece un sistema sobre el cual puedes razonar, extender y actualizar durante años.

Componentes: el producto real

Los componentes son el corazón del proyecto. Cada uno resuelve un único problema y no tiene dependencias del framework.

  • HttpFoundation — clases Request y Response orientadas a objetos.
  • Routing — mapeo de URLs a controladores mediante atributos, YAML o PHP.
  • Console — creación de herramientas de línea de comandos con argumentos y opciones.
  • Mailer — envío de correos electrónicos a través de transports y mensajes basados en plantillas.
  • Serializer — conversión de objetos a JSON, XML y viceversa.
  • EventDispatcher — desacoplamiento de código mediante eventos y listeners.

Puedes instalar cualquiera de ellos directamente:

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

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

Esa portabilidad es la razón por la cual los componentes de Symfony aparecen en todas partes, incluso en proyectos que nunca adoptarían el framework completo.

El kernel de HTTP y los controladores

En una aplicación completa, cada solicitud fluye a través del HttpKernel. Este despacha un evento, coincide con una ruta, llama a un controlador y convierte el Response devuelto en una salida. Los controladores extienden AbstractController para obtener helpers convenientes.

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]);
    }
}

Un controlador debe devolver un Response. $this->render() construye uno a partir de una plantilla de Twig, $this->json() devuelve JSON y $this->redirectToRoute() devuelve una redirección. Debido a que el objeto de respuesta es explícito, las pruebas y el middleware son sencillos.

Las plantillas de Twig extienden un diseño base y rellenan bloques, y la salida se escapa a menos que decidas lo contrario:

{# 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 %}

Enrutamiento por atributos

Las rutas se declaran junto a la acción que las maneja utilizando atributos de PHP 8.

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

El enrutamiento también puede residir en config/routes.yaml cuando prefieras mantenerlo separado. Los atributos son la opción predeterminada en el Symfony moderno porque la ruta y el método permanecen juntos. Puedes inspeccionar qué está registrado en cualquier momento con:

php bin/console debug:router

El contenedor de servicios y el autowiring

Casi todo en Symfony es un servicio, y el contenedor se encarga de construirlos y conectarlos. Con el autowiring habilitado, solo necesitas añadir el type-hint en el constructor.

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

config/services.yaml indica al contenedor que debe aplicar autowiring a tus clases:

services:
    _defaults:
        autowire: true
        autoconfigure: true

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

Cuando existan dos implementaciones de una misma interfaz, vincula la correcta de forma explícita. El contenedor se compila a PHP puro para producción, razón por la cual es rápido a pesar de ser tan dinámico al escribirlo.

Bundles y recetas de Flex

Un bundle empaqueta funcionalidades en una unidad distribuible. La funcionalidad de terceros —Doctrine, seguridad, mailer, paneles de administración— llega en forma de bundle, y el propio framework está compuesto por bundles principales.

Instalar uno requiere un solo comando:

composer require symfony/orm-pack

Symfony Flex es el plugin de Composer que hace que este proceso sea sencillo. Cuando un paquete tiene una receta, Flex crea los archivos de configuración, registra el bundle y, a menudo, añade variables de entorno o un servicio docker-compose. Es por esto que las aplicaciones modernas de Symfony tienen una configuración pequeña y explícita en lugar de páginas enteras de código repetitivo.

Doctrine ORM

Doctrine es la capa de persistencia predeterminada. Las entidades son clases PHP sencillas anotadas con atributos de mapeo, y los repositorios encapsulan las consultas.

#[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;
}

Las consultas suelen expresarse mediante la API del repositorio o DQL en lugar de cadenas SQL:

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

Los cambios de esquema se gestionan con Doctrine Migrations, que comparan tu mapeo con la base de datos y generan archivos de migración versionados que puedes revisar antes de ejecutarlos.

Configuración y entornos

Symfony separa la configuración del código y la aplica por entorno. APP_ENV selecciona el entorno, y los archivos bajo config/ se superponen entre sí.

  • .env contiene los valores predeterminados locales y las variables de entorno.
  • config/packages/ configura los bundles para todos los entornos.
  • config/packages/dev/ y config/packages/prod/ sobrescriben la configuración por entorno.
  • config/routes.yaml y config/services.yaml son los ajustes propios de la aplicación.

El entorno dev habilita el profiler y las herramientas de depuración, test utiliza una base de datos independiente y prod compila todo para optimizar la velocidad. Limpiar la caché después de realizar cambios en la configuración es una parte normal del flujo de trabajo:

php bin/console cache:clear

Messenger y trabajo asíncrono

El componente Messenger desplaza la carga de trabajo fuera de la solicitud. Envías un mensaje a un bus y un handler lo procesa, ya sea de forma inmediata o mediante un 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
    }
}

Envíalo desde cualquier lugar y consume la cola en un proceso independiente:

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

Los transports pueden ser Doctrine, Redis, AMQP o un servicio de terceros, y los mensajes fallidos pueden reintentarse o almacenarse, lo que convierte el procesamiento en segundo plano en una funcionalidad de primer nivel.

Pruebas con PHPUnit

Symfony incluye PHPUnit y una clase base WebTestCase que arranca el kernel y realiza solicitudes HTTP reales.

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

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

Dado que los servicios se inyectan, las pruebas unitarias pueden reemplazar una dependencia con un stub o un mock sin tocar el contenedor. Reserva las pruebas de integración para la capa HTTP y las pruebas unitarias para los servicios que contienen las reglas de negocio.

Mejores prácticas

  • Prefiere la inyección por constructor en lugar de obtener servicios directamente del contenedor.
  • Mantén los controladores ligeros; mueve la lógica a los servicios.
  • Utiliza repositorios para las consultas y evita escribir SQL en los controladores.
  • Declara las rutas como atributos junto a la acción que invocan.
  • Deja que Flex gestione la configuración del bundle en lugar de escribir código repetitivo.
  • Separa la configuración por entorno y guarda los secretos en variables de entorno.
  • Delega las tareas lentas a Messenger en lugar de procesarlas durante la solicitud.
  • Consulta el profiler cuando algo sea lento o sorprendente.

Errores comunes

  • Llamar a $container->get() en lugar de inyectar dependencias.
  • Colocar lógica de negocio en controladores y entidades.
  • Olvidar registrar o vincular una interfaz y luego preguntarse qué resolvió el contenedor.
  • Hacer commit de secretos en .env en lugar de mantenerlos fuera del control de versiones.
  • Editar migraciones generadas después de que se hayan ejecutado.
  • Mezclar estilos de configuración aleatoriamente entre YAML, XML y PHP.
  • Ignorar los avisos de deprecación hasta la siguiente actualización mayor.

Próximos pasos

Symfony te proporciona los componentes y la disciplina necesarios para construir sistemas duraderos, y sus ideas son evidentes en diversos frameworks de todo el ecosistema. Lee la guía de Laravel para ver cómo se utilizan esos componentes para el desarrollo rápido de aplicaciones, o compara su filosofía con el enfoque minimalista de Express. Para diseñar y evolucionar las API que expongas, continúa con REST y versionado de API.

Obtener un servicio

Deja que el contenedor inyecte lo que necesites. Extraer servicios directamente del contenedor oculta las dependencias y dificulta las pruebas de la clase.

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

Buscar una entidad

Los repositorios expresan la intención y devuelven objetos. El SQL puro en un controlador acopla la capa HTTP al esquema y omite el mapeo de Doctrine.

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

Compromisos

¿Es Symfony el framework de PHP adecuado?

Symfony cambia la gratificación instantánea por estructura, estabilidad y reutilización. Este intercambio es más rentable en sistemas complejos y de larga duración.

Strengths

  • Componentes reutilizables

    Como el framework se ensambla a partir de librerías, puedes llevar las piezas que necesites a cualquier proyecto, y ya las conoces cuando trabajas con Drupal o Laravel.

  • Estabilidad y actualizaciones predecibles

    Las versiones LTS, las deprecaciones documentadas y una política estricta de retrocompatibilidad significan que las actualizaciones son un trabajo planificado y no una reescritura.

  • Diseñado para dominios complejos

    El contenedor, el event dispatcher, Messenger, workflow y los componentes de formularios gestionan el tipo de aplicación que supera la capacidad de un stack más simple.

Trade-offs

  • Más conceptos iniciales

    Kernel, contenedor, bundles, entornos y recetas son muchos conceptos que aprender antes de escribir una funcionalidad. Reserva tiempo para asimilar el modelo mental.

  • Carga pesada de configuración

    Incluso con Flex, Symfony te pide configurar servicios, enrutamiento y paquetes explícitamente, lo cual es potente pero verboso.

  • Prototipado más lento

    Para una aplicación CRUD pequeña, un framework 'batteries-included' te lleva a una pantalla funcional más rápido. La estructura de Symfony empieza a ganar ventaja a medida que el dominio crece.

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender Symfony?

Nuestro tutorial interactivo te guia a traves de Symfony paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.