Symfony/Mailer для PHP: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Symfony/Mailer для PHP: полное руководство по отправке почты

1. Что это и зачем нужна

Symfony/Mailer — это мощный и гибкий компонент экосистемы Symfony, предназначенный для отправки электронных писем из PHP-приложений. В отличие от встроенной функции PHP mail(), которая требует ручной настройки заголовков и MIME-типов, Symfony/Mailer предоставляет объектно-ориентированный API для создания, форматирования и отправки сообщений через различные транспорты (SMTP, Sendmail, API сторонних сервисов).

Основная задача библиотеки — абстрагировать процесс отправки почты, позволяя разработчику сосредоточиться на содержимом письма, а не на низкоуровневых деталях протоколов. Symfony/Mailer поддерживает HTML-шаблоны, вложения, встраивание изображений, подпись DKIM, а также интеграцию с популярными почтовыми сервисами через Mailer DSN (Data Source Name).

Библиотека незаменима для проектов, где требуется надежная отправка транзакционных писем (подтверждение регистрации, сброс пароля, уведомления), маркетинговых рассылок или любых других email-уведомлений. Благодаря модульной архитектуре Symfony/Mailer легко тестируется (через MockTransport) и масштабируется.

2. Установка

Установка производится через Composer — стандартный менеджер пакетов для PHP. Выполните в терминале в корне вашего проекта:

composer require symfony/mailer

Если вы используете Symfony Flex (рекомендуется), то после установки будет автоматически создан файл конфигурации config/packages/mailer.yaml. Для работы с популярными почтовыми сервисами (SendGrid, Mailgun, Amazon SES и др.) установите соответствующий адаптер, например:

composer require symfony/sendgrid-mailer
composer require symfony/mailgun-mailer
composer require symfony/amazon-mailer

Также потребуется компонент symfony/http-client для работы с API-транспортами:

composer require symfony/http-client

3. Быстрый старт — минимальный рабочий пример

После установки библиотеки вы можете отправить первое письмо буквально в несколько строк. Создайте файл send_email.php:

<?php
// send_email.php
require_once 'vendor/autoload.php';

use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
use Symfony\Component\Mime\Email;

// 1. Создаем транспорт (SMTP-сервер Gmail)
$transport = new EsmtpTransport('smtp.gmail.com', 587, true);
$transport->setUsername('your-email@gmail.com');
$transport->setPassword('your-app-password'); // Используйте пароль приложения

// 2. Создаем Mailer
$mailer = new Mailer($transport);

// 3. Создаем письмо
$email = (new Email())
    ->from('your-email@gmail.com')
    ->to('recipient@example.com')
    ->subject('Привет от Symfony Mailer!')
    ->text('Это тестовое письмо, отправленное с помощью Symfony/Mailer.')
    ->html('<h1>Привет!</h1><p>Это <strong>HTML</strong> версия письма.</p>');

// 4. Отправляем
try {
    $mailer->send($email);
    echo "Письмо отправлено успешно!\n";
} catch (\Exception $e) {
    echo "Ошибка: " . $e->getMessage() . "\n";
}

Запустите скрипт:

php send_email.php

Если всё настроено правильно, вы получите письмо на указанный адрес.

4. Основные методы/классы

4.1. Email::class — класс для создания письма

Сигнатура: new Email() (конструктор без параметров)

Что делает: Создает объект письма с методами-сеттерами для настройки всех атрибутов (отправитель, получатель, тема, тело письма, вложения и т.д.).

Пример:

use Symfony\Component\Mime\Email;

$email = (new Email())
    ->from('sender@example.com')
    ->to('recipient@example.com')
    ->cc('cc@example.com')
    ->bcc('bcc@example.com')
    ->replyTo('support@example.com')
    ->priority(Email::PRIORITY_HIGH)
    ->subject('Важное уведомление')
    ->text('Текстовая версия')
    ->html('<h1>HTML версия</h1>');

4.2. Mailer::send() — отправка письма

Сигнатура: public function send(Email $email, ?Envelope $envelope = null): void

Что делает: Отправляет подготовленное письмо через настроенный транспорт. Может выбросить исключение TransportExceptionInterface при ошибке.

Пример:

$mailer->send($email);

4.3. Mailer::sendWithEnvelope() — отправка с конвертом

Сигнатура: public function sendWithEnvelope(Email $email, Envelope $envelope): void

Что делает: Отправляет письмо с явно заданным конвертом (Envelope), что позволяет переопределить адреса отправителя и получателя на транспортном уровне.

Пример:

use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mime\Address;

$envelope = new Envelope(
    new Address('bounce@example.com'),
    [new Address('actual-recipient@example.com')]
);
$mailer->sendWithEnvelope($email, $envelope);

4.4. AbstractTransport::send() — низкоуровневая отправка

Сигнатура: abstract protected function doSend(SentMessage $message): void

Что делает: Базовый метод для всех транспортов. Реализует фактическую отправку через SMTP, API или другой протокол. Разработчики редко вызывают его напрямую.

Пример: Используется внутри Mailer::send().

4.5. Transport::fromDsn() — создание транспорта из DSN

Сигнатура: public static function fromDsn(string $dsn, ?HttpClientInterface $client = null, ?EventDispatcherInterface $dispatcher = null): TransportInterface

