Asynchronous e-mails with Symfony Mailer
By: Arjan Frans
Tags: Messenger, Symfony, Asynchronous, PHP, Mailer

TL;DR

If you are using Symfony Mailer, Symfony Messenger and are sending e-mails with attachments asynchronously you might have come across some issues:

An email cannot be sent, because the (temporary) attachment files do not exist anymore.

An email failed to send, and the message ended up in a “failed” queue. After retrying to send the message, the attachments files do not exist anymore.

An email contains binary data, which is bloating the message transport. When attaching a file to an email using attach (instead of attachFromPath ), binary data is included in the messages itself.

Install fusonic/messenger-mailer-bundle to solve the issues above.

The Problem

If e-mails are important in your application then handling them asynchronously is essential. You do not want your e-mails to live in memory. If the system crashes, they will be gone!

Swiftmailer has been a popular option for sending e-mails for a long time. This has now changed since the introduction of Symfony Mailer. Swiftmailer had an option to save e-mails to the disk before sending. These files would also include attachments. Swiftmailer does not have the problem we are addressing here.

Symfony Mailer uses Symfony Messenger. Emails should get send over an asynchronous message transport. This is where problems arise if you are adding attachments to e-mails. If you attach files by path (using attachFromPath on the Email class), the attachments will not live in the same place as the email itself. To make sure that e-mails will contain the files, you will have to make sure that the files exist!

If you attach files (using attach on the Email class) directly, it will work! However, this will result in your message queue containing the complete messages with the binary data. This is something you might want to avoid. Depending on the transport, different problems could come up, for example:

The queue might get too large and will impact the performance.

Messages can become too big if you attach large files.

Concluding from the above problems: you want to keep binary data outside your message transport.

Solution

Our bundle abstracts the handing of managing attachments away.

A new class AttachmentEmail is introduced, which extends the built in Email class from Symfony Mailer. This class contains a new id property and has two new methods:

attachPersistedFromPath : Similar to attachFromPath , but files are copied and persisted until the actual e-mail has been sent.

: Similar to , but files are copied and persisted until the actual e-mail has been sent. attachPersisted : Similar to attach , but the content is persisted to a file until the actual e-mail has been sent.

By using the methods above you are making sure that attachments will be handled by the configured EmailAttachmentHandlerInterface . By default, a filesystem based implementation will be used. This implementation will store the files in a temporary directory, located in var/email-attachments/{email-id} . The handler will automatically create/copy files to the temporary directory and remove them after the e-mail has been sent. This works by utilizing the SendMessageToTransportsEvent and WorkerMessageHandledEvent Symfony Messenger events.

Getting started

Head over to fusonic/messenger-mailer-bundle for installation, requirements and usage instructions.