Sending Emails with Mailer (Symfony Docs) (original) (raw)
Installation
Symfony's Mailer & Mime components form a powerful system for creating and sending emails - complete with support for multipart messages, Twig integration, CSS inlining, file attachments and a lot more. Get them installed with:
Transport Setup
Emails are delivered via a "transport". Out of the box, you can deliver emails over SMTP by configuring the DSN in your .env file (the user,pass and port parameters are optional):
Warning
If the username, password or host contain any character considered special in a URI (such as : / ? # [ ] @ ! $ & ' ( ) * + , ; =), you must encode them. See RFC 3986 for the full list of reserved characters or use theurlencode function to encode them.
Using Built-in Transports
| DSN protocol | Example | Description |
|---|---|---|
| smtp | smtp://user:pass@smtp.example.com:25 | Mailer uses an SMTP server to send emails |
| sendmail | sendmail://default | Mailer uses the local sendmail binary to send emails |
| native | native://default | Mailer uses the sendmail binary and options configured in the sendmail_path setting of php.ini. On Windows hosts, Mailer fallbacks to smtp and smtp_port php.ini settings when sendmail_path is not configured. |
Warning
When using native://default, if php.ini uses the sendmail -tcommand, you won't have error reporting and Bcc headers won't be removed. It's highly recommended to NOT use native://default as you cannot control how sendmail is configured (prefer using sendmail://default if possible).
Using a 3rd Party Transport
Instead of using your own SMTP server or sendmail binary, you can send emails via a third-party provider:
| Service | Install with | Webhook support |
|---|---|---|
| AhaSend | composer require symfony/aha-send-mailer | yes |
| Amazon SES | composer require symfony/amazon-mailer | |
| Azure | composer require symfony/azure-mailer | |
| Brevo | composer require symfony/brevo-mailer | yes |
| Infobip | composer require symfony/infobip-mailer | |
| Mailgun | composer require symfony/mailgun-mailer | yes |
| Mailjet | composer require symfony/mailjet-mailer | yes |
| Mailomat | composer require symfony/mailomat-mailer | yes |
| MailPace | composer require symfony/mail-pace-mailer | |
| MailerSend | composer require symfony/mailer-send-mailer | yes |
| Mailtrap | composer require symfony/mailtrap-mailer | yes |
| Mandrill | composer require symfony/mailchimp-mailer | yes |
| Microsoft Graph | composer require symfony/microsoft-graph-mailer | |
| Postal | composer require symfony/postal-mailer | |
| Postmark | composer require symfony/postmark-mailer | yes |
| Resend | composer require symfony/resend-mailer | yes |
| Scaleway | composer require symfony/scaleway-mailer | |
| SendGrid | composer require symfony/sendgrid-mailer | yes |
| Sweego | composer require symfony/sweego-mailer | yes |
Note
As a convenience, Symfony also provides support for Gmail (composer require symfony/google-mailer), but this should not be used in production. In development, you should probably use an email catcher instead. Note that most supported providers also offer a free tier.
Each library includes a Symfony Flex recipe that will add a configuration example to your .env file. For example, suppose you want to use SendGrid. First, install it:
You'll now have a new line in your .env file that you can uncomment:
The MAILER_DSN isn't a real address: it's a convenient format that offloads most of the configuration work to mailer. The sendgrid scheme activates the SendGrid provider that you installed, which knows all about how to deliver messages via SendGrid. The only part you need to change is theKEY placeholder.
Each provider has different environment variables that the Mailer uses to configure the actual protocol, address and authentication for delivery. Some also have options that can be configured with query parameters at the end of theMAILER_DSN - like ?region= for Amazon SES, Mailgun or Scaleway. Some providers support sending via http, api or smtp. Symfony chooses the best available transport, but you can force to use one:
This table shows the full list of available DSN formats for each third party provider:
| Provider | Formats |
|---|---|
| AhaSend | SMTP ahasend+smtp://USERNAME:PASSWORD@default HTTP n/a API ahasend+api://KEY@default |
| Amazon SES | SMTP ses+smtp://USERNAME:PASSWORD@default HTTP ses+https://ACCESS\_KEY:SECRET\_KEY@default API ses+api://ACCESS_KEY:SECRET_KEY@default |
| Azure | SMTP n/a HTTP n/a API azure+api://ACS_RESOURCE_NAME:KEY@default |
| Brevo | SMTP brevo+smtp://USERNAME:PASSWORD@default HTTP n/a API brevo+api://KEY@default |
| Google Gmail | SMTP gmail+smtp://USERNAME:APP-PASSWORD@default HTTP n/a API n/a |
| Infobip | SMTP infobip+smtp://KEY@default HTTP n/a API infobip+api://KEY@BASE_URL |
| Mandrill | SMTP mandrill+smtp://USERNAME:PASSWORD@default HTTP mandrill+https://KEY@default API mandrill+api://KEY@default |
| MailerSend | SMTP mailersend+smtp://KEY@default HTTP n/a API mailersend+api://KEY@BASE_URL |
| Mailgun | SMTP mailgun+smtp://USERNAME:PASSWORD@default HTTP mailgun+https://KEY:DOMAIN@default API mailgun+api://KEY:DOMAIN@default |
| Mailjet | SMTP mailjet+smtp://ACCESS_KEY:SECRET_KEY@default HTTP n/a API mailjet+api://ACCESS_KEY:SECRET_KEY@default |
| Mailomat | SMTP mailomat+smtp://USERNAME:PASSWORD@default HTTP n/a API mailomat+api://KEY@default |
| MailPace | SMTP mailpace+api://API_TOKEN@default HTTP n/a API mailpace+api://API_TOKEN@default |
| Mailtrap | SMTP mailtrap+smtp://PASSWORD@default HTTP n/a API (Live) mailtrap+api://API_TOKEN@default API (Sandbox) mailtrap+sandbox://API_TOKEN@default/?inboxId=INBOX_ID |
| Microsoft Graph | SMTP n/a HTTP n/a API microsoftgraph+api://CLIENT_APP_ID:CLIENT_APP_SECRET@default?tenantId=TENANT_ID |
| Postal | SMTP n/a HTTP n/a API postal+api://API_KEY@BASE_URL |
| Postmark | SMTP postmark+smtp://ID@default HTTP n/a API postmark+api://KEY@default |
| Resend | SMTP resend+smtp://resend:API_KEY@default HTTP n/a API resend+api://API_KEY@default |
| Scaleway | SMTP scaleway+smtp://PROJECT_ID:API_KEY@default HTTP n/a API scaleway+api://PROJECT_ID:API_KEY@default |
| Sendgrid | SMTP sendgrid+smtp://KEY@default HTTP n/a API sendgrid+api://KEY@default |
| Sweego | SMTP sweego+smtp://LOGIN:PASSWORD@HOST:PORT HTTP n/a API sweego+api://API_KEY@default |
Warning
If your credentials contain special characters, you must URL-encode them. For example, the DSN ses+smtp://ABC1234:abc+12/345@default should be configured as ses+smtp://ABC1234:abc%2B12%2F345@default
Warning
If you want to use the ses+smtp transport together with Messengerto send messages in background, you need to add the ping_threshold parameter to your MAILER_DSN with a value lower than 10: ses+smtp://USERNAME:PASSWORD@default?ping_threshold=9
Note
When using SMTP, the default timeout for sending a message before throwing an exception is the value defined in the default_socket_timeout PHP.ini option.
Note
Besides SMTP, many 3rd party transports offer a web API to send emails. To do so, you have to install (additionally to the bridge) the HttpClient component via composer require symfony/http-client.
Note
To use Google Gmail, you must have a Google Account with 2-Step-Verification (2FA) enabled and you must use App Password to authenticate. Also note that Google revokes your App Passwords when you change your Google Account password and then you need to generate a new one. Using other methods (like XOAUTH2 or the Gmail API) are not supported currently. You should use Gmail for testing purposes only and use a real provider in production.
Tip
If you want to override the default host for a provider (to debug an issue using a service like requestbin.com), change default by your host:
Note that the protocol is always HTTPs and cannot be changed.
Note
The specific transports, e.g. mailgun+smtp are designed to work without any manual configuration. Changing the port by appending it to your DSN is not supported for any of these <provider>+smtp transports. If you need to change the port, use the smtp transport instead, like so:
Tip
Some third party mailers, when using the API, support status callbacks via webhooks. See the Webhook documentation for more details.
High Availability
Symfony's mailer supports high availability via a technique called "failover" to ensure that emails are sent even if one mailer server fails.
A failover transport is configured with two or more transports and thefailover keyword:
The failover-transport starts using the first transport and if it fails, it will retry the same delivery with the next transports until one of them succeeds (or until all of them fail).
By default, delivery is retried 60 seconds after a failed attempt. You can adjust the retry period by setting the retry_period option in the DSN:
Load Balancing
Symfony's mailer supports load balancing via a technique called "round-robin" to distribute the mailing workload across multiple transports.
A round-robin transport is configured with two or more transports and theroundrobin keyword:
The round-robin transport starts with a randomly selected transport and then switches to the next available transport for each subsequent email.
As with the failover transport, round-robin retries deliveries until a transport succeeds (or all fail). In contrast to the failover transport, it spreads the load across all its transports.
By default, delivery is retried 60 seconds after a failed attempt. You can adjust the retry period by setting the retry_period option in the DSN:
TLS Peer Verification
By default, SMTP transports perform TLS peer verification. This behavior is configurable with the verify_peer option. Although it's not recommended to disable this verification for security reasons, it can be useful while developing the application or when using a self-signed certificate:
TLS Peer Fingerprint Verification
Additional fingerprint verification can be enforced with the peer_fingerprintoption. This is especially useful when a self-signed certificate is used and disabling verify_peer is needed, but security is still desired. Fingerprint may be specified as SHA1 or MD5 hash:
Disabling Automatic TLS
By default, the Mailer component will use encryption when the OpenSSL extension is enabled and the SMTP server supports STARTTLS. This behavior can be turned off by calling setAutoTls(false) on the EsmtpTransport instance, or by setting the auto_tls option to false in the DSN:
Warning
It's not recommended to disable TLS while connecting to an SMTP server over the Internet, but it can be useful when both the application and the SMTP server are in a secured network, where there is no need for additional encryption.
Note
This setting only works when the smtp:// protocol is used.
Ensure TLS
You may want to ensure that TLS is used (either directly or via STARTTLS) when sending mail over SMTP, regardless of other options or SMTP server support. To require TLS, call setRequireTls(true) on the EsmtpTransport instance, or set the require_tls option to true in the DSN:
When TLS is required, a TransportExceptionis thrown if a TLS connection cannot be established during the initial communication with the SMTP server.
Note
This setting only applies when using the smtp:// protocol.
Binding to IPv4 or IPv6
By default, the underlying SocketStream will bind to IPv4 or IPv6 based on the available interfaces. You can enforce binding to a specific protocol or IP address by using the source_ip option. To bind to IPv4, use:
As per RFC2732, IPv6 addresses must be enclosed in square brackets. To bind to IPv6, use:
Note
This option only works when using the smtp:// protocol.
Overriding default SMTP authenticators
By default, SMTP transports will try to login using all authentication methods available on the SMTP server, one after the other. In some cases, it may be useful to redefine the supported authentication methods to ensure that the preferred method will be used first.
This can be done from EsmtpTransport constructor or using thesetAuthenticators() method:
Other Options
command
Command to be executed by sendmail transport:
local_domain
The domain name to use in HELO command:
restart_threshold
The maximum number of messages to send before re-starting the transport. It can be used together with restart_threshold_sleep:
restart_threshold_sleep
The number of seconds to sleep between stopping and re-starting the transport. It's common to combine it with restart_threshold:
ping_threshold
The minimum number of seconds between two messages required to ping the server:
max_per_second
The number of messages to send per second (0 to disable this limitation):
Custom Transport Factories
If you want to support your own custom DSN (acme://...), you can create a custom transport factory. To do so, create a class that implementsTransportFactoryInterface or, if you prefer, extend the AbstractTransportFactoryclass to save some boilerplate code:
After creating the custom transport class, register it as a service in your application and tag it with themailer.transport_factory tag.
Creating & Sending Messages
To send an email, get a Mailerinstance by type-hinting MailerInterfaceand create an Email object:
That's it! The message will be sent immediately via the transport you configured. If you prefer to send emails asynchronously to improve performance, read theSending Messages Async section. Also, if your application has the Messenger component installed, all emails will be sent asynchronously by default (but you can change that).
Email Addresses
All the methods that require email addresses (from(), to(), etc.) accept both strings or address objects:
Tip
Instead of calling ->from() every time you create a new email, you canconfigure emails globally to set the same From email to all messages.
Note
The local part of the address (what goes before the @) can include UTF-8 characters, except for the sender address (to avoid issues with bounced emails). For example: föóbàr@example.com, 用户@example.com, θσερ@example.com, etc.
Use addTo(), addCc(), or addBcc() methods to add more addresses:
Alternatively, you can pass multiple addresses to each method:
Messages include a number of header fields to describe their contents. Symfony sets all the required headers automatically, but you can set your own headers too. There are different types of headers (Id header, Mailbox header, Date header, etc.) but most of the times you'll set text headers:
Tip
Instead of calling ->addTextHeader() every time you create a new email, you canconfigure emails globally to set the same headers to all sent emails.
Message Contents
The text and HTML contents of the email messages can be strings (usually the result of rendering some template) or PHP resources:
Tip
You can also use Twig templates to render the HTML and text contents. Read the Twig: HTML & CSS section later in this article to learn more.
File Attachments
Use the addPart() method with a File to add files that exist on your file system:
Alternatively you can attach contents from a stream by passing it directly to the DataPart:
Embedding Images
If you want to display images inside your email, you must embed them instead of adding them as attachments. When using Twig to render the email contents, as explained later in this article, the images are embedded automatically. Otherwise, you need to embed them manually.
First, use the addPart() method to add an image from a file or stream:
Use the asInline() method to embed the content instead of attaching it.
The second optional argument of both methods is the image name ("Content-ID" in the MIME standard). Its value is an arbitrary string that must be unique in each email message and is used later to reference the images inside the HTML contents:
The actual Content-ID value present in the e-mail source will be randomly generated by Symfony. You can also use the DataPart::setContentId()method to define a custom Content-ID for the image and use it as its cid reference:
Configuring Emails Globally
Instead of calling ->from() on each Email you create, you can configure this value globally so that it is set on all sent emails. The same is true with ->to()and headers.
Warning
Some third-party providers don't support the usage of keywords like fromin the headers. Check out your provider's documentation before setting any global header.
Handling Sending Failures
Symfony Mailer considers that sending was successful when your transport (SMTP server or third-party provider) accepts the mail for further delivery. The message can later be lost or not delivered because of some problem in your provider, but that's out of reach for your Symfony application.
If there's an error when handing over the email to your transport, Symfony throws a TransportExceptionInterface. Catch that exception to recover from the error or to display some message:
Debugging Emails
The send() method of the mailer service injected when using MailerInterfacedoesn't return anything, so you can't access the sent email information. This is because it sends email messages asynchronously when the Messenger componentis used in the application.
To access information about the sent email, update your code to replace theMailerInterface withTransportInterface:
The send() method of TransportInterface returns an object of typeSentMessage. This is because it always sends the emails synchronously, even if your application uses the Messenger component.
The SentMessage object provides access to the original message (getOriginalMessage()) and to some debug information (getDebug()) such as the HTTP calls done by the HTTP transports, which is useful to debug errors.
You can also access the SentMessage object by listening to the SentMessageEvent, and retrievegetDebug() by listening to the FailedMessageEvent.
Note
Some mailer providers change the Message-Id when sending the email. ThegetMessageId() method from SentMessage always returns the final ID of the message - whether it's the original random ID generated by Symfony or a new one generated by the provider.
Exceptions related to mailer transports (those implementingTransportException) also provide this debug information via the getDebug() method.
Twig: HTML & CSS
The Mime component integrates with the Twig template engineto provide advanced features such as CSS style inlining and support for HTML/CSS frameworks to create complex HTML email messages. First, make sure Twig is installed:
HTML Content
To define the contents of your email with Twig, use theTemplatedEmail class. This class extends the normal Email class but adds some new methods for Twig templates:
Then, create the template:
The Twig template has access to any of the parameters passed in the context()method of the TemplatedEmail class and also to a special variable calledemail, which is an instance ofWrappedTemplatedEmail.
Text Content
When the text content of a TemplatedEmail is not explicitly defined, it is automatically generated from the HTML contents.
Symfony uses the following strategy when generating the text version of an email:
- If an explicit HTML to text converter has been configured (seetwig.mailer.html_to_text_converter), it calls it;
- If not, and if you have league/html-to-markdown installed in your application, it uses it to turn HTML into Markdown (so the text email has some visual appeal);
- Otherwise, it applies the strip_tags PHP function to the original HTML contents.
If you want to define the text content yourself, use the text() method explained in the previous sections or the textTemplate() method provided by the TemplatedEmail class:
Embedding Images
Instead of dealing with the <img src="cid: ..."> syntax explained in the previous sections, when using Twig to render email contents you can refer to image files as usual. First, to simplify things, define a Twig namespace calledimages that points to whatever directory your images are stored in:
Now, use the special email.image() Twig helper to embed the images inside the email contents:
By default this will create an attachment using the file path as file name:Content-Disposition: inline; name="cid..."; filename="@images/logo.png". This behavior can be overridden by passing a custom file name as the third argument:
Inlining CSS Styles
Designing the HTML contents of an email is very different from designing a normal HTML page. For starters, most email clients only support a subset of all CSS features. In addition, popular email clients like Gmail don't support defining styles inside <style> ... </style> sections and you must inline all the CSS styles.
CSS inlining means that every HTML tag must define a style attribute with all its CSS styles. This can make organizing your CSS a mess. That's why Twig provides a CssInlinerExtension that automates everything for you. Install it with:
The extension is enabled automatically. To use it, wrap the entire template with the inline_css filter:
Using External CSS Files
You can also define CSS styles in external files and pass them as arguments to the filter:
You can pass unlimited number of arguments to inline_css() to load multiple CSS files. For this example to work, you also need to define a new Twig namespace called styles that points to the directory where email.css lives:
Rendering Markdown Content
Twig provides another extension called MarkdownExtension that lets you define the email contents using Markdown syntax. To use this, install the extension and a Markdown conversion library (the extension is compatible with several popular libraries):
The extension adds a markdown_to_html filter, which you can use to convert parts or the entire email contents from Markdown to HTML:
Inky Email Templating Language
Tip
Symfony recommends Inky, but you can also use MJML, a more actively maintained alternative for responsive email templates.
Creating beautifully designed emails that work on every email client is so complex that there are HTML/CSS frameworks dedicated to that. One of the most popular frameworks is called Inky. It defines a syntax based on some HTML-like tags which are later transformed into the real HTML code sent to users:
Twig provides integration with Inky via the InkyExtension. First, install the extension in your application:
The extension adds an inky_to_html filter, which can be used to convert parts or the entire email contents from Inky to HTML:
You can combine all filters to create complex email messages:
This makes use of the styles Twig namespace we created earlier. You could, for example, download the foundation-emails.css filedirectly from GitHub and save it in assets/styles.
Signing and Encrypting Messages
It's possible to sign and/or encrypt email messages to increase their integrity/security. Both options can be combined to encrypt a signed message and/or to sign an encrypted message.
Before signing/encrypting messages, make sure to have:
- The OpenSSL PHP extension properly installed and configured;
- A valid S/MIME security certificate.
Tip
When using OpenSSL to generate certificates, make sure to add the-addtrust emailProtection command option.
Warning
Signing and encrypting messages require their contents to be fully rendered. For example, the content of templated emails is rendered by a MessageListener. So, if you want to sign and/or encrypt such a message, you need to do it in a MessageEvent listener run after it (you need to set a negative priority to your listener).
Signing Messages
When signing a message, a cryptographic hash is generated for the entire content of the message (including attachments). This hash is added as an attachment so the recipient can validate the integrity of the received message. However, the contents of the original message are still readable for mailing agents not supporting signed messages, so you must also encrypt the message if you want to hide its contents.
You can sign messages using either S/MIME or DKIM. In both cases, the certificate and private key must be PEM encoded, and can be either created using for example OpenSSL or obtained at an official Certificate Authority (CA). The email recipient must have the CA certificate in the list of trusted issuers in order to verify the signature.
Warning
If you use message signature, sending to Bcc will be removed from the message. If you need to send a message to multiple recipients, you need to compute a new signature for each recipient.
S/MIME Signer
S/MIME is a standard for public key encryption and signing of MIME data. It requires using both a certificate and a private key:
Tip
The SMimeSigner class defines other optional arguments to pass intermediate certificates and to configure the signing process using a bitwise operator options for openssl_pkcs7_sign PHP function.
DKIM Signer
DKIM is an email authentication method that affixes a digital signature, linked to a domain name, to each outgoing email messages. It requires a private key but not a certificate:
Signing Messages Globally
Instead of creating a signer instance for each email, you can configure a global signer that automatically applies to all outgoing messages. This approach minimizes repetition and centralizes your configuration for DKIM and S/MIME signing.
Encrypting Messages
When encrypting a message, the entire message (including attachments) is encrypted using a certificate. Therefore, only the recipients that have the corresponding private key can read the original message contents:
You can pass more than one certificate to the SMimeEncrypter constructor and it will select the appropriate certificate depending on the To option:
Encrypting Messages Globally
Instead of creating a new encrypter for each email, you can configure a global S/MIME encrypter that automatically applies to all outgoing messages:
The repository option is the ID of a service that implementsSmimeCertificateRepositoryInterface. This interface requires only one method: findCertificatePathFor(), which must return the file path to the certificate associated with the given email address:
Multiple Email Transports
You may want to use more than one mailer transport for delivery of your messages. This can be configured by replacing the dsn configuration entry with atransports entry, like:
By default the first transport is used. The other transports can be selected by adding an X-Transport header (which Mailer will remove automatically from the final email):
Sending Messages Async
When you call $mailer->send($email), the email is sent to the transport immediately. To improve performance, you can leverage Messenger to send the messages later via a Messenger transport.
Start by following the Messenger documentation and configuring a transport. Once everything is set up, when you call $mailer->send(), aSendEmailMessage message will be dispatched through the default message bus (messenger.default_bus). Assuming you have a transport called async, you can route the message there:
Thanks to this, instead of being delivered immediately, messages will be sent to the transport to be handled later (see Messenger: Sync & Queued Message Handling). Note that the "rendering" of the email (computed headers, body rendering, ...) is also deferred and will only happen just before the email is sent by the Messenger handler.
When sending an email asynchronously, its instance must be serializable. This is always the case for Mailerinstances, but when sending aTemplatedEmail, you must ensure that the context is serializable. If you have non-serializable variables, like Doctrine entities, either replace them with more specific variables or render the email before calling $mailer->send($email):
You can configure which bus is used to dispatch the message using the message_bus option. You can also set this to false to call the Mailer transport directly and disable asynchronous delivery.
Note
In cases of long-running scripts, and when Mailer uses theSmtpTransportyou may manually disconnect from the SMTP server to avoid keeping an open connection to the SMTP server in between sending emails. You can do so by using the stop() method.
You can also select the transport by adding an X-Bus-Transport header (which will be removed automatically from the final message):
Certain 3rd party transports support email tags and metadata, which can be used for grouping, tracking and workflows. You can add those by using theTagHeader andMetadataHeader classes. If your transport supports headers, it will convert them to their appropriate format:
If your transport does not support tags and metadata, they will be added as custom headers:
The following transports currently support tags and metadata:
- Brevo
- Mailgun
- Mailtrap
- Mandrill
- Postmark
- Sendgrid
The following transports only support tags:
- MailPace
- Resend
The following transports only support metadata:
- Amazon SES (note that Amazon refers to this feature as "tags", but Symfony calls it "metadata" because it contains a key and a value)
Draft Emails
DraftEmail is a special instance ofEmail. Its purpose is to build up an email (with body, attachments, etc) and make available to download as an .eml with the X-Unsent header. Many email clients can open these files and interpret them as draft emails. You can use these to create advanced mailto: links.
Here's an example of making one available to download:
Note
As it's possible for DraftEmail's to be created without a To/From they cannot be sent with the mailer.
Mailer Events
MessageEvent
Event Class: MessageEvent
MessageEvent allows to change the Mailer message and the envelope before the email is sent:
If you want to stop the Message from being sent, call reject() (it will also stop the event propagation):
Execute this command to find out which listeners are registered for this event and their priorities:
SentMessageEvent
Event Class: SentMessageEvent
SentMessageEvent allows you to act on the SentMessageclass to access the original message (getOriginalMessage()) and somedebugging information (getDebug()) such as the HTTP calls made by the HTTP transports, which is useful for debugging errors:
Execute this command to find out which listeners are registered for this event and their priorities:
FailedMessageEvent
Event Class: FailedMessageEvent
FailedMessageEvent allows acting on the initial message in case of a failure and some debugging information (getDebug()) such as the HTTP calls made by the HTTP transports, which is useful for debugging errors:
Execute this command to find out which listeners are registered for this event and their priorities:
Development & Debugging
Enabling an Email Catcher
When developing locally, it is recommended to use an email catcher. If you have enabled Docker support via Symfony recipes, an email catcher is automatically configured. In addition, if you are using the Symfony CLItool, the mailer DSN is automatically exposed via thesymfony binary Docker integration.
Sending Test Emails
Symfony provides a command to send emails, which is useful during development to test if sending emails works correctly:
This command bypasses the Messenger bus, if configured, to ease testing emails even when the Messenger consumer is not running.
Disabling Delivery
While developing (or testing), you may want to disable delivery of messages entirely. You can do this by using null://null as the mailer DSN, either in your .env configuration files or in the mailer configuration file (e.g. in the dev or test environments):
Note
If you're using Messenger and routing to a transport, the message will _still_be sent to that transport.
Always Send to the same Address
Instead of disabling delivery entirely, you might want to always send emails to a specific address, instead of the real address:
Use the allowed_recipients option to define specific addresses that should still receive their original emails. These messages will also be sent to the address(es) defined in recipients, as with all other emails:
With this configuration, all emails will be sent to youremail@example.com. Additionally, emails sent to internal@example.com, internal-monitoring@example.fr, etc., will also be delivered to those addresses.
Write a Functional Test
Symfony provides lots of built-in mailer assertionsto functionally test that an email was sent, its contents or headers, etc. They are available in test classes extendingKernelTestCase or when using the MailerAssertionsTrait:
Tip
If your controller returns a redirect response after sending the email, make sure to have your client not follow redirects. The kernel is rebooted after following the redirection and the message will be lost from the mailer event handler.
This work, including the code samples, is licensed under aCreative Commons BY-SA 3.0 license.