symfony/mailer
Symfony Mailer helps you send emails via SMTP and other transports with a clean API. Build Email/TemplatedEmail messages, add attachments and headers, and integrate with Twig templates for HTML rendering. Configure transports via DSN and send reliably.
Installation:
composer require symfony/mailer
For Twig integration (recommended for Laravel):
composer require symfony/twig-bridge
Configure .env:
MAIL_MAILER=smtp
MAIL_HOST=your-smtp-host
MAIL_PORT=587
MAIL_USERNAME=your-username
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="[email protected]"
MAIL_FROM_NAME="${APP_NAME}"
Service Provider:
Register the SymfonyMailerServiceProvider in config/app.php:
'providers' => [
// ...
Symfony\Component\Mailer\MailerServiceProvider::class,
],
First Email (Plain Text):
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
public function sendWelcomeEmail(MailerInterface $mailer)
{
$email = (new Email())
->from('[email protected]')
->to('[email protected]')
->subject('Welcome!')
->text('Thanks for signing up!');
$mailer->send($email);
}
First Email (HTML with Twig):
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
public function sendTemplatedEmail(MailerInterface $mailer)
{
$email = (new TemplatedEmail())
->from('[email protected]')
->to('[email protected]')
->subject('Welcome!')
->htmlTemplate('emails/welcome.twig')
->context(['name' => 'John']);
$mailer->send($email);
}
config/mail.php (Laravel’s mail config, now compatible with Symfony Mailer)resources/views/emails/ (Store Twig templates here)app/Providers/AppServiceProvider.php (For custom mailers or transports)Leverage Laravel’s container to inject MailerInterface or TransportInterface:
use Symfony\Component\Mailer\MailerInterface;
public function __construct(MailerInterface $mailer) {
$this->mailer = $mailer;
}
Use RoundRobinTransport for failover or load balancing:
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mailer\Transport\RoundRobinTransport;
$transports = [
Transport::fromDsn('smtp://user:[email protected]'),
Transport::fromDsn('sendmail:///usr/sbin/sendmail -bs'),
];
$roundRobin = new RoundRobinTransport($transports);
$mailer = new Mailer($roundRobin);
Attach listeners for logging, analytics, or custom logic:
use Symfony\Component\Mailer\EventListener\MessageListener;
use Symfony\Component\EventDispatcher\EventDispatcher;
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new class implements MessageListenerInterface {
public function onMessage(MessageEvent $event) {
// Log or modify the message
}
});
$transport = Transport::fromDsn('smtp://...', $dispatcher);
resources/views/emails/.TemplatedEmail for dynamic content:
$email = (new TemplatedEmail())
->htmlTemplate('emails/invoice.twig')
->context(['amount' => 99.99, 'due_date' => now()->addDays(7)]);
$email = (new Email())
->attachFromPath('/path/to/file.pdf')
->embedFromPath('/path/to/image.png', 'image-id')
->html('<img src="cid:image-id">');
Use NullTransport for unit tests:
use Symfony\Component\Mailer\Transport\NullTransport;
$mailer = new Mailer(new NullTransport());
$mailer->send($email); // No actual email sent
Pair with Laravel Queues for async sending:
use Illuminate\Support\Facades\Bus;
Bus::dispatch(function () use ($mailer, $email) {
$mailer->send($email);
});
Twig Template Paths:
resources/views/emails/ and use htmlTemplate() with the correct path (e.g., 'emails/welcome.twig').TwigEnvironment is properly configured in AppServiceProvider.SMTP Authentication Failures:
.env credentials and encryption (tls/ssl).NullTransport or LogTransport to inspect raw messages:
$transport = new LogTransport();
$mailer = new Mailer($transport);
HTML vs. Text Conflicts:
text() and html() content. Some email clients (e.g., Outlook) ignore HTML if text is missing.->text($email->html) as a fallback.Attachment Encoding Issues:
->attach() with explicit encoding:
$email->attachFromPath($path, ['as' => 'encoded-filename.pdf']);
Event Dispatcher Scope:
EventDispatcher is shared across requests (e.g., via Laravel’s service container).Microsoft Graph API Quirks:
Return-Path and Sender headers if using MicrosoftGraphApiTransport (see release notes).Log Raw Messages:
use Symfony\Component\Mailer\Transport\LogTransport;
$transport = new LogTransport();
$mailer = new Mailer($transport);
Check Laravel logs for the raw email output.
Inspect Headers:
Use ->getHeaders() on the Email object to verify custom headers:
$email->getHeaders()->get('X-Custom-Header');
Transport-Specific Issues:
sendmail:///usr/sbin/sendmail -bs).api://[email protected]).Batch Sending:
Use RoundRobinTransport to distribute load across multiple SMTP servers.
Template Caching: Pre-compile Twig templates for faster rendering:
$twig->addExtension(new \Twig\Extension\StringLoaderExtension());
Async Processing: Offload email sending to a queue worker to avoid blocking HTTP responses.
Custom Transports:
Extend AbstractTransport for proprietary APIs:
use Symfony\Component\Mailer\Transport\AbstractTransport;
class CustomTransport extends AbstractTransport {
public function __toString() {
return 'custom://...';
}
public function send(RawMessage $message) { ... }
}
Message Modifiers:
Subclass Email to add domain-specific methods:
class NewsletterEmail extends Email {
public function addUnsubscribeLink(string $url) {
$this->text .= "\n\nUnsubscribe: $url";
}
}
Event Subscribers:
Listen for MessageEvent to log, modify, or reject messages:
use Symfony\Component\Mailer\EventListener\MessageListenerInterface;
class SpamFilterSubscriber implements MessageListenerInterface {
public function onMessage(MessageEvent $event) {
if (str_contains($event->getMessage()->getHtmlBody(), 'win a prize')) {
$event->stopPropagation();
}
}
}
Avoid Hardcoding Credentials:
Always use .env for sensitive data (e.g., SMTP passwords, API keys).
Sanitize Email Inputs: Validate recipient addresses to prevent injection:
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Invalid email address');
}
Rate Limiting:
Implement retries with exponential backoff for transient failures (e.g., using RetryTransport).
**CVE-20
How can I help you explore Laravel packages today?