How PHP email sending works: SMTP authentication and HTML templates
The article on How to Send Emails in PHP was originally published at Mailtrap's blog.
Today I'd love to share with you PHP email sending guide, one of the most popular web development languages.
PHP built-in mail function ()
There are two basic ways of sending emails with PHP: built-in mail function and external mail packages.
PHP’s built-in mail function () is very simple but at the same time, it provides a limited functionality for sending emails. You won’t be able to add attachments to your email, and building a beautiful HTML template with embedded images will be a tricky task as well.
The other side of the PHP mail function () is that the email is sent from your web server, which may cause issues with deliverability due to security concerns such as suspicion of spam and blacklisting. The best way to overcome this problem is sending messages via an SMTP server, however, this functionality is limited as well. PHP mail() does not usually allow you to use the external SMTP server and it does not support SMTP authentication.
In other words, I don’t recommend using the PHP built-in mail function() and advise you to avoid the headaches it may cause by installing an external mailer package.
If you are still committed to the PHP built-in mail function() and are ready to accept the challenge, let’s take a look at the basic code and its main parameters.
Syntax and parameters
The PHP mail syntax is pretty simple:
<?php
mail($to_email_address,$subject,$message,[$headers],[$parameters]);
?>
It uses the following parameters:
- “$to” = your message recipient(s). The email address format may be user@example.com or User user@example.com. If there are several recipients, separate them with comma(-s).
- “subject” = your message’s subject “message” = the body of your message. Lines should be separated with a CRLF (\r\n). Each line should not exceed 70 characters.
- “[$headers]” = additional recipients of your message, which can be included in CC or BCC. Headers are optional, except for the “from” header: it must be specified, otherwise, you will receive an error message like Warning: mail(): "sendmail_from" not set in php.ini or custom "From:" header missing.
For more details and additional parameters, refer to the PHP documentation.
Sending HTML email using PHP mail() function
The body of the message can be written in HTML. However, as I’ve mentioned above, it should be simple. In the PHP mail function(), the HTML part will look like this:
// Message
$message = '
<html>
<head>
<title>Review Request Reminder</title>
</head>
<body>
<p>Here are the cases requiring your review in December:</p>
<table>
<tr>
<th>Case title</th><th>Category</th><th>Status</th><th>Due date</th>
</tr>
<tr>
<td>Case 1</td><td>Development</td><td>pending</td><td>Dec-20</td>
</tr>
<tr>
<td>Case 1</td><td>DevOps</td><td>pending</td><td>Dec-21</td>
</tr>
</table>
</body>
</html>
';
It’s important to remember that to send HTML mail, you need to set the Content-type header:
$headers[] = 'MIME-Version: 1.0';
$headers[] = 'Content-type: text/html; charset=iso-8859-1';
Sending email via an SMTP server in PHP
Where do I specify the SMTP settings? This is a fair question. Go to the PHP installation folder and configure them in the “php.ini” file. But this will only work for localhost or Xmapp like solutions because as I have already mentioned, PHP mail function does not support SMTP authentication and doesn’t allow sending messages via external servers.
There are some other, rather haphazard options but I won’t promote them and recommend using external PHP mail packages for sending emails via an external SMTP server.
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. Besides, 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 it was required just 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.0"
or run
composer require phpmailer/phpmailer
If you don’t want to install Composer, for example, while working within a testing environment, you can 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. I will describe debugging is a separate section of this post.
To see detailed instructions on installation, check PHPMail documentation on Github.
SMTP configuration
To test PHP mail functionality, I will use Mailtrap, an Email Delivery Platform. Mailtrap Email Testing has a Fake SMTP Server, which allows us 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 easily be able to substitute the SMTP settings in our examples with your real server’s, whether you prefer a local one or a third-party service, like Gmail. (Just note that If you want to use the Gmail XOAUTH2 authentication class, you will also need to add a dependency on the league/oauth2-client package in your composer.json.)
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 = ‘234234234’ (example, generated by Mailtrap)
- password ($mail->Password = ‘435454545’ (example, generated by Mailtrap)
- and port ($mail->Port = 25, or 465, or 2525).
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
In any case, I start with PHPMailer class:
<?php
use PHPMailer\PHPMailer\PHPMailer;
If you have used Composer for installation, include the Composer generated autoload.php file:
require 'pathtocomposer\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';
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 = 'paste one generated by Mailtrap';
$mail->Password = 'paste one generated by Mailtrap’
$mail->SMTPSecure = 'tls';
$mail->Port = 25;
$mail->setFrom('info@mailtrap.io', 'Mailtrap');
$mail->addReplyTo('info@mailtrap.io', 'Mailtrap';
Specify the recipient of your message:
$mail->addAddress('info@mailtrap.io');
Optionally you can add a CC:
$mail->addCC('support@mailtrap.io’);
Set a subject for your message:
$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 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 executes correctly. To check the delivery result and details, go to your Mailtrap inbox: your messages will get there in seconds.
With Mailtrap Email Testing, 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.
For more configuration examples, check the “examples” folder inside PHPMailer documentation.
How to debug SMTP problems in PHPMailer
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, I entered an invalid hostname: mailtrap.io instead of smtp.mailtrap.io. and instantly received a message that an error 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 should you consider about PHP mail sending options?
In this article, I have described the basic PHP email sending principles, syntax, and parameters. Besides, I have reviewed two main ways of sending emails with PHP: its built-in mail function and PHPMailer, the most popular external mail package.
Undoubtedly, the second one is a much more advanced solution, with SMTP authentication support and a wide set of HTML related features, however, PHP mail function() can still be an option if you are sending plain text notification via localhost.
Beyond PHPMailer, there are a bunch of other noteworthy solutions for building an HTML email and sending it via an SMTP server, such as PEAR mail package, Swift Mailer, or Zend Framework.
I will review them and other PHP mail sending options in future posts.