Send Emails with Swift Mailer from PHP Apps

On November 12, 2019
7min read
Dmitriy Shcherbakan Full Stack Developer @Railsware

Swift Mailer can be used with any PHP framework, and then easily integrated with an external SMTP like Gmail as well as popular email providers like Mandrill, SendGrid, or Mailgun. In this post, we will go step-by-step and review Swift Mailer’s functionality, build an HTML email, embed images, attach files, test the message with Mailtrap Email Sandbox, and then send it. 

What is Swift Mailer?

Swift Mailer is a component-based mailing library, which is compatible with PHP web apps. It was created by SensioLabs, a French company famous for its open-source software community commitment and in particular, for the creation of the Symfony framework.

Before June 2019, Swift Mailer was used as the main mailing module in Symfony and was mostly associated with it. What did happen in June 2019? The Mailer component was announced (along with the Mime component) as a standalone mailing module for Symfony (available for version 4.3 and higher). 

You might find worrying posts over the internet asking “What is going to happen with Swift Mailer then?”. We believe that it will be further supported and maintained. Swift Mailer is used as the main mail option in frameworks like Yii2 and CMS like Drupal, Laravael’s email API is built on top of the Swift Mailer library as well.  

Update 2021: it looks like we were wrong – Swift Mailer is not supported since November 2021. But the demand for its is there, so keep reading if you use it. Otherwise, read more about sending emails with Symfony Mailer, the next evolution of Swift Mailer.

First email configurations 

Let’s start with the installation. It’s recommended to make it via Composer: 

$ composer require "swiftmailer/swiftmailer:^6.0"

Then let’s specify transport and mailer. We always run all the experiments in the pre-production environment. So, we will start by testing our emails with Mailtrap Email Sandbox, a comprehensive tool that lets you run email tests from staging in a safe environment by capturing outgoing SMTP traffic. 

To create a transport, we need the:

  • Hostname
  • Port
  • Username
  • Password

You will find them in the SMTP settings tab of your inbox (if you already have several, choose the one where you want this message to be delivered):

$transport = (new Swift_SmtpTransport('sandbox.smtp.mailtrap.io', 2525))
  ->setUsername('1a2b3c4d5e6f7g') // generated by Mailtrap
  ->setPassword('1a2b3c4d5e6f7g') // generated by Mailtrap
;
$mailer = new Swift_Mailer($transport);

How to create an email in Swift Mailer

In Swift Mailer, messages are composed with the help of Swift_Message class. Its instances have a lot of methods to manage HTML, attachments, media – all the additional attributes to compose a good looking and informative email. 

Let’s start by creating a message from top to bottom, likely to an email client: set recipient, sender,  and a subject. Usually, we will make it with setSubject(), setTo(), and setFrom() methods. To include several recipients, use an array and to add recipients in copy, use setCc() or setBcc(). Also, you can set name headers with associative arrays:

// Create the message
$message = (new Swift_Message())
  // Add subject
  ->setSubject('Here should be a subject')
  //Put the From address
->setFrom(['support@example.com'])
  // Include several To addresses
 ->setTo(['newuser@example.com' => 'New Mailtrap user'])
->setCc([
'support@example.com',
‘product@example.com’ => ‘Product manager’
]);

For more details and alternatives, refer to the corresponding section in the Swift Mailer documentation

Adding HTML content

In Swift Mailer, you need to define which type of content you are going to add to your message body. It can be text/html, text/plain, or both. 

Tip: wherever possible, include a plain text version for the HTML content. If for some reason the HTML part won’t be rendered, your recipient will still be able to understand the content of your message.

For this purpose, use setBody() method and addPart() for the alternative. For example:

