Table of contents
- The email sending process
- The Mail System module
- Frequently used mail system classes
- Transactional and bulk emails
- What about Drupal 8?
Email sending in Drupal is actually pretty simple. Basically you only have to deal with two steps if you want to send an email to someone:
Define your email properties (like subject, body, headers, etc.) in an appropriate hook_mail() implementation.
Call drupal_mail() with your parameters for the actual email sending.
That’s all, however, in the case of a bigger or more complex site the two steps described above probably won’t be enough. Fortunately, you can customize the whole email sending process, but you need to know how things actually work behind the scenes first. In this article I’ll show you how you can customize and extend the Drupal mail system to fully meet your needs.
The email sending process
The drupal_mail() function uses mail system classes for email sending. Every mail system class needs to implement Drupal's [MailSystemInterface](https://api.drupal.org/api/drupal/includes%21mail.inc/interface/MailSys…) interface class to define its own mail sending behavior. The MailSystemInterface class has two methods which need to be implemented:
The first one is the format() method. As the name implies it takes care of email message formatting before actually sending the email.
The second method is the mail() method which defines the exact mail sending behavior.
To visualize how drupal_mail() actually works take a look at the following process diagram which tries to sum up the most important steps.
As you can see the mail system class plays a key role in the email sending process. By default drupal_mail() will use Drupal’s built in DefaultMailSystem class to send emails. We’ll talk about this class in more detail later, but first we get to know the Mail System module.
The Mail System module
The Mail System module provides “... an Administrative UI and Developers API for safely updating the mail_system configuration variable” as you can read on its overview page. Unfortunately Drupal 7 doesn’t give you an administrative UI to adjust the mail_system variable which actually defines “email key” => “mail system class” type key-value pairs which correspond to what mail system class will be used by drupal_mail() at a specific email key. This is the point where the Mail System module comes in. It allows you to adjust the mail_system variable by an administrative UI and it provides some other useful configuration options, too.
The Mail System module related settings can be found under the Configuration > System > Mail System menu path inside your Drupal installation. Let’s examine what configuration options the module offers.
Mail System Settings
As mentioned before, the mail_system variable defines “email key” => “mail system class” type key-value pairs (well this is not entirely true as you will see later) which correspond to what mail system class will be used by drupal_mail() at a specific email key. If you haven’t set up any mail system related settings before, the mail_system variable contains only two key-value pairs which are default-system => DefaultMailSystem and mailsystem_theme => current. These key-value pairs are the settings you can adjust under the “Mail System Settings” fieldset.
The default-system “email key” always exists and it determines which mail system class will be used by default for all outgoing emails. As you can see its default value is DefaultMailSystem so this is the reason why this mail system class will be used by drupal_mail() by default. The (site-wide) default-system key is actually an “abstract” email key for all emails, therefore it will match with all email keys used in drupal_mail() calls.
The mailsystem_theme key is a little bit different since it defines which theme will be used to render the emails. To be more specific let’s assume that there is a drupal mail system related module (eg. Mime Mail) which uses a template file to render emails. The mentioned template file obviously belongs to a hook_theme() entry to be able to use it at appropriate theme() calls. The Mail System module checks every theme registry entry for a specific “mail theme” key/property and if it exists in a particular entry then the specified mailsystem_theme value will be used to search for more specific template files when the theme registry entry related theme hook will be called.
Let’s assume that there are two or more different mail system classes available in your system provided by different modules. Each one has a format() and a mail() method. Now you would need a custom mail system class which would use the format() method from one mail system class and it would use the mail() method from another class. This is the point where the Mail System module’s “New Class” tool comes in, since it allows you to easily combine the behavior of two mail system classes by selecting their format() and mail() method for the new class.
After you saved your settings a new mail system class will be available which will use the previously selected mail system classes’ format() and mail() methods as its own methods.
For some scenarios it is enough to simply create a new mail system class instead of writing your own.
As we have seen, the *mail_system* variable’s *default-system* email key defines the site-wide mail system class which will be used for all emails. However there can be cases when one generic mail system class is not enough. In cases like these the Mail System module allows you to easily add new email keys to the *mail_system* variable by selecting a module and a specific email key from its hook_mail() implementation.
After you added a new email key to the mail_system variable you will be able to select a responsible mail system class for it as seen at the site-wide option. For example, if you have an example machine named module, which defines a custom_mail email key in its hook_mail() implementation, you can create a new example_custom_mail named email key in the mail_system variable and you can select a responsible mail system class, which will handle the email message(s) at this email key.
When drupal_mail() is called with the example_custom_mail email key, then the indirectly used drupal_mail_system() function checks if this email key exists in the mail_system variable. If so, the associated mail system class will be used instead of the default one.
Frequently used mail system classes
Before you start to write your own mail system class it is a good idea to study the most commonly used ones, because probably there are already some that will suit your needs and save you a lot of unnecessary work. Below you can read quick overviews of frequently used mail system classes and their most important features. The different modules usually give you several options to adjust the behavior of their classes’ format() and mail() methods, therefore it is always worth studying them before you start using them.
Drupal's built-in default mail system class. If you don’t modify the site-wide mail system class then this one will be used by drupal_mail() by default.
DefaultMailSystem‘s format() method enforces the emails’ output as plain text, therefore it doesn’t matter how your messages are formatted — the result will always be plain text.
The mail() method of the class sends the emails via PHP’s mail() function, so a correctly set up and working email sending service is required for it.
Provided by the Devel module, this mail system class is often used to debug emails on local development environments.
Since DevelMailLog extends the DefaultMailSystem class and it doesn’t override the format() method of its parent class, it uses the same format() method as DefaultMailSystem does.
The mail() method of the class writes each email message into a separate plain text file including email header properties, that’s why this class is useful for email debugging. All emails sent by DevelMailLog will end up in your site’s temporary folder, so you can easily check whether the email sendings were successful or not, without actually sending emails.
The MaillogMailSystem class is part of the Maillog / Mail Developer module and it basically does the same as the DevelMailLog class except that it saves the outgoing emails into the database instead of writing them into plain text files. The Maillog module also provides a nice administration UI to list sent emails and some additional useful configuration options to manage outgoing emails.
On the Maillog module’s configuration page you can select a mail system class which will be implicitly used by the MaillogMailSystem’s format() method to format the email messages before they will be sent to the recipient(s) or saved into the database.
The mail() method of the class can do multiple things depending on the module’s configuration settings. Like the format() method it also uses the selected mail system class’s mail() method for the email sending and it is also capable to save the email messages into the database and optionally display them by the Devel module’s dpm() function right after sending.
This class is provided by the HTML Mail module and is useful when you want to send HTML formatted email messages rather than plain text ones. It is also good to know that the HTML Mail module cooperates well with the Mail MIME and Echo modules.
The format() method of this mail system class uses a very useful template based formatting technique which means that you can easily specify template files for your email messages in your site’s theme directory. The point is that you don’t have to set up template processing for your emails because the HTML Mail module takes care of them.
HTMLMailSystem’s mail() method uses PHP’s mail() function for the email sending just like the DefaultMailSystem class, but before sending, it takes care of several HTML format related aspects and optionally cooperates with the Mail MIME module.
The MimeMailSystem class is part of the Mime Mail module which is similar to the HTML Mail module, but it also gives some specific configuration options and a great “mail engine” toolkit that allows you to implement so-called “mail engines” and related format functions for a more adjustable email sending process. Actually the “mail engine toolkit” is a further abstraction layer which gives you the opportunity to override the behavior of the MimeMailSystem’s format() and mail() methods if their default behaviors don’t meet your needs.
By default the MimeMailSystem’s format() method is similar to HTMLMailSystem’s: for HTML type emails it uses a similar template based formatting, that cooperates with the module’s configuration options. You can override the format() method’s default behavior if it doesn’t meet your needs.
Like the format() method the mail() method also has a default behavior which uses the PHP’s mail() function for the email sending, but similarly you can override it as well.
This mail system class is part of the Mandrill module which provides integration with Mandrill transactional emails. As per Madrill, it is “… a scalable and affordable email infrastructure service, with all the marketing-friendly analytics tools you’ve come to expect from MailChimp.” So Mandrill is MailChimp’s transactional email service.
The MandrillMailSystem’s format() method implodes the email message’s body if it was given as an array.
The mail() method is much more interesting because it is using the Mandrill API to deliver the emails with Mandrill rather than trying to send them directly from your server like we saw at previously mentioned mail system classes. You don’t need a server capable of sending emails since Mandrill will do the job. The mail() method also filters the messages according to an input format related setting, therefore you can use any input format you like (Full HTML, Filtered HTML, Plain text, etc..) not just plain text.
Transactional and bulk emails
In email related terminology we usually make distinction between transactional and bulk emails.
Transactional emails are triggered by some kind of transaction/user activity, for example when someone registers to a site, confirms an order in a webshop, or signs up for a newsletter.
Bulk emails are emails sent in large quantities at once or with some kind of scheduling. For instance we are talking about bulk emails when a site sends newsletters to its registered users or a one-time notification about an important event.
The line between transactional and bulk emails can get blurry, since transactional events can have several recipients and bulk email sending can be triggered by transactional events as well.
Transactional email sending is commonly used with drupal_mail(). As you saw earlier there are many great mail system classes starting from Drupal’s built in default solution to the most complex ones shipped by Mime Mail or Mandrill. Let’s talk a little bit more about Mandrill, MailChimp’s great transactional email sending service. Just to name a few features, it gives you:
- Globally distributed infrastructure (for faster email sending)
- Advanced analytics tools and email tracking (like open rates, clicks and unsubscribers)
- Email template management with dynamic content integration
- Test environment to test your emails without actually sending them
- Customizable email sending rules and options
- Free account to send up to 12k emails per month
What is even more important for us is that there is a Mandrill module for Drupal, which lets you use many Mandrill features through the Mandrill API in your Drupal environment. You don’t need a server for email sending, Mandrill handles it: The email sending process will be split into two main parts. First your server will send the constructed email messages to Mandrill via the Mandrill API and then Mandrill will send the emails using its own infrastructure.
If you don’t want to use Mandrill you can use Swift Mailer or PHPMailer which both support external SMTP servers and allow you to send mixed (HTML and text) emails, attachments, etc. There are available Drupal modules for Swift Mailer and PHPMailer, too.
Sending hundreds or thousands of emails “at once” requires much attention from multiple aspects.
As we saw drupal_mail() goes through several steps before the actual email sending, which could require quite a lot of time and resources at multiple calls. Furthermore if we take a look at PHP’s mail() function (which is widely used in the different mail system classes) we will see that it wasn’t designed for mass email sending since it opens and closes a new SMTP socket for each email. This is not very efficient as you can read on its documentation page. The situation is a bit better if all emails contain the same content because then it is not necessary to render each email differently (user specifically) with different parameters, therefore a simple drupal_mail() call could be enough. However in such cases our script can still run too long or reach the maximum execution time limit (max_execution_time) which can interrupt a long running mail() call if we have too many recipients.
So performance and speed are especially important here. Basically there are two approaches to solve the problem: we can try to implement our own solution or we can use an external service.
Do it yourself
If you want to handle bulk emails yourself, or don’t want to use an external service an obvious idea could be to use [cron queues](https://api.drupal.org/api/drupal/modules!system!system.queue.inc/group…) for email sendings, so you can populate large queues and then simply process a few items from them from time to time until they are emptied. Actually you could imitate a similar and maybe slightly faster behavior without the queue API, but since it makes our life easier it is worth using it.
If you have to send lots of emails per request then the Queue Mail module could be useful, which can queue the outgoing emails for later processing instead of sending them immediately. Queue Mail also works well with any kind of mail system class which makes it more flexible, however it uses hook_mail_alter() to achieve the latter behavior which is OK, but can be done faster and more elegantly if you write a specific mail system class for your needs. Let’s assume you want to use the HTML Mail module but you also would like to queue your emails for later processing. In such cases you can write something like this:
Then, in our queue worker callback we just need to send the messages:
So queues are useful, however, the queue processing speed depends on the cron scheduling and on the quantity of the emails you have to send. Just a quick calculation: let’s assume you have to send 60.000 emails and you can process approximately 5.000 queue items in a cron run. If your cron runs in every half an hour, it will take more than 6 hours to deliver all emails which is quite a lot of time, so it is good to keep these things in mind before you schedule your cron task. To speed things up, you can process queues in parallel for example with Ultimate Cron.
Outsourcing the task
If you have a large number of recipients or have to send a lot of emails as quickly as possible, it will be probably better to use an external service like [MailChimp](http://mailchimp.com/). MailChimp is a popular service for bulk emails, but what makes it even better is its great Drupal [module](https://www.drupal.org/project/mailchimp) including many features for a better Drupal integration. Like Mandrill, MailChimp also supports free accounts to send emails up to 12k per month with subscribers up to 2k, so you can easily try the features whenever you want for free.
Using MailChimp is a little bit different from what you would expect. Terminology lists play the key role containing the recipient email addresses with some additional information and optional user specific parameters which can be used dynamically in email templates. Email templates are another important part of the system. You can create reusable templates from scratch or from predefined layouts and even from complete themes. If you have lists and templates, you can create campaigns corresponding to events. To visualize the above mentioned system components and the connections among them, take a look at the following diagram:
So if you are using MailChimp the main task will be to subscribe users to MailChimp lists, and start or schedule campaigns based on your previously populated lists. Lists and templates are reusable, so you can create multiple campaigns for the same list or you can send emails just for new subscribers and so on.
On the Drupal side the MailChimp module comes with a few submodules. The core module provides basic configuration options and API integration while the submodules provide other useful features like list and campaign management, signup forms, custom fields, etc. The point is that you can use many MailChimp features through the MailChimp API, but due to its limitations there are some options that are only available via MailChimp’s web interface.
What about Drupal 8?
As you probably know Drupal 8 will bring a lot of changes. Fortunately the logic behind the mail system will be the same as you experienced at Drupal 7, although system components will be named differently and everything will be a little bit more object oriented. So fortunately you won’t have to learn everything from scratch.
First of all, drupal_mail()‘s features have been moved into the MailManager class, therefore you will have to use MailManager’s mail() method to send emails. Instead of mail system classes MailManager uses mail interface classes, which actually correspond to Drupal 7’s mail system classes, even their structure is the same. So just remember Drupal 7’s MailSystemInterface interface class has been replaced with the MailInterface interface class. As we experienced with drupal_mail(), when we call MailManager’s mail() method, it will “find” the appropriate mail interface class responsible for the email formatting and sending. In Drupal 8, instead of DefaultMailSystem the default mail interface class is called PhpMail (which gives a better representation of what this mail interface class actually does). There are no significant differences between PhpMail and DefaultMailSystem so basically only the naming have been changed here, too.
Since Drupal 8 is still under development it is possible that some of the above will change until the final release. In addition, probably not all Drupal 7 contrib modules related to the mail system will be available in the first round, but as you can see there is no need to throw out your Drupal 7 knowledge.
Email sending is an especially important task that affects most of your projects, therefore it is always good to know how to get the most out of the system. I hope this overview of the options you have for customizing and extending Drupal’s mail system helps you choose the best solution for your needs.