When it comes to adding email functionality to your PHP app, PHPMailer class is the winning option. It is compatible with most of the PHP frameworks (Laravel or Symfony are based on the SwiftMailer library, though, but it is still possible to use PHPMailer as well.) PHPMailer provides powerful functionality to create HTML emails with attachments and send them to multiple recipients via SMTP or a local webserver. Learn how to easily use all these options in this tutorial!
What is PHPMailer
PHPMailer is the classic email sending library for PHP. It supports several ways of sending email messages such as mail(), Sendmail, qmail, and direct dispatch to SMTP servers. In addition, it provides a list of advanced features:
- SMTP authentication
- secure/MIME encryption
- support of TLS and SSL protocols
- HTML content along with plain text
- multiple fs, string, and binary attachments
- embedded images support
Sending Email With PHPMailer and SMTP
To send emails with PHPMailer and SMTP, you need to install PHPMailer and configure SMTP settings first.
How to install PHPMailer
Up to version 5, PHPMailer was providing “PHPMailerAutoload.php” file, so all that was needed was to include it in your script and create a PHPMailer instance. Starting from PHPMailer 6.0 release in August 2017, you need to install it, preferably via Composer, a dependency manager for PHP (this way is recommended by PHPMailer’s creators on Github). After installing Composer, add this line to your composer.json file:
"phpmailer/phpmailer": "~6.1"
or run
composer require phpmailer/phpmailer
If you don’t want to install Composer, for example, while working within a testing environment, you can add PHPMailer manually. Download files with PHPMailer source code, then copy the contents of the PHPMailer folder to one of the include_path directories specified in your PHP configuration, and load each class file manually:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'path/to/PHPMailer/src/Exception.php';
require 'path/to/PHPMailer/src/PHPMailer.php';
require 'path/to/PHPMailer/src/SMTP.php';
Adding Exception class will help you handle errors and debug them. In PHP it works similarly to the other programming languages. So, without it, if there is an error in your email sending code, you will just see a message saying Exception class is not found, but you won’t be provided with any details on how to debug it. We will describe debugging is a separate section of this post.
To see detailed instructions on installation, check PHPMailer documentation on Github.
SMTP configuration
How to send test messages with PHPMailer?
To test PHP mail functionality, we will use Mailtrap, a fake SMTP server, so as not to flood our inboxes or even worse, the inboxes of our customers. Once you make sure that everything works properly and your email messages look right, you will be able to easily substitute the SMTP settings in our examples with your real server’s.
If you are not using Mailtrap yet, create a free account , go to your Inbox, and copy the following values from the SMTP settings tab to your PHPMailer script:
- server host ($mail->Host = ’smtp.mailtrap.io’)
- username ($mail->Username = ‘1a2b3c4d5e6f7g’ (example, generated by Mailtrap)
- password ($mail->Password = ‘1a2b3c4d5e6f7g’ (example, generated by Mailtrap)
- and port ($mail->Port = 25, or 465, or 2525).
There is one more point left to mention about SMTP: POP-before-SMTP for authentication. This method is almost fully replaced by the SMTP authentication but still can be used sometimes. Its main advantage is the transparency for the user who is sending an email. Check this Github page to get the example of using POP-before-SMTP with PHPMailer.
Sending HTML email via SMTP with PHPMailer
Most likely, you will use HTML to design your email notification. So, let’s review some examples of using HTML methods and attributes.
Basic HTML message
If you have used Composer for installation, include the Composer generated autoload.php file:
require 'path/to/composer/vendor/autoload.php';
If you have installed PHPMailer manually, initialize it like this:
require path/to/PHPMailer/src/PHPMailer.php';
require path/to/PHPMailer/src/SMTP.php';
require path/to/PHPMailer/src/Exception.php';
Then create the PHPMailer class:
<?php
use PHPMailer\PHPMailer\PHPMailer;
The next step is to create a new PHPMailer object:
$mail = new PHPMailer();
Now let’s move to an SMTP configuration:
$mail->isSMTP();
$mail->Host = 'smtp.mailtrap.io';
$mail->SMTPAuth = true;
$mail->Username = '1a2b3c4d5e6f7g'; //paste one generated by Mailtrap
$mail->Password = '1a2b3c4d5e6f7g’ //paste one generated by Mailtrap
$mail->SMTPSecure = 'tls';
$mail->Port = 2525;
Specify PHPMailer headers:
$mail->setFrom('info@mailtrap.io', 'Mailtrap');
$mail->addReplyTo('info@mailtrap.io', 'Mailtrap');
$mail->addAddress('recipient1@mailtrap.io', 'Tim');
$mail->addCC('cc1@example.com', 'Elena');
$mail->addBCC('bcc1@example.com', 'Alex');
If you need to add several addresses, specify each of them as a new command:
$mail->AddBCC('bcc2@example.com', 'Anna');
$mail->AddBCC('bcc3@example.com', 'Mark');
With PHPMailer, you can loop your message and send it to multiple recipients. In particular, you can read a list from the table. We will review such examples a bit later in this post, and now let’s get back to our basic message.
Set a subject:
$mail->Subject = 'Test Email via Mailtrap SMTP using PHPMailer';
Then set the email format to HTML with isHTML(true) property:
$mail->isHTML(true);
Now you can input the desired content:
$mailContent = "<h1>Send HTML Email using SMTP in PHP</h1>
<p>This is a test email I’m sending using SMTP mail server with PHPMailer.</p>";
$mail->Body = $mailContent;
With PHPMailer, you can also make a nice HTML email, with custom formatting, images, and send emails with attachments. For your convenience, PHPMailer provides an option to read an HTML message body from an external file, convert referenced images to embedded as well as convert HTML into a basic plain-text alternative body. This way, you will not overload your message sending code with HTML and will be able to update your HTML template independently. To include a separate HTML file, add these attributes:
$mail->msgHTML(file_get_contents('contents.html'), __DIR__);
In the end, specify the email sending attributes:
if($mail->send()){
echo 'Message has been sent';
}else{
echo 'Message could not be sent.';
echo 'Mailer Error: ' . $mail->ErrorInfo;
}
The result “Message has been sent” informs you that your code executes correctly. To check the delivery result and details, go to your Mailtrap inbox: your messages will get there in seconds.
With Mailtrap, you will be able to review your HTML code, raw data, perform email browser testing as well as make sure your message won’t be marked as spam, or your sending domain won’t go blacklisted.
Capture all test emails and inspect them all in one place.
HTML email with attachments
There are two options for adding files to your message:
- Enclose a file from your filesystem – like you can make it in the email client. In this case, your files should be saved in the same directory as the script.
- Also, a file can be inlined into your message, this is valid for images.
- Add a string attachment – in this case, the data stored in a variable is attached to the message. It can be extracted from a database, from example, so you don’t need to save it as a file, actually.
To attach a file, you just need to specify its path. Also, you can add a filename but it’s optional: the script will use the actual name of your file:
$mail->addAttachment('path/to/invoice1.pdf', 'invoice1.pdf');
To add another file, repeat the command:
$mail->addAttachment('path/to/calculation1.xlsx', 'calculation1.xlsx');
To add an attachment from the string, use addStringAttachment() command. You should pass the content and the filename:
$mysql_data = $mysql_row['blob_data'];
$mail->addStringAttachment($mysql_data, 'db_data.db');
This is an example of adding data stored as a BLOB (Binary Large OBject) from MySQL database.
Alternatively, you can use a remote URL, like below:
$mail->addStringAttachment(file_get_contents($url), 'myfile.pdf');
To embed an image, we use CID attachments here:
$mail->addEmbeddedImage('path/to/image_file.jpg', 'image_cid');
$mail->isHTML(true);
$mail->Body = '<img src="cid:image_cid">';
Finally, let’s review an example of a whole message with an inlined image and internal HTML body:
<?php
use PHPMailer\PHPMailer\PHPMailer;
require 'path/to/composer/vendor/autoload.php';
$mail->isSMTP();
$mail->Host = 'smtp.mailtrap.io';
$mail->SMTPAuth = true;
$mail->Username = 'paste one generated by Mailtrap';
$mail->Password = 'paste one generated by Mailtrap’
$mail->SMTPSecure = 'tls';
$mail->Port = 2525;
$mail->setFrom('from@example.com', 'First Last');
$mail->addReplyTo('towho@example.com', 'John Doe';
$mail->isHTML(true);
$mail->Subject = "PHPMailer SMTP test";
$mail->addEmbeddedImage('path/to/image_file.jpg', 'image_cid');
$mail->Body = '<img src="cid:image_cid"> Mail body in HTML';
$mail->AltBody = 'This is the plain text version of the email content';
if(!$mail->send()){
echo 'Message could not be sent.';
echo 'Mailer Error: ' . $mail->ErrorInfo;
}else{
echo 'Message has been sent';
The result “Message has been sent” informs you that your code is executing correctly. To check the delivery result and details, go to your Mailtrap inbox: your messages will get there in seconds.
With Mailtrap, you will be able to review your HTML code, raw data, perform email browser testing as well as make sure your message won’t be marked as spam, or your sending domain won’t go blacklisted.
Once you have verified that your PHPMailer sending result matches your expectations, you can switch to regular email sending via any preferred SMTP server.
Sending emails to multiple recipients
Above we have examined how to send one and the same email to several recipients, including CCing and BCCing. What if you need to send a modified message to a list of recipients, which is often required for transactional emails? PHPMailer offers features for such use cases as well!
<?php
use PHPMailer\PHPMailer\PHPMailer;
require 'path/to/composer/vendor/autoload.php';
$mail = new PHPMailer;
$body = file_get_contents('contents.html');
$mail->isSMTP();
$mail->Host = 'smtp.mailtrap.io';
$mail->SMTPAuth = true;
$mail->Username = '1a2b3c4d5e6f7g'; //generated by Mailtrap
$mail->Password = '1a2b3c4d5e6f7g'; //generated by Mailtrap
$mail->SMTPSecure = 'tls';
$mail->Port = 2525;
$mail->SMTPKeepAlive = true; // add it to keep SMTP connection open after each email sent
$mail->setFrom('list@example.com', 'List manager');
$mail->Subject = "New Mailtrap mailing list";
$users = [
['email' => 'max@example.com', 'name' => 'Max'],
['email' => 'box@example.com', 'name' => 'Bob']
];
foreach ($users as $user) {
$mail->addAddress($user['email'], $user['name']);
$mail->Body = "<h2>Hello, {$user['name']}!</h2> <p>How are you?</p>";
$mail->AltBody = "Hello, {$user['name']}! \n How are you?";
try {
$mail->send();
echo "Message sent to: ({$user['email']}) {$mail->ErrorInfo}\n";
} catch (Exception $e) {
echo "Mailer Error ({$user['email']}) {$mail->ErrorInfo}\n";
}
$mail->clearAddresses();
}
$mail->smtpClose();
Test emails like this in a safe testing environment.
How to use PHPMailer with Gmail
Gmail is one of the most popular SMTP servers, which allows you to send emails for free. However, it does require some tinkering with authentication settings.
Encryption
Here are the basic Gmail SMTP credentials:
$mail->SMTPSecure = 'tls';
$mail->Host = 'smtp.gmail.com';
$mail->Port = 587;
or
$mail->SMTPSecure = 'ssl';
$mail->Host = 'smtp.gmail.com';
$mail->Port = 465;
If you don’t use the 2-step verification for your account, use your Gmail address and password to your Gmail account.
You should Allow less secure apps in your account settings. Please note that it may take an hour or two for this setting to come into effect. In some cases, you might additionally need to make necessary changes on the unlock CAPTCHA page.
If something goes wrong, you will receive an error “5.7.14 Please log in via your web browser and then try again” or “Username and Password not accepted”. It should contain instructions on how to fix it.
2-Step Verification
If 2-step verification is enabled for your Gmail account and you wish to use it with PHPMailer as well, you will need to create an App password to continue. It is a 16-digit passcode, which you should put as a value to $mail->Password. It is easy to generate one following these Google instructions.
XOAuth2
OAuth2 is the most complicated but still the recommended method. Fortunately, there is oauth2-google. It is a package to support Google OAuth 2.0 for the PHP League’s OAuth 2.0 Client. It supports PHP 7.0 – PHP 7.3. To use it, you need to get a Google client ID and client secret first. Follow this Google’s guide to set everything up. Once completed, install the package with composer and add the authorization details as below:
composer require league/oauth2-google
use League\OAuth2\Client\Provider\Google;
$provider = new Google([
'clientId' => '{google-client-id}',
'clientSecret' => '{google-client-secret}',
'redirectUri' => 'https://example.com/callback-url',
]);
For more details, follow the oauth2-google on GitHub.
Send an email with PHPMailer without SMTP server
An SMTP transport option is the most popular and usable with PHPMailer. However, as we’ve already mentioned, it supports other ways as well.
For localhost, you can use the standard Sendmail program:
// Set mailer with this method
$mailer->isSendmail();
// And send mail as usual
$mailer->send();
If you prefer Unix, then use qmail:
// Set mailer with this method
$mailer->isQmail();
// And send mail as usual
$mailer->send();
Debugging
If you experience some troubles when sending emails through an SMTP server, the SMTPDebug command will help you explore those errors and find out what should be fixed.
Enable SMTP debugging and set the debug level in your script as follows:
$mail->SMTPDebug = 2;
- level 1 = client; will show you messages sent by the client
- level 2 = client and server; will add server messages, it’s the recommended setting.
- level 3 = client, server, and connection; will add information about the initial information, might be useful for discovering STARTTLS failures
- level 4 = low-level information.
Use level 3 or level 4 if you are not able to connect at all. Setting level 0 will turn the debugging off, it is usually used for production.
For a better understanding of how debugging in PHPMailer works, let’s review a couple of examples.
Example1. Invalid SMTP hostname
2018-12-12 14:51:32 Connection: opening to mailtrap.io:2525, timeout=10, options=array()
2018-12-12 14:51:42 Connection failed. Error #2: stream_socket_client(): unable to connect to mailtrap.io:2525 (Operation timed out) [/Users/xxxx/Downloads/PHPMailer/src/SMTP.php line 326]
2018-12-12 14:51:42 SMTP ERROR: Failed to connect to server: Operation timed out (60)
2018-12-12 14:51:42 SMTP connect() failed.
Mailer Error: SMTP connect() failed.
For this example, we entered an invalid hostname: mailtrap.io instead of smtp.mailtrap.io. and instantly received a message that an error has occurred at the very first stage of communication with the server.
Example 2. Invalid credentials
2018-12-12 14:49:26 Connection: opening to smtp.mailtrap.io:2525, timeout=300, options=array()
2018-12-12 14:49:26 Connection: opened
2018-12-12 14:49:26 SMTP INBOUND: "220 mailtrap.io ESMTP ready"
2018-12-12 14:49:26 SERVER -> CLIENT: 220 mailtrap.io ESMTP ready
...
2018-12-12 14:49:30 SMTP INBOUND: "535 5.7.0 Invalid login or password"
2018-12-12 14:49:30 SERVER -> CLIENT: 535 5.7.0 Invalid login or password
2018-12-12 14:49:30 SMTP ERROR: Username command failed: 535 5.7.0 Invalid login or password
2018-12-12 14:49:30 SMTP Error: Could not authenticate.
2018-12-12 14:49:30 CLIENT -> SERVER: QUIT
2018-12-12 14:49:30 SMTP INBOUND: "221 2.0.0 Bye"
2018-12-12 14:49:30 SERVER -> CLIENT: 221 2.0.0 Bye
2018-12-12 14:49:30 Connection: closed
2018-12-12 14:49:30 SMTP connect() failed.
Mailer Error: SMTP connect() failed.
This example demonstrates where the error occurs: now the SMTP, its hostname, and port are valid but a combination of login and password was not found, so you should double-check and modify your credentials.
There are a couple of detailed articles on GitHub about debugging and troubleshooting, refer to them when you need to dive deeper into these topics.
What else you should know about PHPMailer
In this tutorial we have reviewed the most common PHPMailer use cases: creating HTML emails with images and attachments, sending them via SMTP or localhost to different types of recipients along with debugging options.
If you need more specific examples, check the “examples” folder inside PHPMailer documentation on Github.