Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Symfony Mailer
https://github.com/symfony/mailer
Admin
Symfony Mailer is a component that helps sending emails with support for multiple transports,
...
Tokens:
8,038
Snippets:
42
Trust Score:
9.2
Update:
1 week ago
Context
Skills
Chat
Benchmark
78.6
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Symfony Mailer Symfony Mailer is a powerful PHP component for sending emails with support for multiple transport protocols including SMTP, Sendmail, and third-party providers. It provides a clean, object-oriented API for composing and delivering email messages with features like transport failover, rate limiting, and event-driven message manipulation. The component integrates seamlessly with the Symfony ecosystem but can be used standalone in any PHP application. The Mailer component supports both synchronous and asynchronous email delivery through Symfony Messenger integration. It provides extensible transport architecture allowing DSN-based configuration, DKIM signing, S/MIME encryption, and Twig template rendering for HTML emails. The event system enables pre-send manipulation of messages and envelopes, while test utilities simplify email assertions in automated tests. ## Creating and Sending Basic Emails The Mailer class sends email messages using a configured transport. Create an Email instance with recipients, subject, and body content, then pass it to the mailer's send method. ```php <?php use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Email; // Create transport from DSN string $transport = Transport::fromDsn('smtp://user:pass@smtp.example.com:587'); $mailer = new Mailer($transport); // Compose email message $email = (new Email()) ->from('sender@example.com') ->to('recipient@example.com') ->cc('cc@example.com') ->bcc('bcc@example.com') ->replyTo('reply@example.com') ->priority(Email::PRIORITY_HIGH) ->subject('Important Notification') ->text('Plain text email body') ->html('<h1>HTML Email</h1><p>Rich content with formatting.</p>'); // Send the email $mailer->send($email); ``` ## Configuring Transports with DSN The Transport class creates transport instances from DSN (Data Source Name) strings. DSN configuration supports various protocols and options for flexible email delivery setup. ```php <?php use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Transport\Dsn; // SMTP transport with authentication $transport = Transport::fromDsn('smtp://username:password@mail.example.com:465'); // SMTP with TLS options $transport = Transport::fromDsn('smtps://user:pass@smtp.example.com:465?verify_peer=false'); // SMTP with additional parameters $transport = Transport::fromDsn('smtp://localhost:25?auto_tls=false&require_tls=true'); // Null transport for testing (discards emails) $transport = Transport::fromDsn('null://null'); // Sendmail transport $transport = Transport::fromDsn('sendmail://default'); // Parse DSN manually $dsn = Dsn::fromString('smtp://user:secret@mail.example.com:587?timeout=30'); echo $dsn->getScheme(); // 'smtp' echo $dsn->getHost(); // 'mail.example.com' echo $dsn->getPort(); // 587 echo $dsn->getUser(); // 'user' echo $dsn->getPassword(); // 'secret' echo $dsn->getOption('timeout'); // '30' ``` ## Using ESMTP Transport with Authentication The EsmtpTransport class provides full ESMTP support with multiple authentication mechanisms (CRAM-MD5, LOGIN, PLAIN, XOAUTH2), automatic TLS negotiation, and configurable security options. ```php <?php use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; use Symfony\Component\Mime\Email; // Create ESMTP transport with credentials $transport = new EsmtpTransport('smtp.example.com', 587, false); $transport->setUsername('smtp_user'); $transport->setPassword('smtp_password'); // Configure TLS settings $transport->setAutoTls(true); // Enable automatic STARTTLS $transport->setRequireTls(true); // Require TLS connection // Set rate limiting (emails per second) $transport->setMaxPerSecond(10); // Send email $email = (new Email()) ->from('noreply@example.com') ->to('user@example.com') ->subject('Account Verification') ->html('<p>Please verify your email address.</p>'); $sentMessage = $transport->send($email); // Access sent message details echo $sentMessage->getMessageId(); echo $sentMessage->getDebug(); ``` ## Configuring Failover and Round-Robin Transports Multiple transports can be combined using FailoverTransport (automatic fallback on failure) or RoundRobinTransport (load distribution) for high availability email delivery. ```php <?php use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Transport\FailoverTransport; use Symfony\Component\Mailer\Transport\RoundRobinTransport; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Email; // Configure failover via DSN (tries transports in order) $transport = Transport::fromDsn('failover(smtp://primary.example.com smtp://backup.example.com)'); // Configure round-robin via DSN (distributes load) $transport = Transport::fromDsn('roundrobin(smtp://server1.example.com smtp://server2.example.com)'); // Programmatic failover setup with retry period $primary = Transport::fromDsn('smtp://primary-smtp.example.com'); $backup = Transport::fromDsn('smtp://backup-smtp.example.com'); $failover = new FailoverTransport( [$primary, $backup], 60 // Retry dead transport after 60 seconds ); // Programmatic round-robin setup $server1 = Transport::fromDsn('smtp://smtp1.example.com'); $server2 = Transport::fromDsn('smtp://smtp2.example.com'); $roundRobin = new RoundRobinTransport( [$server1, $server2], 120 // Retry period for dead transports ); $mailer = new Mailer($roundRobin); $email = (new Email()) ->from('system@example.com') ->to('admin@example.com') ->subject('System Notification') ->text('Load-balanced email delivery'); $mailer->send($email); ``` ## Using Named Transports with X-Transport Header The Transports class manages multiple named transports, allowing runtime selection via the X-Transport header. This enables different delivery methods for different email types. ```php <?php use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Transport\Transports; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Email; // Create named transports $transactional = Transport::fromDsn('smtp://transactional.example.com'); $marketing = Transport::fromDsn('smtp://marketing.example.com'); $internal = Transport::fromDsn('smtp://internal.example.com'); $transports = new Transports([ 'transactional' => $transactional, 'marketing' => $marketing, 'internal' => $internal, ]); $mailer = new Mailer($transports); // Send via specific transport using X-Transport header $email = (new Email()) ->from('newsletters@example.com') ->to('subscriber@example.com') ->subject('Weekly Newsletter') ->text('Newsletter content here'); $email->getHeaders()->addTextHeader('X-Transport', 'marketing'); $mailer->send($email); // Default transport used when no X-Transport specified $orderConfirmation = (new Email()) ->from('orders@example.com') ->to('customer@example.com') ->subject('Order Confirmed') ->text('Your order has been confirmed.'); $mailer->send($orderConfirmation); // Uses 'transactional' (first defined) ``` ## Working with Email Envelopes The Envelope class controls the actual SMTP envelope sender and recipients, which can differ from the message headers (From/To). This is useful for bounce handling and recipient management. ```php <?php use Symfony\Component\Mailer\Envelope; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; $transport = Transport::fromDsn('smtp://localhost'); $mailer = new Mailer($transport); // Create custom envelope for bounce handling $envelope = new Envelope( new Address('bounces@example.com'), // Actual SMTP sender (for bounces) [ new Address('recipient1@example.com'), new Address('recipient2@example.com'), ] ); // Email headers can show different sender $email = (new Email()) ->from('noreply@example.com') // Displayed in email client ->to('recipient1@example.com') ->subject('Important Update') ->text('Message content'); // Envelope overrides actual delivery $mailer->send($email, $envelope); // Create envelope from message automatically $autoEnvelope = Envelope::create($email); echo $autoEnvelope->getSender()->getAddress(); // Check for Unicode localparts (SMTPUTF8 support) $hasUnicode = $envelope->anyAddressHasUnicodeLocalpart(); ``` ## Handling Message Events The event system allows manipulation of messages before sending. Subscribe to MessageEvent to modify content, headers, or reject emails based on custom logic. ```php <?php use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Mailer\Event\MessageEvent; use Symfony\Component\Mailer\Event\SentMessageEvent; use Symfony\Component\Mailer\Event\FailedMessageEvent; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Email; use Symfony\Component\Mime\Address; $dispatcher = new EventDispatcher(); // Modify messages before sending $dispatcher->addListener(MessageEvent::class, function (MessageEvent $event) { $message = $event->getMessage(); if ($message instanceof Email) { // Add tracking header $message->getHeaders()->addTextHeader('X-Campaign-ID', 'spring-2024'); // Modify envelope recipients $envelope = $event->getEnvelope(); // Reject spam-like messages if (str_contains($message->getSubject() ?? '', 'URGENT!!!')) { $event->reject(); return; } } // Check if message is queued for async delivery if ($event->isQueued()) { // Add Messenger stamps for queued messages // $event->addStamp(new SomeStamp()); } }); // Log successful sends $dispatcher->addListener(SentMessageEvent::class, function (SentMessageEvent $event) { $sentMessage = $event->getMessage(); error_log('Email sent: ' . $sentMessage->getMessageId()); }); // Handle failures $dispatcher->addListener(FailedMessageEvent::class, function (FailedMessageEvent $event) { $error = $event->getError(); error_log('Email failed: ' . $error->getMessage()); }); $transport = Transport::fromDsn('smtp://localhost', $dispatcher); $mailer = new Mailer($transport, null, $dispatcher); $email = (new Email()) ->from('app@example.com') ->to('user@example.com') ->subject('Welcome!') ->text('Welcome to our platform.'); $mailer->send($email); ``` ## Using EnvelopeListener for Global Sender/Recipient Control EnvelopeListener automatically overrides envelope settings globally, useful for development environments or centralized bounce handling. ```php <?php use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Mailer\EventListener\EnvelopeListener; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Email; use Symfony\Component\Mime\Address; $dispatcher = new EventDispatcher(); // Override all emails to go to test mailbox in development $listener = new EnvelopeListener( 'bounces@example.com', // Override sender [new Address('dev-testing@example.com')], // Override recipients ['.*@allowed-domain\.com'] // Regex patterns for allowed recipients ); $dispatcher->addSubscriber($listener); $transport = Transport::fromDsn('smtp://localhost', $dispatcher); $mailer = new Mailer($transport, null, $dispatcher); // All emails redirected to dev-testing@example.com $email = (new Email()) ->from('app@example.com') ->to('real-customer@example.com') // Will be overridden ->cc('manager@example.com') // Will be overridden ->subject('Order Confirmation') ->text('Your order details...'); $mailer->send($email); ``` ## Using MessageListener for Default Headers and Template Rendering MessageListener sets default headers and integrates with Twig for template-based email rendering. ```php <?php use Symfony\Bridge\Twig\Mime\BodyRenderer; use Symfony\Bridge\Twig\Mime\TemplatedEmail; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Mailer\EventListener\MessageListener; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Header\Headers; use Twig\Environment; use Twig\Loader\FilesystemLoader; // Setup Twig environment $loader = new FilesystemLoader('/path/to/templates'); $twig = new Environment($loader); $bodyRenderer = new BodyRenderer($twig); // Create default headers $defaultHeaders = new Headers(); $defaultHeaders->addMailboxHeader('From', 'noreply@example.com'); $defaultHeaders->addMailboxListHeader('Bcc', ['archive@example.com']); $defaultHeaders->addTextHeader('X-Mailer', 'MyApp/1.0'); $dispatcher = new EventDispatcher(); // Configure listener with header rules $listener = new MessageListener( $defaultHeaders, $bodyRenderer, [ 'from' => MessageListener::HEADER_SET_IF_EMPTY, 'bcc' => MessageListener::HEADER_ADD, 'x-mailer' => MessageListener::HEADER_REPLACE, ] ); $dispatcher->addSubscriber($listener); $transport = Transport::fromDsn('smtp://localhost', $dispatcher); $mailer = new Mailer($transport, null, $dispatcher); // Send templated email $email = (new TemplatedEmail()) ->to('user@example.com') ->subject('Welcome, {{ username }}!') ->htmlTemplate('emails/welcome.html.twig') ->context([ 'username' => 'John', 'activation_link' => 'https://example.com/activate/abc123', ]); $mailer->send($email); ``` ## DKIM Signing Messages DkimSignedMessageListener automatically signs outgoing messages with DKIM for email authentication and deliverability. ```php <?php use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Mailer\EventListener\DkimSignedMessageListener; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Crypto\DkimSigner; use Symfony\Component\Mime\Email; // Create DKIM signer $signer = new DkimSigner( file_get_contents('/path/to/dkim-private-key.pem'), 'example.com', // Domain 'mail' // DKIM selector ); $dispatcher = new EventDispatcher(); $listener = new DkimSignedMessageListener($signer); $dispatcher->addSubscriber($listener); $transport = Transport::fromDsn('smtp://localhost', $dispatcher); $mailer = new Mailer($transport, null, $dispatcher); // Emails are automatically DKIM-signed $email = (new Email()) ->from('noreply@example.com') ->to('recipient@example.com') ->subject('Authenticated Message') ->text('This email is signed with DKIM.'); $mailer->send($email); ``` ## Asynchronous Email Delivery with Messenger The Mailer integrates with Symfony Messenger for asynchronous email delivery, improving application response times by queuing emails for background processing. ```php <?php use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mailer\Messenger\MessageHandler; use Symfony\Component\Mailer\Messenger\SendEmailMessage; use Symfony\Component\Mailer\Transport; use Symfony\Component\Messenger\Handler\HandlersLocator; use Symfony\Component\Messenger\MessageBus; use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; use Symfony\Component\Mime\Email; // Setup transport $transport = Transport::fromDsn('smtp://localhost'); $dispatcher = new EventDispatcher(); // Create message handler $messageHandler = new MessageHandler($transport); // Setup Messenger bus $bus = new MessageBus([ new HandleMessageMiddleware(new HandlersLocator([ SendEmailMessage::class => [$messageHandler], ])), ]); // Create mailer with bus for async delivery $mailer = new Mailer($transport, $bus, $dispatcher); // Email is dispatched to bus instead of sent immediately $email = (new Email()) ->from('app@example.com') ->to('user@example.com') ->subject('Background Task Complete') ->text('Your export is ready for download.'); $mailer->send($email); // Process queue manually (normally done by messenger:consume command) // The handler will call $transport->send() when processing the message ``` ## Using Custom Headers for Metadata and Tags MetadataHeader and TagHeader provide standardized ways to add tracking information and tags to emails for analytics and filtering. ```php <?php use Symfony\Component\Mailer\Header\MetadataHeader; use Symfony\Component\Mailer\Header\TagHeader; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Email; $transport = Transport::fromDsn('smtp://localhost'); $mailer = new Mailer($transport); $email = (new Email()) ->from('marketing@example.com') ->to('subscriber@example.com') ->subject('Special Offer Inside!') ->text('Check out our latest deals.'); // Add metadata (creates X-Metadata-* headers) $email->getHeaders()->add(new MetadataHeader('campaign-id', 'summer-sale-2024')); $email->getHeaders()->add(new MetadataHeader('user-segment', 'premium')); // Add tags (creates X-Tag header) $email->getHeaders()->add(new TagHeader('promotional')); $email->getHeaders()->add(new TagHeader('newsletter')); $mailer->send($email); ``` ## Testing Email Functionality The Test namespace provides PHPUnit constraints for asserting email behavior in automated tests. ```php <?php use PHPUnit\Framework\TestCase; use Symfony\Component\Mailer\Event\MessageEvents; use Symfony\Component\Mailer\Test\Constraint\EmailCount; use Symfony\Component\Mailer\Test\Constraint\EmailIsQueued; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Mailer\EventListener\MessageLoggerListener; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Email; class EmailTest extends TestCase { public function testEmailIsSent(): void { // Setup logger to capture sent emails $logger = new MessageLoggerListener(); $dispatcher = new EventDispatcher(); $dispatcher->addSubscriber($logger); // Use null transport for testing $transport = Transport::fromDsn('null://null', $dispatcher); $mailer = new Mailer($transport, null, $dispatcher); // Send test email $email = (new Email()) ->from('test@example.com') ->to('user@example.com') ->subject('Test Email') ->text('Test content'); $mailer->send($email); // Assert email count $events = $logger->getEvents(); $this->assertThat($events, new EmailCount(1)); // Assert on specific transport $this->assertThat($events, new EmailCount(1, 'null://')); // Check queued vs sent $this->assertThat($events, new EmailCount(1, null, false)); // sent } } ``` ## Console Command for Testing Transports The MailerTestCommand provides a CLI tool to verify transport configuration by sending test emails. ```bash # Basic usage - send test email php bin/console mailer:test recipient@example.com # Customize sender and content php bin/console mailer:test recipient@example.com \ --from=sender@example.com \ --subject="Test from staging" \ --body="Verifying email configuration" # Test specific named transport php bin/console mailer:test recipient@example.com --transport=marketing ``` ```php <?php use Symfony\Component\Console\Application; use Symfony\Component\Mailer\Command\MailerTestCommand; use Symfony\Component\Mailer\Transport; // Register command programmatically $transport = Transport::fromDsn('smtp://localhost'); $command = new MailerTestCommand($transport); $application = new Application(); $application->add($command); $application->run(); ``` ## Sendmail Transport Configuration SendmailTransport sends emails through the local sendmail binary, supporting both interactive (-bs) and non-interactive (-t) modes. ```php <?php use Symfony\Component\Mailer\Transport; use Symfony\Component\Mailer\Transport\SendmailTransport; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mime\Email; // Via DSN (uses default sendmail path) $transport = Transport::fromDsn('sendmail://default'); // Custom sendmail command with options $transport = new SendmailTransport('/usr/sbin/sendmail -bs'); // Non-interactive mode (less reliable, no error reporting) $transport = new SendmailTransport('/usr/sbin/sendmail -oi -t'); $mailer = new Mailer($transport); $email = (new Email()) ->from('server@example.com') ->to('admin@example.com') ->subject('Server Alert') ->text('Disk usage is above 90%'); $mailer->send($email); ``` ## Summary Symfony Mailer excels in enterprise applications requiring reliable email delivery with high availability through failover and round-robin transport configurations. The DSN-based setup simplifies configuration management across environments, while the event-driven architecture enables powerful customizations like DKIM signing, envelope manipulation, and message filtering. Integration with Symfony Messenger provides non-blocking email delivery essential for high-performance web applications. The component's testing utilities make it straightforward to verify email functionality in automated test suites without sending actual emails. For production deployments, the combination of multiple transport backends, rate limiting, automatic TLS negotiation, and comprehensive error handling ensures reliable email delivery. The modular design allows developers to start with simple SMTP configuration and progressively add features like template rendering, encryption, and analytics tracking as requirements evolve.