The Mime Component

The Mime Component

The Mime component allows manipulating the MIME messages used to send emails and provides utilities related to MIME types.

Installation

  1. $ composer require symfony/mime

Note

If you install this component outside of a Symfony application, you must require the vendor/autoload.php file in your code to enable the class autoloading mechanism provided by Composer. Read this article for more details.

Introduction

MIME (Multipurpose Internet Mail Extensions) is an Internet standard that extends the original basic format of emails to support features like:

  • Headers and text contents using non-ASCII characters;
  • Message bodies with multiple parts (e.g. HTML and plain text contents);
  • Non-text attachments: audio, video, images, PDF, etc.

The entire MIME standard is complex and huge, but Symfony abstracts all that complexity to provide two ways of creating MIME messages:

  • A high-level API based on the Symfony\Component\Mime\Email class to quickly create email messages with all the common features;
  • A low-level API based on the Symfony\Component\Mime\Message class to have an absolute control over every single part of the email message.

Usage

Use the Symfony\Component\Mime\Email class and their chainable methods to compose the entire email message:

  1. use Symfony\Component\Mime\Email;
  2. $email = (new Email())
  3. ->from('[email protected]')
  4. ->to('[email protected]')
  5. ->cc('[email protected]')
  6. ->bcc('[email protected]')
  7. ->replyTo('[email protected]')
  8. ->priority(Email::PRIORITY_HIGH)
  9. ->subject('Important Notification')
  10. ->text('Lorem ipsum...')
  11. ->html('<h1>Lorem ipsum</h1> <p>...</p>')
  12. ;

This only purpose of this component is to create the email messages. Use the Mailer component to actually send them.

Twig Integration

The Mime component comes with excellent integration with Twig, allowing you to create messages from Twig templates, embed images, inline CSS and more. Details on how to use those features can be found in the Mailer documentation: Twig: HTML & CSS.

But if you’re using the Mime component without the Symfony framework, you’ll need to handle a few setup details.

Twig Setup

To integrate with Twig, use the Symfony\Bridge\Twig\Mime\BodyRenderer class to render the template and update the email message contents with the results:

  1. // ...
  2. use Symfony\Bridge\Twig\Mime\BodyRenderer;
  3. use Twig\Environment;
  4. use Twig\Loader\FilesystemLoader;
  5. // when using the Mime component inside a full-stack Symfony application, you
  6. // don't need to do this Twig setup. You only have to inject the 'twig' service
  7. $loader = new FilesystemLoader(__DIR__.'/templates');
  8. $twig = new Environment($loader);
  9. $renderer = new BodyRenderer($twig);
  10. // this updates the $email object contents with the result of rendering
  11. // the template defined earlier with the given context
  12. $renderer->render($email);

Inlining CSS Styles (and other Extensions)

To use the inline_css filter, first install the Twig extension:

  1. $ composer require twig/cssinliner-extra

Now, enable the extension:

  1. // ...
  2. use Twig\Extra\CssInliner\CssInlinerExtension;
  3. $loader = new FilesystemLoader(__DIR__.'/templates');
  4. $twig = new Environment($loader);
  5. $twig->addExtension(new CssInlinerExtension());

The same process should be used for enabling other extensions, like the MarkdownExtension and InkyExtension.

Creating Raw Email Messages

This is useful for advanced applications that need absolute control over every email part. It’s not recommended for applications with regular email requirements because it adds complexity for no real gain.

Before continuing, it’s important to have a look at the low level structure of an email message. Consider a message which includes some content as both text and HTML, a single PNG image embedded in those contents and a PDF file attached to it. The MIME standard allows structuring this message in different ways, but the following tree is the one that works on most email clients:

  1. multipart/mixed
  2. ├── multipart/related
  3. ├── multipart/alternative
  4. ├── text/plain
  5. └── text/html
  6. └── image/png
  7. └── application/pdf

This is the purpose of each MIME message part:

  • multipart/alternative: used when two or more parts are alternatives of the same (or very similar) content. The preferred format must be added last.
  • multipart/mixed: used to send different content types in the same message, such as when attaching files.
  • multipart/related: used to indicate that each message part is a component of an aggregate whole. The most common usage is to display images embedded in the message contents.

When using the low-level Symfony\Component\Mime\Message class to create the email message, you must keep all the above in mind to define the different parts of the email by hand:

  1. use Symfony\Component\Mime\Header\Headers;
  2. use Symfony\Component\Mime\Message;
  3. use Symfony\Component\Mime\Part\Multipart\AlternativePart;
  4. use Symfony\Component\Mime\Part\TextPart;
  5. $headers = (new Headers())
  6. ->addMailboxListHeader('From', ['[email protected]'])
  7. ->addMailboxListHeader('To', ['[email protected]'])
  8. ->addTextHeader('Subject', 'Important Notification')
  9. ;
  10. $textContent = new TextPart('Lorem ipsum...');
  11. $htmlContent = new TextPart('<h1>Lorem ipsum</h1> <p>...</p>', null, 'html');
  12. $body = new AlternativePart($textContent, $htmlContent);
  13. $email = new Message($headers, $body);

