Что такое Symfony
Symfony — одновременно full-stack PHP-фреймворк и набор переиспользуемых компонентов. Это важное отличие: можно взять весь framework skeleton и получить routing, controllers, service container, config, profiler, console и security из коробки, а можно подключить один пакет вроде symfony/console, symfony/routing или symfony/http-foundation в проект без Symfony-приложения. Поэтому Symfony стоит читать как мост между чистым PHP, Composer, Packagist и composer.json, Autoloading и PSR-4, HTTP-слоем и фреймворками поверх этих соглашений.
На практике Symfony часто выбирают там, где нужны явные границы, долгий срок жизни проекта, сильная инфраструктура компонентов и предсказуемая архитектура. Laravel делает ставку на цельный developer experience и «framework-way»; Symfony чаще ощущается как конструктор: больше конфигурации и понятий, зато легче брать только нужные части и строить систему вокруг них.
flowchart TD
A[HTTP request] --> B[Front controller / Kernel]
B --> C[HttpFoundation Request]
C --> D[Routing]
D --> E[Controller]
E --> F[Service container]
F --> G[Application services]
E --> H[EventDispatcher]
E --> I[Messenger bus]
I --> J[Sync handler или queue transport]
E --> K[HttpFoundation Response]
K --> L[HTTP response]HttpFoundation, Routing и контроллеры
Базовая web-модель Symfony строится вокруг Request и Response из компонента HttpFoundation. Request::createFromGlobals() собирает данные из суперглобалов, query string, body, cookies, uploaded files и server parameters в объект. Response хранит body, status code и headers. Это та же предметная область, что в SAPI и суперглобалы, GET, POST и фильтрация ввода и HTTP-заголовки, ответы и редиректы, только с объектной оболочкой.
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$name = $request->query->get('name', 'мир');
$response = new Response("Привет, {$name}", Response::HTTP_OK);
$response->headers->set('Content-Type', 'text/plain; charset=UTF-8');
$response->send();Routing связывает URL с controller action. В современных Symfony-приложениях часто используют PHP attributes, хотя YAML, XML и PHP-конфиги тоже поддерживаются. Controller обычно возвращает Response или значение, которое framework умеет преобразовать в response.
namespace App\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
final class HealthController
{
#[Route('/health', name: 'health', methods: ['GET'])]
public function __invoke(): JsonResponse
{
return new JsonResponse(['ok' => true]);
}
}Symfony исторически использует HttpFoundation, а не PSR-7 как основной request/response API. Это не конфликт: для совместимости есть bridge-пакеты, а сама идея стандартизированных HTTP-сообщений подробно относится к PSR-7, middleware и HTTP-клиенты. Если библиотека требует Psr\Http\Message\ServerRequestInterface, обычно нужен адаптер, а не переписывание всего приложения.
Service container и dependency injection
Service container — центральная часть Symfony. Он знает, какие классы являются сервисами, как создавать их зависимости и какие настройки подставлять. В типовом config/services.yaml включают autowiring и autoconfiguration: Symfony сам читает type declarations конструктора и регистрирует классы из src/ как сервисы.
# config/services.yaml
services:
_defaults:
autowire: true
autoconfigure: true
App\:
resource: '../src/'После этого controller, command или handler может требовать зависимость явно:
namespace App\Controller;
use App\Service\ReportRenderer;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class ReportController
{
public function __construct(
private ReportRenderer $renderer,
) {}
#[Route('/reports/{id}', methods: ['GET'])]
public function show(int $id): Response
{
return new Response($this->renderer->renderHtml($id));
}
}Важная деталь: container — не глобальный ящик, из которого доменный код должен доставать всё подряд. Хороший Symfony-код обычно получает зависимости через constructor injection. Если нужен интерфейс, несколько реализаций или параметр из env/config, binding задают явно. Это хорошо ложится на темы Классы, объекты и видимость, Неймспейсы и use и PHPDoc, generics и статический анализ: зависимости видны в сигнатурах, а не спрятаны в static-вызовах.
EventDispatcher, Console и Messenger
EventDispatcher даёт способ ослабить связность между частями приложения. Один код dispatch-ит событие, другой слушает его и реагирует. Внутри самого framework есть kernel events вокруг request lifecycle; в приложении события часто используют для side effects: логирования, уведомлений, аудита, обновления read model.
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
final class SecurityHeadersSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [ResponseEvent::class => 'onResponse'];
}
public function onResponse(ResponseEvent $event): void
{
$event->getResponse()->headers->set('X-Frame-Options', 'DENY');
}
}Console — отдельный компонент для CLI-команд. Он используется в Symfony-приложениях, Composer-пакетах и самостоятельных инструментах. Команды удобно применять для migrations, импорта данных, обслуживания очередей, cron-задач и внутренних админских операций.
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(name: 'app:reindex')]
final class ReindexCommand
{
public function __invoke(SymfonyStyle $io): int
{
$io->success('Индекс обновлён');
return Command::SUCCESS;
}
}Messenger добавляет message bus: объект-сообщение отправляется в bus, а handler обрабатывает его синхронно или через transport — например, очередь. Это смежно с Очереди, фоновые задачи и воркеры: как только handler уезжает в worker, появляются retries, timeouts, failed messages, идемпотентность и мониторинг.
final readonly class SendWelcomeEmail
{
public function __construct(public int $userId) {}
}
final class SendWelcomeEmailHandler
{
public function __invoke(SendWelcomeEmail $message): void
{
// загрузить пользователя и отправить письмо
}
}Flex, bundles и экосистема
Symfony Flex — Composer plugin, который применяет recipes: добавляет конфиги, файлы, env-переменные и registration для пакетов. Поэтому composer require symfony/mailer или composer require symfony/messenger в Symfony-приложении обычно делает больше, чем просто кладёт пакет в vendor/: проект получает готовую минимальную интеграцию.
Bundles — способ подключать функциональность к Symfony-приложению. FrameworkBundle, SecurityBundle, TwigBundle, DoctrineBundle, MonologBundle и другие bundles связывают компоненты с application kernel и конфигурацией. На уровне идеи bundle — модуль интеграции, а component — переиспользуемая библиотека. Эту границу полезно держать в голове: symfony/console можно использовать где угодно, а bundle обычно рассчитан на Symfony-приложение.
Где Symfony особенно уместен
Symfony хорошо подходит для больших B2B-систем, API, админок, монолитов с долгим жизненным циклом, проектов с сильными требованиями к тестируемости и команд, которым важна явная архитектура. Он также удобен как источник отдельных компонентов: многие PHP-проекты используют Console, Finder, Process, HttpFoundation, EventDispatcher или DependencyInjection без полного Symfony framework.
Главный риск Symfony — переусложнить маленький проект. Если приложение — два endpoint-а и простой middleware pipeline, может быть достаточно Slim и Mezzio. Если команда хочет максимально цельный full-stack путь с большим количеством готовых продуктовых пакетов, стоит сравнить с Laravel. А если проект выбирается не по вкусу, а по ограничениям бизнеса, легаси, найма и поддержки, лучше смотреть общую статью Выбор PHP-фреймворка.
См. также
- Composer, Packagist и composer.json — как Symfony-пакеты и Flex recipes попадают в проект.
- Autoloading и PSR-4 — почему сервисы находятся по namespace и файловому пути.
- PSR-7, middleware и HTTP-клиенты — отличие PSR-7 HTTP messages от Symfony HttpFoundation и где нужны bridges.
- HTTP-заголовки, ответы и редиректы — базовые правила, которые остаются верными под HttpFoundation.
- Очереди, фоновые задачи и воркеры — эксплуатационная сторона Symfony Messenger.
- Laravel, Yii, Slim и Mezzio, Выбор PHP-фреймворка — соседние варианты в PHP-экосистеме.