Что делает: Создает транспорт на основе DSN-строки (например, smtp://user:pass@smtp.example.com:587). Упрощает конфигурацию.

Пример:

use Symfony\Component\Mailer\Transport;

$transport = Transport::fromDsn('smtp://user:pass@smtp.example.com:587?encryption=tls');
$mailer = new Mailer($transport);

4.6. Email::attach() — добавление вложения

Сигнатура: public function attach(string $body, ?string $name = null, ?string $contentType = null): static

Что делает: Добавляет файл к письму в виде вложения. Можно передать содержимое файла, имя и MIME-тип.

Пример:

$email->attach(
    file_get_contents('/path/to/document.pdf'),
    'document.pdf',
    'application/pdf'
);

4.7. Email::embed() — встраивание изображения

Сигнатура: public function embed(string $body, ?string $name = null, ?string $contentType = null): string

Что делает: Встраивает изображение (или другой контент) непосредственно в HTML-тело письма. Возвращает CID (Content-ID), который можно использовать в тегах <img>.

Пример:

$cid = $email->embed(
    file_get_contents('/path/to/logo.png'),
    'logo.png',
    'image/png'
);
$email->html('<img src="cid:' . $cid . '" alt="Logo">');

4.8. Email::addPart() — добавление произвольной MIME-части

Сигнатура: public function addPart(BodyRendererInterface $part): static

Что делает: Добавляет любую MIME-часть к письму (например, календарь iCal, подпись DKIM). Позволяет расширять функциональность письма.

Пример:

use Symfony\Component\Mime\Part\TextPart;

$icalPart = new TextPart(
    file_get_contents('/path/to/event.ics'),
    'text/calendar; method=REQUEST'
);
$email->addPart($icalPart);

4.9. Mailer::getTransport() — получение текущего транспорта

Сигнатура: public function getTransport(): TransportInterface

Что делает: Возвращает объект транспорта, используемый Mailer'ом. Полезно для отладки или проверки конфигурации.

Пример:

$transport = $mailer->getTransport();
echo get_class($transport); // Например, Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport

4.10. Email::getHeaders() — работа с заголовками

Сигнатура: public function getHeaders(): Headers

Что делает: Возвращает объект Headers, позволяющий добавлять, изменять или удалять произвольные заголовки письма (например, X-Mailer, List-Unsubscribe).

Пример:

$headers = $email->getHeaders();
$headers->addTextHeader('X-Custom-Header', 'MyValue');
$headers->remove('X-Mailer');

5. Реальный практический пример (150+ строк кода)

Рассмотрим пример отправки письма с подтверждением регистрации, включая HTML-шаблон, вложение и логирование. Создадим класс RegistrationMailer:

<?php
// src/Mailer/RegistrationMailer.php

namespace App\Mailer;

use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Address;
use Psr\Log\LoggerInterface;

class RegistrationMailer
{
    private Mailer $mailer;
    private LoggerInterface $logger;
    private string $fromEmail;
    private string $fromName;

    public function __construct(
        string $dsn,
        LoggerInterface $logger,
        string $fromEmail = 'noreply@example.com',
        string $fromName = 'Example App'
    ) {
        $transport = Transport::fromDsn($dsn);
        $this->mailer = new Mailer($transport);
        $this->logger = $logger;
        $this->fromEmail = $fromEmail;
        $this->fromName = $fromName;
    }

    /**
     * Отправляет письмо с подтверждением регистрации.
     *
     * @param string $recipientEmail Email получателя
     * @param string $recipientName  Имя получателя
     * @param string $confirmationLink Ссылка для подтверждения
     * @param string $attachmentPath  Путь к файлу вложения (опционально)
     * @return bool true в случае успеха, false при ошибке
     */
    public function sendConfirmationEmail(
        string $recipientEmail,
        string $recipientName,
        string $confirmationLink,
        ?string $attachmentPath = null
    ): bool {
        try {
            // Создаем HTML-шаблон письма
            $htmlContent = $this->buildHtmlTemplate($recipientName, $confirmationLink);
            $textContent = "Здравствуйте, $recipientName!\n\n"
                         . "Для подтверждения регистрации перейдите по ссылке:\n"
                         . "$confirmationLink\n\n"
                         . "С уважением, команда Example App.";

            // Формируем письмо
            $email = (new Email())
                ->from(new Address($this->fromEmail, $this->fromName))
                ->to(new Address($recipientEmail, $recipientName))
                ->subject('Подтверждение регистрации на Example App')
                ->text($textContent)
                ->html($htmlContent)
                ->priority(Email::PRIORITY_HIGH);

            // Добавляем вложение, если указано
            if ($attachmentPath !== null && file_exists($attachmentPath)) {
                $email->attach(
                    file_get_contents($attachmentPath),
                    basename($attachmentPath),
                    mime_content_type($attachmentPath) ?: 'application/octet-stream'
                );
            }

            // Добавляем кастомный заголовок для отслеживания
            $headers = $email->getHeaders();
            $headers->addTextHeader('X-Confirmation-Type', 'registration');

            // Отправляем
            $this->mailer->send($email);

            // Логируем успех
            $this->logger->info('Confirmation email sent', [
                'recipient' => $recipientEmail,
                'subject                    

Рекомендаций