$message->setBody(<p>'Welcome to Maitrap!</p>
Now your test emails will be <i>safe</i>', 'text/html');
$message->addPart('Welcome to Mailtrap, now your test emails will be safe', 'text/plain');

How to embed images

Even if you are sending transactional emails, most probably you will need to include at least your company’s logo. Swift Mailer lets you embed your image directly, without any additional manipulations. Use Swift_EmbeddedFile::fromPath(). method for this purpose. It is very similar to inserting an image to the body of your message in many email clients. So, you should have an image file saved locally on your computer or you can even add an image hosted on some website. For the latter, allow_url_fopen should be enabled in your PHP installation (read more about it in the PHP manual). 

// Create the message, you can add the subject right here if you like
$message = new Swift_Message('The subject should be here');
// Set the body
$message->setBody(
'<html>' .
' <body>' .
'Here is our new logo <img src="’ .
     $message->embed(Swift_Image::fromPath('newlogo.png')) .
   ‘" alt="new logo" />' .
' Let us know how you like it'.
' </body>' .
'</html>',
  'text/html' // don’t forget to mark the content type
);

Embedding an image from a URL looks similar:

$message->setBody(
'<html>' .
' <body>' .
'  Here is our new logo <img src="' .
     $message->embed(Swift_Image::fromPath('/blog/newlogo.png')) .
   '" alt="Image" />' .
'  Let us know how you like it' .
' </body>' .
'</html>',
  'text/html'
);

Alternatively, you can embed the image as a CID attachment:

$cid = $message->embed(Swift_Image::fromPath('newlogo.png'));
$message->setBody(
'<html>' .
' <body>' .
'  Here is our new logo <img src="' . $cid . '" alt="Image" />' .
'  Let us know how you like it' .
' </body>' .
'</html>',
  'text/html'
);

Here is another nice option – Swift_Image class. As per Swift Mailer documentation, “Swift_Image and Swift_EmbeddedFile are just aliases of one another. Swift_Image exists for semantic purposes.”

Anyway, it allows embedding dynamic content, for instance, images created via PHP GD.

// Create your file contents
$img_data = create_my_image_data();
// Create the message
$message = new Swift_Message('Dynamic Picture');
// Set the body
$message->setBody(
'<html>' .
' <body>' .
'  Here is a new picture <img src="' . // Embed the file
     $message->embed(new Swift_Image($img_data, 'image.jpg', 'image/jpeg')) .
   '" alt="Image" />' .
'  Anything else?' .
' </body>' .
'</html>',
  'text/html'
);

How to include attachments

Attaching files works almost the same as embedding images. You also can attach files saved locally, from URLs, or add dynamic content. The difference is that you use Swift_Attachment class here and Swift_Message's attach() method. 

Tip: The recipient will download the file with the same name you’ve specified. Double-check it when adding an attachment! To rename the file right in the script, use ->setFilename() method. 

// For the regular file types like docs, spreadsheets, or images, you can go without the content type
$message->attach(Swift_Attachment::fromPath('/path/to/image.jpg'));
// attach a file from a URL
$message->attach(Swift_Attachment::fromPath('/blog/newlogo.jpg'));
// Create the attachment and rename it
$message->attach(
  Swift_Attachment::fromPath('/path/to/newlogo.jpg')->setFilename('logofinal.jpg')
);
// now let’s add a pdf file created via GD and set its content type
$data = create_confirmation_pdf_data();
$attachment = new Swift_Attachment($data, 'confirmation.pdf', 'application/pdf');
$message->attach($attachment);

Sending a message with Swift Mailer

Above we have defined the subject of our message, its recipients, and the content. Looks like the email is ready to be sent. Let’s test if everything works as intended with Mailtrap Email Sandbox first. We have already created the transport and specified Mailtrap credentials, so we simply use the Mailer class to send the message:

   $mailer->send($message);

Let’s compose the whole message and send it. In this example we use both HTML and plain text parts, embed an image, and attach a PDF file:

<?php
require_once '/path/to/vendor/autoload.php';
 try {
$transport = (new Swift_SmtpTransport('sandbox.smtp.mailtrap.io', 2525))
        ->setUsername('1a2b3c4d5e6f7g')
        ->setPassword('1a2b3c4d5e6f7g');
    $mailer = new Swift_Mailer($transport);
public function index($name, \Swift_Mailer $mailer)
{
 $message = (new Swift_Message())
 ->setSubject('Here should be a subject')
->setFrom(['support@example.com'])
 ->setTo(['newuser@example.com' => 'New Mailtrap user'])
->setCc([
'product@example.com' => 'Product manager'
]);
$message->setBody(
'<html>' .
' <body>' .
'  <img src="' .
     $message->embed(Swift_Image::fromPath('image.png')) .
   '" alt="Image" />' .
'  <p>Welcome to Mailtrap!</p>’.
‘Now your test emails will be <i>safe</i>’ .
' </body>' .
'</html>',
  'text/html'
);
$message->addPart('Welcome to Mailtrap, now your test emails will be safe', 'text/plain');
$message->attach(Swift_Attachment::fromPath('/path/to/confirmation.pdf'));
$mailer->send($message);

This is how the trapped email will appear:

With the help of the Sandbox, you can inspect your email content, headers and Bcc, and other related tech info. Additionally, you can validate your HTML/CSS and get a spam analysis that will help you understand what points need to be improved for better deliverability.  

Once we have verified that the result meets our expectations, we can move on to production options.

Inspect Your Emails

Sending a message via SMTP server

To send your Swift message via any external SMTP server, you need to create an appropriate transport, the same as we did for Mailtrap Email Sandbox.

See below for several examples of integration with the most popular email-sending services.

Swift Mailer + Mailtrap Email API 

Besides an Email Sandbox, Mailtrap offers Email API or SMTP Relay that lets you add email-sending functionality to your application and have more control over your email deliverability.

If you want to use Mailtrap Email API for your PHP app, after verifying your domain name, go to the Sending Domains section, select API and SMTP tab. From the drop-down menu, select PHP and copy paste the below code with your credentials in it. 

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://send.api.mailtrap.io/api/send', 
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS =>'{"from":{"email":"mailtrap@mailtrap.club","name":"Mailtrap Test"},"to":[{"email":"viktoriia.ivanenko@railsware.com"}],"subject":"You are awesome!","text":"Congrats for sending test email with Mailtrap!","category":"Integration Test"}',
    CURLOPT_HTTPHEADER => array(
        'Authorization: Bearer <your api token>,
        'Content-Type: application/json'
    ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

To start sending messages via Swift Mailer using SMTP Relay:

$transport = (new Swift_SmtpTransport('live.smtp.mailtrap.io', 587))
        ->setUsername('apikey')
        ->setPassword('apikey-password');

Swift Mailer + Sendgrid

To be able to send messages via Sendgrid, you will need to create its API Key and use it as a username:

$transport = (new Swift_SmtpTransport('smtp.sendgrid.net', 587))
        ->setUsername('apikey')
        ->setPassword('apikey-password');

Link: Integrating with the SMTP API, Sendgrid documentation

Swift Mailer + Mandrill

Get the ‘MANDRILL_USERNAME’ and ‘MANDRILL_PASSWORD’ at your Mandrill account settings.

$transport = (new Swift_SmtpTransport('smtp.mandrillapp.com', 587));
->setUsername('MANDRILL_USERNAME');
->setPassword('MANDRILL_PASSWORD');

Link: How to Send via SMTP with Popular Programming Languages, Mandrill Knowledge Base

Swift Mailer + Mailgun 

Go to your Mailgun profile and copy-paste your default SMTP login and default password this way:

$transport = (new Swift_SmtpTransport('smtp.mailgun.org', 25));
  ->setUsername('your SMTP username')
  ->setPassword('your SMTP password')

Swift Mailer + Gmail

$transport = (new Swift_SmtpTransport('smtp.gmail.com ', 465));
  ->setUsername('your username')
  ->setPassword('your password')

Here you put your Gmail account username and password. But if the 2-Step-Verification is set for this account, you need to use the App password and let less secure apps access your account. 

Swift Mailer is one of the most popular PHP libraries so it will be easy to find examples of integration with various services.

Article by Dmitriy Shcherbakan Full Stack Developer @Railsware