Embedding images and attaching files is possible by creating the appropriate email multiparts:

  1. // ...
  2. use Symfony\Component\Mime\Part\DataPart;
  3. use Symfony\Component\Mime\Part\Multipart\MixedPart;
  4. use Symfony\Component\Mime\Part\Multipart\RelatedPart;
  5. // ...
  6. $embeddedImage = new DataPart(fopen('/path/to/images/logo.png', 'r'), null, 'image/png');
  7. $imageCid = $embeddedImage->getContentId();
  8. $attachedFile = new DataPart(fopen('/path/to/documents/terms-of-use.pdf', 'r'), null, 'application/pdf');
  9. $textContent = new TextPart('Lorem ipsum...');
  10. $htmlContent = new TextPart(sprintf(
  11. '<img src="cid:%s"/> <h1>Lorem ipsum</h1> <p>...</p>', $imageCid
  12. ), null, 'html');
  13. $bodyContent = new AlternativePart($textContent, $htmlContent);
  14. $body = new RelatedPart($bodyContent, $embeddedImage);
  15. $messageParts = new MixedPart($body, $attachedFile);
  16. $email = new Message($headers, $messageParts);

Serializing Email Messages

Email messages created with either the Email or Message classes can be serialized because they are simple data objects:

  1. $email = (new Email())
  2. ->from('[email protected]')
  3. // ...
  4. ;
  5. $serializedEmail = serialize($email);

A common use case is to store serialized email messages, include them in a message sent with the Messenger component and recreate them later when sending them. Use the Symfony\Component\Mime\RawMessage class to recreate email messages from their serialized contents:

  1. use Symfony\Component\Mime\RawMessage;
  2. // ...
  3. $serializedEmail = serialize($email);
  4. // later, recreate the original message to actually send it
  5. $message = new RawMessage(unserialize($serializedEmail));

MIME Types Utilities

Although MIME was designed mainly for creating emails, the content types (also known as MIME types and “media types”) defined by MIME standards are also of importance in communication protocols outside of email, such as HTTP. That’s why this component also provides utilities to work with MIME types.

The Symfony\Component\Mime\MimeTypes class transforms between MIME types and file name extensions:

  1. use Symfony\Component\Mime\MimeTypes;
  2. $mimeTypes = new MimeTypes();
  3. $exts = $mimeTypes->getExtensions('application/javascript');
  4. // $exts = ['js', 'jsm', 'mjs']
  5. $exts = $mimeTypes->getExtensions('image/jpeg');
  6. // $exts = ['jpeg', 'jpg', 'jpe']
  7. $types = $mimeTypes->getMimeTypes('js');
  8. // $types = ['application/javascript', 'application/x-javascript', 'text/javascript']
  9. $types = $mimeTypes->getMimeTypes('apk');
  10. // $types = ['application/vnd.android.package-archive']

These methods return arrays with one or more elements. The element position indicates its priority, so the first returned extension is the preferred one.

Guessing the MIME Type

Another useful utility allows to guess the MIME type of any given file:

  1. use Symfony\Component\Mime\MimeTypes;
  2. $mimeTypes = new MimeTypes();
  3. $mimeType = $mimeTypes->guessMimeType('/some/path/to/image.gif');
  4. // Guessing is not based on the file name, so $mimeType will be 'image/gif'
  5. // only if the given file is truly a GIF image

Guessing the MIME type is a time-consuming process that requires inspecting part of the file contents. Symfony applies multiple guessing mechanisms, one of them based on the PHP fileinfo extension. It’s recommended to install that extension to improve the guessing performance.

Adding a MIME Type Guesser

You can add your own MIME type guesser by creating a class that implements Symfony\Component\Mime\MimeTypeGuesserInterface:

  1. namespace App;
  2. use Symfony\Component\Mime\MimeTypeGuesserInterface;
  3. class SomeMimeTypeGuesser implements MimeTypeGuesserInterface
  4. {
  5. public function isGuesserSupported(): bool
  6. {
  7. // return true when the guesser is supported (might depend on the OS for instance)
  8. return true;
  9. }
  10. public function guessMimeType(string $path): ?string
  11. {
  12. // inspect the contents of the file stored in $path to guess its
  13. // type and return a valid MIME type ... or null if unknown
  14. return '...';
  15. }
  16. }

MIME type guessers must be registered as services and tagged with the mime.mime_type_guesser tag. If you’re using the default services.yaml configuration, this is already done for you, thanks to autoconfiguration.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.