If you’re wondering how to send emails in PHP, you’re probably not the only one as, according to W3Tech reports, even in 2024, 76.4% of all websites use PHP.
Luckily, you’ve come to the right place.
I’ve been researching the topic for a while now, and in this article, I’ll show you the most popular options for sending emails from PHP.
The processes and code blocks I’ll show you in this article are compatible with PHP version 7 and above.
How to send emails using PHPMailer
PHPMailer is one of the most, if not the most, popular email-sending libraries for PHP. It’s compatible with most PHP frameworks (e.g., Laravel, Symfony, etc.), and it offers powerful functionality and various features, such as:
- SMTP authentication
- Secure/MIME encryption
- TLS and SSL protocol support
- Plain text and HTML content
- Multipart templates/complex HTML
- Multiple formats, string, and binary attachments
- Support for embedded images
- Protection against header injection attacks
- Functionality for automatic email validation
Keep in mind that PHPMailer doesn’t have built-in mail sending. It can send emails using PHP’s mail() function, but I don’t recommend it because of potential spam-related issues and the complexity of the whole process. However, if you want to read more about it, be sure to check out our dedicated PHPMailer article.
Ideally, though, PHPMailer is used with an external SMTP server, with which it’s reliable and secure, making it a perfect choice as an email-sending functionality for your PHP application.
So, now, I’ll describe how to configure PHPMailer for any SMTP provider (eg., Mailtrap, SendGrid, Postmark, etc.) and later on, I’ll show you how to use it with Mailtrap SMTP so you can see how it works in action.
First, install the library via Composer, a dependency manager for PHP, recommended by PHPMailer creators on GitHub. You can also follow the manual for installation.
Once you install Composer, either add this line to your composer.json file:
"phpmailer/phpmailer": "^6.9"
or run the following command:
composer require phpmailer/phpmailer
Alternatively, you can add PHPMailer manually if you don’t want to install Composer, which can be handy within a testing environment. To do this, download files with PHPMailer source code, 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';
Now, all that’s left to do is add the credentials provided by your SMTP service provider (e.g., username, password, SMTP port, etc). They should look something like this:
$mail->isSMTP();
$mail->Host = 'live.smtp.mailtrap.io';
$mail->SMTPAuth = true;
$mail->Username = 'api';
$mail->Password = '1a2b3c4d5e6f7g';
$mail->SMTPSecure = 'tls';
$mail->Port = 587;
And here’s how it looks together with the code you can use to send email:
<?php
// Start with PHPMailer class
use PHPMailer\PHPMailer\PHPMailer;
require_once './vendor/autoload.php';
// create a new object
$mail = new PHPMailer();
// configure an SMTP
$mail->isSMTP();
$mail->Host = 'live.smtp.mailtrap.io';
$mail->SMTPAuth = true;
$mail->Username = 'api';
$mail->Password = '1a2b3c4d5e6f7g';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('confirmation@registered-domain', 'Your Hotel');
$mail->addAddress('receiver@gmail.com', 'Me');
$mail->Subject = 'Thanks for choosing Our Hotel!';
// Set HTML
$mail->isHTML(TRUE);
$mail->Body = '<html>Hi there, we are happy to <br>confirm your booking.</br> Please check the document in the attachment.</html>';
$mail->AltBody = 'Hi there, we are happy to confirm your booking. Please check the document in the attachment.';
// add attachment
// just add the '/path/to/file.pdf'
$attachmentPath = './confirmations/yourbooking.pdf';
if (file_exists($attachmentPath)) {
$mail->addAttachment($attachmentPath, 'yourbooking.pdf');
}
// send the message
if(!$mail->send()){
echo 'Message could not be sent.';
echo 'Mailer Error: ' . $mail->ErrorInfo;
} else {
echo 'Message has been sent';
}
How to send emails using Symfony Mailer
It’s 2024, which means Pear:: Mail and Swift Mailer are quite a bit outdated now, so we’re left with Symfony as our next obvious choice for sending emails in PHP. It’s a framework that’s super flexible, and thanks to its new Mailer and Mime components introduced in Symfony 4.3 release, it offers the following features:
- CSS inlining
- Twig template integration
- File attachments
- Signing and encrypting messages
- Direct integration with the most popular email-sending providers
- Asynchronous email sending with Symfony Messenger
Like PHPMailer, Symfony allows you to send emails with and without an external SMTP service by using Sendmail or other local transports. Although I’m not a big fan of Sendmail program as it faces higher spam filter scrutiny and requires a lot more maintenance, here’s how you can do it:
First, install the Mime and Mailer components, like so:
composer require symfony/mailer
Then, run the following script:
<?php
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport\SendmailTransport;
use Symfony\Component\Mime\Email;
require_once './vendor/autoload.php';
// Create the Sendmail transport
$transport = new SendmailTransport();
// Create the Mailer using your created Transport
$mailer = new Mailer($transport);
// Create a new email
$email = (new Email())
->from('hello@example.com')
->to('you@example.com')
->subject('Using Sendmail with Symfony!')
->text('Sending emails with Sendmail is easy!')
->html('<p>See Twig integration for better HTML integration!</p>');
// Send the email
$mailer->send($email);
On the other hand, if you want to send your emails securely and with a higher deliverability rate, here’s a snippet you can use with Symfony and an external SMTP:
<?php
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
use Symfony\Component\Mime\Email;
require_once './vendor/autoload.php';
$transport = (new Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport
('smtp.server.com', 587))
->setUsername('username')
->setPassword('password');
$mailer = new Mailer($transport);
$email = (new Email())
->from('hello@registered.com')
->to('you@example.com')
->subject('Time for Symfony Mailer!')
->text('Sending emails is fun again!')
->html('<p>See Twig integration for better HTML integration!</p>');
$mailer->send($email);
For more information on sending emails using Symfony Mailer, be sure to check our detailed guide explaining the whole process and the official documentation for the current version.
PHP mail() function
PHP mail() function is a built-in function for sending emails from PHP, but it has some limitations and drawbacks that make it less popular and an elephant in the room of sorts. 🐘
Namely, it is prone to deliverability issues as it relies on the local mail server configuration (e.g., it sends email from your web server), which can result in emails being flagged as spam or rejected by email service providers. It doesn’t have many advanced features like email tracking and most importantly, it can be vulnerable to email injection attacks.
Hence, you can see why I don’t recommend the mail() function. Nonetheless, I’ll show you how it works for explanatory purposes.
The syntax for PHP mail function is quite simple:
<?php
mail($to,$subject,$message,[$headers],[$parameters]);
?>
It uses the following mandatory parameters:
$to
= your message recipient’s email. The email address format may be user@example.com or User <user@example.com>. In general, it needs to comply with RFC 2822. This is mandatory.<strong>$subject</strong>
= your message’s email subject.$message
= the body of your message. Lines should be separated with a Carriage Return Line Feed, CRLF,(\r\n)
. Each line should not exceed 70 characters.$headers
= the mandatory one is thefrom
header: it must be specified, otherwise, you will receive an error message likeWarning: mail(): “sendmail_from”
not set in php.ini or customFrom:
header missing.
The additional headers indicate other recipients or copies of your message like CC
or BCC
. They can be an array where the key is a header name and the value is a header value. Or they can be a string. In this case, headers should be separated with a CRLF (\r\n).
$parameters
= for specifying the additional parameters defined in thesendmail_path
configuration setting.
Here’s how you can send a plain text email with additional headers:
<?php
$to = "somebody@example.com";
$subject = "My subject";
$txt = "Hello world!";
$headers = "From: webmaster@example.com" . "\r\n" .
"CC: somebodyelse@example.com";
mail($to,$subject,$txt,$headers);
?>
You also need to go to the PHP file installation folder and configure the SMTP settings in the php.ini file. But this will only work for localhost or XAMPP-like solutions, because, as we have already mentioned, the PHP mail() function does not support SMTP authentication and does not allow sending messages via external servers.
Now, if you don’t believe me when I tell you that sending HTML emails with mail function is tricky, here’s what the HTML message part usually looks like:
// 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>
';
To send HTML mail, you also need to set the Content-type header:
$headers = "MIME-Version: 1.0" . "\r\n";
$headers .= "Content-type:text/html;charset=UTF-8" . "\r\n";
or
$headers['MIME-Version'] = 'MIME-Version: 1.0';
$headers['Content-type'] = text/html; charset=iso-8859-1';
And if you want to send your message to multiple recipients, specify their email addresses in the $to =
parameter separating them with a comma(-s).
Don’t forget that when you send emails with <strong>mail()</strong>
, you may encounter some grave email deliverability issues. ❌ The messages dispatched will not benefit from the SPF and DKIM setup on your domain. Therefore, the messages will likely be treated as spam by the receiving MTA (Mail Transfer Agent). Thus, the overall deliverability of email messages sent via PHP mail ()
function is not guaranteed. Lastly, you won’t receive bounce-back messages if there is a delivery failure.
Send emails using SMTP
Now, I’ll show you how to use PHPMailer with Mailtrap Email Sending SMTP as it offers higher deliverability rates than other options and it’s super easy to configure.
The first thing you need to do is register an account and verify your email-sending domain as described in the video below:
Then, find the SMTP credentials provided by Mailtrap in the Sending Domains section under the SMTP/API Settings tab.
Important: Choose the Transactional Stream for now; I’ll go over the Bulk Stream later on in the article.
Now, all you have to do is use the following script to send a plain-text email and insert your credentials in it:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php'; // Adjust based on your installation method
$mail = new PHPMailer(true); // Enable exceptions
// SMTP Configuration
$mail->isSMTP();
$mail->Host = 'live.smtp.mailtrap.io'; // Your SMTP server
$mail->SMTPAuth = true;
$mail->Username = 'your_username'; // Your Mailtrap username
$mail->Password = 'your_password'; // Your Mailtrap password
$mail->SMTPSecure = 'tls';
$mail->Port = 587;
// Sender and recipient settings
$mail->setFrom('from@example.com', 'From Name');
$mail->addAddress('recipient@example.com', 'Recipient Name');
// Sending plain text email
$mail->isHTML(false); // Set email format to plain text
$mail->Subject = 'Your Subject Here';
$mail->Body = 'This is the plain text message body';
// Send the email
if(!$mail->send()){
echo 'Message could not be sent. Mailer Error: ' . $mail->ErrorInfo;
} else {
echo 'Message has been sent';
}
Don’t forget to make sure that isHTML
method is set to false if you want to send plain text emails.
Send HTML email
Dive into HTML email customization in PHP by checking out our dedicated article.
To send a basic HTML message with PHPMailer, we just have to make sure that isHTML property is set to (true) and we can send it like this:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'path/to/composer/vendor/autoload.php'; // Ensure the path is correct
$mail = new PHPMailer(true); // Passing `true` enables exceptions
try {
$mail->isSMTP();
$mail->Host = 'live.smtp.mailtrap.io';
$mail->SMTPAuth = true;
$mail->Username = 'paste one generated by Mailtrap'; // Your Mailtrap username
$mail->Password = 'paste one generated by Mailtrap'; // Your Mailtrap password
$mail->SMTPSecure = 'tls';
$mail->Port = 587;
$mail->setFrom('from@example.com', 'First Last');
$mail->addReplyTo('towho@example.com', 'John Doe'); //
$mail->addAddress('recipient@example.com', 'Recipient Name'); // Add a recipient
$mail->isHTML(true); // Set email format to HTML
$mail->Subject = "PHPMailer SMTP test";
$mail->Body = "<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>"; // Example HTML body
$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';
}
} catch (Exception $e) {
echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}
Send email to multiple recipients
To send email to multiple recipients, all you have to do is call the addAddress()
method for each recipient.
Here’s a code snippet for sending a HTML and plain-text email to multiple recipients:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'path/to/composer/vendor/autoload.php';
$mail = new PHPMailer(true); // Enable exceptions
$mail->isSMTP();
$mail->Host = 'live.smtp.mailtrap.io';
$mail->SMTPAuth = true;
$mail->Username = '1a2b3c4d5e6f7g'; // Your Mailtrap username
$mail->Password = '1a2b3c4d5e6f7g'; // Your Mailtrap password
$mail->SMTPSecure = 'tls';
$mail->Port = 587;
$mail->SMTPKeepAlive = true; // Keep the 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' => 'bob@example.com', 'name' => 'Bob']
];
foreach ($users as $user) {
$mail->addAddress($user['email'], $user['name']); // Correctly add each user's email
$mail->Body = "<h2>Hello, {$user['name']}!</h2> <p>How are you?</p>";
$mail->AltBody = "Hello, {$user['name']}! \nHow are you?";
try {
if ($mail->send()) {
echo "Message sent to: {$user['email']}\n";
} else {
echo "Mailer Error ({$user['email']}): {$mail->ErrorInfo}\n";
}
} catch (Exception $e) {
echo "Mailer Error ({$user['email']}): {$e->getMessage()}\n";
}
$mail->clearAddresses(); // Clear addresses for the next iteration
}
$mail->smtpClose(); // Optionally close the SMTP connection
Pro tip:
- You can also use addCC() and addBCC() to add recipients as carbon (CC) and blind carbon copies (BCC) respectively.
Send emails with attachments
When it comes to sending attachments with PHPMailer, you have two options:
- Attach a file from your filesystem
With this option, you should save your files in the same directory as the script.
To attach a file, all you need to do is specify its path. Moreover, you can add a filename, but it’s optional as the script will use the actual name of your file:
$mail->addAttachment('path/to/invoice1.pdf', 'invoice1.pdf');
So, for example, when you call this script, PHPMailer will attach the file located at path/to/invoice1.pdf to the email. The second parameter, invoice1.pdf, is optional and specifies the filename as it will appear to the recipient. If you don’t include it, PHPMailer will use the original file name.
If you want to add another file, repeat the command:
$mail->addAttachment('path/to/calculation1.xlsx', 'calculation1.xlsx');
- Add a string attachment
This method allows you to attach data without first having to save it as a physical file on your filesystem. Essentially, you attach the data stored in a variable. For instance, you can extract a file from a database, such as BLOB (Binary Large Object), so you don’t need to save it as a file.
To do this, use the addStringAttachment()
command, which 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 from a MySQL database.
You can also use a remote URL, like so:
$mail->addStringAttachment(file_get_contents($url), 'myfile.pdf');
Sending emails with an embedded image
To send an email with an embedded image, I use CID attachments, like so:
$mail->addEmbeddedImage('path/to/image_file.jpg', 'image_cid');
$mail->isHTML(true);
$mail->Body = '<img src="cid:image_cid">';
And here’s a full code snippet example to paint you a better picture:
<?php
use PHPMailer\PHPMailer\PHPMailer;
require 'path/to/composer/vendor/autoload.php';
$mail = new PHPMailer(true); // Enables exceptions
$mail->isSMTP();
$mail->Host = 'live.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 = 587;
$mail->setFrom('from@example.com', 'First Last');
$mail->addReplyTo('towho@example.com', 'John Doe');
$mail->addAddress('recipient@example.com', 'Recipient Name'); // Specify the recipient
$mail->isHTML(true);
$mail->Subject = "PHPMailer SMTP test";
$mail->addEmbeddedImage('path/to/image_file.jpg', 'image_cid'); // Specify the path to your image and a CID
$mail->Body = '<img src="cid:image_cid"> Mail body in HTML'; // Use the CID as the src attribute in your img tag
$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';
}
Asynchronous email sending
Although PHPMailer is synchronous by nature and doesn’t support asynchronous operations like Node.js for example, we can use the exec()
function to call a PHP script that sends the email in the background.
For this method to work, you’ll need to install the PHP CLI (Command Line Interface).
Here’s the sendEmail.php script you can use:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'path/to/vendor/autoload.php'; // Adjust the path as needed
$mail = new PHPMailer(true);
// SMTP Configuration
$mail->isSMTP();
$mail->Host = 'smtp.example.com'; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = 'user@example.com'; // SMTP username
$mail->Password = 'secret'; // SMTP password
$mail->SMTPSecure = 'tls'; // Enable TLS encryption, `ssl` also accepted
$mail->Port = 587; // TCP port to connect to
// Email Settings
$mail->setFrom('from@example.com', 'Mailer');
$mail->addAddress('recipient@example.com', 'Recipient Name'); // Add a recipient
$mail->isHTML(true); // Set email format to HTML
$mail->Subject = 'Here is the subject';
$mail->Body = 'This is the HTML message body <b>in bold!</b>';
$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
try {
$mail->send();
echo 'Message has been sent';
} catch (Exception $e) {
echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}
To execute this script, use the following command:
exec("php /Absolute/path/to/sendEmail.php > /dev/null &");
Notes:
- You need to make sure that the path to the PHP CLI executable (php) is correctly specified.
- In some cases and environments, you might have to use the full path to the CLI binary (e.g., /usr/bin/php).
- Don’t forget to check your server’s PHP configuration to ensure functions like
exec()
are not disabled, as they often are in shared hosting environments for security reasons.- The directive that controls this is the
disable_functions
directive in php.ini.
- The directive that controls this is the
How to send bulk emails
Up until this point, we used Mailtrap’s Transactional Stream, but now we’ll use Bulk Stream credentials, which allows us to send email to many recipients simultaneously.
So, log in to your Mailtrap account and navigate to the SMTP Settings tab, where you can find the Bulk Stream credentials on the right side of the window.
Then, insert these credentials into the following script, which you can use to send bulk email:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php'; // Path to the Composer autoload file
$mail = new PHPMailer(true);
try {
//Server settings
$mail->isSMTP();
$mail->Host = 'bulk.smtp.mailtrap.io'; // Set the SMTP server to send through
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = 'your_smtp_username'; // SMTP username
$mail->Password = 'your_smtp_password'; // SMTP password
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // Enable TLS encryption;
$mail->Port = 587; // TCP port to connect to
// Sender and reply-to address
$mail->setFrom('from@example.com', 'Mailer');
$mail->addReplyTo('replyto@example.com', 'Mailer');
// Content
$mail->isHTML(true); // Set email format to HTML
// Recipients list
$recipients = [
['email' => 'person1@example.com', 'name' => 'Person One'],
['email' => 'person2@example.com', 'name' => 'Person Two'],
// Add more recipients as needed
];
foreach ($recipients as $recipient) {
$mail->addAddress($recipient['email'], $recipient['name']); // Add a recipient
// Personalize the message
$mail->Subject = 'Here is the subject';
$mail->Body = 'This is the HTML message body <b>in bold!</b>';
$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
$mail->send();
$mail->clearAddresses(); // Clear addresses for the next iteration
}
echo 'Messages have been sent';
} catch (Exception $e) {
echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}
This way, we can send emails to multiple recipients without opening and closing the SMTP connection for each mail. It’s more efficient than, let’s say, creating a new PHPMailer instance for each mail.
But, if you’re dealing with a large volume of emails, you can use a job queue system like RabbitMQ, Beanstalkd, Redis, etc.
For the sake of the example, I’ll show you how to do it with Redis.
First, let’s install a PHP client for Redis, predis/predis
, via Composer by running the following command in the project’s root directory where your composer.json file is located:
composer require predis/predis
Then, we’ll need a job producer script that will push email jobs onto a Redis list. Note that here, each job can be a JSON-encoded string with all the necessary data for sending an email:
require 'vendor/autoload.php';
use Predis\Client;
$redis = new Client();
while (true) {
// Try to pop a job from the 'emailQueue' list, block for up to 5 seconds if it's empty
$job = $redis->brpop(['emailQueue'], 5);
if ($job) {
// Job format: [$listName, $jobData]
$emailDataJson = $job[1];
$emailData = json_decode($emailDataJson, true);
// Process the emailData...
// Send the email with PHPMailer, log success/failure, etc.
echo "Processed a job from emailQueue: " . $emailDataJson . "\n";
} else {
// No job was available in the queue within the timeout period
echo "Waiting for jobs...\n";
}
}
Lastly, let’s create a worker script and ensure that PHPMailer is configured with the Bulk Stream credentials:
<?php
require 'vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use Predis\Client;
// Initialize Redis client
$redis = new Client();
// Logger function
function logError($message) {
// Append errors to a log file
file_put_contents('email_errors.log', $message . PHP_EOL, FILE_APPEND);
}
// Function to send email, returns true on success or false on failure
function sendEmail($emailData) {
$mail = new PHPMailer(true);
try {
// SMTP Configuration
$mail->isSMTP();
$mail->Host = 'bulk.smtp.mailtrap.io';
$mail->SMTPAuth = true;
$mail->Username = 'your_smtp_username';
$mail->Password = 'your_smtp_password';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// Recipients
$mail->setFrom('from@example.com', 'Mailer');
$mail->addAddress($emailData['email']); // Recipient
// Content
$mail->isHTML(true); // Email format to HTML
$mail->Subject = $emailData['subject'];
$mail->Body = $emailData['body'];
$mail->send();
return true;
} catch (Exception $e) {
// Log the specific error
logError("Email could not be sent to {$emailData['email']}. Mailer Error: " . $mail->ErrorInfo);
return false;
}
}
// Retry policy
$maxRetries = 3; // Maximum number of retries for a failed email sending attempt
$retryDelay = 2; // Initial delay between retries in seconds
while ($emailDataJson = $redis->rpop('emailQueue')) {
$emailData = json_decode($emailDataJson, true);
$attempt = 0;
while (!sendEmail($emailData) && $attempt < $maxRetries) {
$attempt++;
// Exponential backoff
sleep($retryDelay * pow(2, $attempt - 1));
}
if ($attempt == $maxRetries) {
// Final log for emails that failed after all retries
logError("Final failure: Email could not be sent to {$emailData['email']} after $maxRetries attempts.");
} else {
echo "Email sent to {$emailData['email']}\n";
}
}
Pro tip: If you go with Redis, I recommend you use a process manager like Supervisor, which will keep the worker script running and automatically restart it if it crashes.
Send emails using email API
The best way to go about API-based email sending is to use an email service provider’s official PHP SDK as PHPMailer isn’t designed for this purpose. Luckily, we can rely on Mailtrap Email Sending API since it offers a PHP client and allows you to easily integrate Mailtrap with your app.
To start sending emails with Mailtrap Email Sending API, you first need to create an account and then add and verify your domain.
Then, go ahead and install the official Mailtrap PHP SDK via Composer.
You can use one of the following commands to get started quickly:
# With symfony http client (recommend)
composer require railsware/mailtrap-php symfony/http-client nyholm/psr7
# Or with guzzle http client
composer require railsware/mailtrap-php guzzlehttp/guzzle php-http/guzzle7-adapter
Note: As the Mailtrap API Client uses PSR-18 client abstraction and is thus not hard coupled to any library that sends HTTP messages, it gives you the flexibility to choose which HTTP client you want to use.
And to send a plain-text email with the SDK, simply use the code snippet below:
<?php
use Mailtrap\Config;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Mailtrap\Helper\ResponseHelper;
use Mailtrap\MailtrapClient;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;
require __DIR__ . '/vendor/autoload.php';
// Your API token from here https://mailtrap.io/api-tokens
$apiKey = getenv('MAILTRAP_API_KEY');
$mailtrap = new MailtrapClient(new Config($apiKey));
$email = (new Email())
->from(new Address('example@your-domain-here.com', 'Mailtrap Test'))
->replyTo(new Address('reply@your-domain-here.com'))
->to(new Address('email@example.com', 'Jon')) // Single recipient
->priority(Email::PRIORITY_HIGH)
->subject('Best practices of building HTML emails')
->text("Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap's Guide on How to Build HTML Email is live on our blog");
// Headers
$email->getHeaders()
->addTextHeader('X-Message-Source', 'domain.com')
->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client')); // the same as addTextHeader
// Custom Variables
$email->getHeaders()
->add(new CustomVariableHeader('user_id', '45982'))
->add(new CustomVariableHeader('batch_id', 'PSJ-12'));
// Category (should be only one)
$email->getHeaders()
->add(new CategoryHeader('Integration Test'));
try {
$response = $mailtrap->sending()->emails()->send($email); // Email sending API (real)
var_dump(ResponseHelper::toArray($response)); // body (array)
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
Note:
- Make sure to copy and paste your API key in the script, which you can find in the SMTP/API Settings tab under the Sending Domains section.
Send HTML email
To send HTML email, simply modify the script for plain-text emails by adding ->html() method.
In the code example below, I’ll show you how to send an HTML email that you can send to multiple recipients and attach files to it:
<?php
use Mailtrap\Config;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Mailtrap\Helper\ResponseHelper;
use Mailtrap\MailtrapClient;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;
use Symfony\Component\Mime\Part\DataPart;
require __DIR__ . '/vendor/autoload.php';
// Your API token from here https://mailtrap.io/api-tokens
$apiKey = “MAILTRAP_API_KEY”;
$mailtrap = new MailtrapClient(new Config($apiKey));
$email = (new Email())
->from(new Address('example@your-domain-here.com', 'Mailtrap Test'))
->replyTo(new Address('reply@your-domain-here.com'))
->to(new Address('email@example.com', 'Jon'))
->priority(Email::PRIORITY_HIGH)
->cc('mailtrapqa@example.com')
->addCc('staging@example.com')
->bcc('mailtrapdev@example.com')
->subject('Best practices of building HTML emails')
->text('Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap's Guide on How to Build HTML Email is live on our blog')
->html(
'<html>
<body>
<p><br>Hey</br>
Learn the best practices of building HTML emails and play with ready-to-go templates.</p>
<p><a href="https://mailtrap.io/blog/build-html-email/">Mailtrap's Guide on How to Build HTML Email</a> is live on our blog</p>
<img src="cid:logo">
</body>
</html>'
)
->embed(fopen('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg', 'r'), 'logo', 'image/svg+xml');
// Add an attachment
$email->attachFromPath('/path/to/your/file.pdf', 'Filename.pdf', 'application/pdf');
// Headers
$email->getHeaders()
->addTextHeader('X-Message-Source', 'domain.com')
->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client')); // the same as addTextHeader
// Custom Variables
$email->getHeaders()
->add(new CustomVariableHeader('user_id', '45982'))
->add(new CustomVariableHeader('batch_id', 'PSJ-12'));
// Category (should be only one)
$email->getHeaders()
->add(new CategoryHeader('Integration Test'));
try {
$response = $mailtrap->sending()->emails()->send($email); // Email sending API (real)
var_dump(ResponseHelper::toArray($response)); // body (array)
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
Send email to multiple recipients
To send an email to multiple recipients, simply add the addresses to the to
, cc
, and bcc
fields of the code. The Symfony Email
class, used in the script, allows us to add multiple recipients in each of the fields.
Here’s an example of a code you can use:
<?php
use Mailtrap\Config;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Mailtrap\Helper\ResponseHelper;
use Mailtrap\MailtrapClient;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;
require __DIR__ . '/vendor/autoload.php';
// Your API token from here https://mailtrap.io/api-tokens
$apiKey = getenv('MAILTRAP_API_KEY');
$mailtrap = new MailtrapClient(new Config($apiKey));
$email = (new Email())
->from(new Address('example@your-domain-here.com', 'Mailtrap Test'))
->replyTo(new Address('reply@your-domain-here.com'))
->to(new Address('email@example.com', 'Jon'))
->priority(Email::PRIORITY_HIGH)
->cc('mailtrapqa@example.com')
->addCc('staging@example.com')
->bcc('mailtrapdev@example.com')
->subject('Best practices of building HTML emails')
->text("Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap's Guide on How to Build HTML Email is live on our blog");
// Headers
$email->getHeaders()
->addTextHeader('X-Message-Source', 'domain.com')
->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client')); // the same as addTextHeader
// Custom Variables
$email->getHeaders()
->add(new CustomVariableHeader('user_id', '45982'))
->add(new CustomVariableHeader('batch_id', 'PSJ-12'));
// Category (should be only one)
$email->getHeaders()
->add(new CategoryHeader('Integration Test'));
try {
$response = $mailtrap->sending()->emails()->send($email); // Email sending API (real)
var_dump(ResponseHelper::toArray($response)); // body (array)
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
Send emails with attachments
To send an email with attachments, we can use the attachFromPath()
method provided by the Symfony Mime Email
class. It allows us to attach a file by specifying the path to the file.
You can use the following code to send an email with attached files:
<?php
use Mailtrap\Config;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Mailtrap\Helper\ResponseHelper;
use Mailtrap\MailtrapClient;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;
require __DIR__ . '/vendor/autoload.php';
// Your API token from here https://mailtrap.io/api-tokens
$apiKey = “MAILTRAP_API_KEY”;
$mailtrap = new MailtrapClient(new Config($apiKey));
// Initialize the email object
$email = (new Email())
->from(new Address('example@your-domain-here.com', 'Mailtrap Test'))
->replyTo(new Address('reply@your-domain-here.com'))
->to(new Address('email@example.com', 'Jon')) // For multiple recipients, repeat this line with different addresses
->priority(Email::PRIORITY_HIGH)
->subject('Best practices of building HTML emails')
->text("Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap's Guide on How to Build HTML Email is live on our blog");
// Attach a file
$email->attachFromPath('/path/to/your/file.pdf', 'filename.pdf', 'application/pdf');
// Headers and Custom Variables as previously defined
$email->getHeaders()
->addTextHeader('X-Message-Source', 'domain.com')
->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client'))
->add(new CustomVariableHeader('user_id', '45982'))
->add(new CustomVariableHeader('batch_id', 'PSJ-12'))
->add(new CategoryHeader('Integration Test'));
try {
$response = $mailtrap->sending()->emails()->send($email); // Send the email through Mailtrap
var_dump(ResponseHelper::toArray($response)); // Display the response
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
Sending emails with an embedded image
Sending emails with an embedded image is also quite easy, as we can use the ->embed()
method as part of the Email
object. The method essentially allows us to include images directly within the HTML body of the email, which can be referenced through a Content-ID (CID).
<?php
use Mailtrap\Config;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Mailtrap\Helper\ResponseHelper;
use Mailtrap\MailtrapClient;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;
require __DIR__ . '/vendor/autoload.php';
// Your API token from here https://mailtrap.io/api-tokens
$apiKey = getenv('MAILTRAP_API_KEY');
$mailtrap = new MailtrapClient(new Config($apiKey));
// Initialize the email object with necessary details
$email = (new Email())
->from(new Address('example@your-domain-here.com', 'Mailtrap Test'))
->replyTo(new Address('reply@your-domain-here.com'))
->to(new Address('email@example.com', 'Recipient Name'))
->subject('HTML Email with Embedded Image')
->html('<html><body>Here is an embedded image: <img src="cid:unique-image-id"></body></html>');
// Embed the image and assign it a CID
$imagePath = '/path/to/your/image.jpg'; // Make sure to use the correct path to your image file
$email->embedFromPath($imagePath, 'unique-image-id');
// (Optional) Add additional configurations like CC, BCC, text part, etc.
try {
$response = $mailtrap->sending()->emails()->send($email); // Send the email through Mailtrap
var_dump(ResponseHelper::toArray($response)); // Display the response
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
Tip: Don’t forget to replace /path/to/your/image.jpg with the actual path to your image file.
Asynchronous email sending
To send HTTP requests asynchronously, you can use PHP’s curl_multi
functions.
The <strong>curl_multi_init()</strong>
function is part of the cURL extension, which allows you to make multiple HTTP requests simultaneously, but if this extension is not enabled or installed, you won’t be able to use it or any other cURL functions.
To resolve this issue, you need to enable the cURL extension in your PHP installation. Here are the steps to do so for different operating systems:
sudo apt-get update
sudo apt-get install php-curl
After installing, you may need to enable the extension manually by editing your php.ini file, though it’s typically enabled automatically. If needed, find your php.ini file (the location depends on your PHP version and setup, a common path might be /etc/php/7.4/cli/php.ini for PHP 7.4 CLI) and make sure the line <strong>extension=curl</strong>
is uncommented (remove the semicolon at the beginning of the line if it exists).
You need to restart the Apache server.
sudo service apache2 restart
Here’s a code example of how you might do it:
<?php
// Initialize cURL multi handle
$multiCurl = curl_multi_init();
// Array to keep track of the individual cURL handles
$curlHandles = [];
// Example: Multiple email data (for demonstration, sending the same email to multiple recipients)
$emailDatas = [
[
'from' => 'from@example.com',
'to' => 'recipient1@example.com',
'subject' => 'Here is the subject',
'text' => 'This is the plain text message body',
'html' => '<p>This is the HTML message body</p>',
],
[
'from' => 'from@example.com',
'to' => 'recipient2@example.com',
'subject' => 'Here is the subject',
'text' => 'This is the plain text message body',
'html' => '<p>This is the HTML message body</p>',
]
// Add more as needed
];
foreach ($emailDatas as $emailData) {
// Prepare the cURL handle for each email
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.mailtrap.io/v1/messages'); // Use the correct API endpoint
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($emailData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer your_mailtrap_api_token', // Use your actual Mailtrap API token
]);
// Add the cURL handle to the multi handle
curl_multi_add_handle($multiCurl, $ch);
$curlHandles[] = $ch; // Keep track of the handles
}
// Execute the cURL multi handle
$active = null;
do {
$mrc = curl_multi_exec($multiCurl, $active);
if ($mrc != CURLM_OK) {
// Handle cURL multi error
break;
}
// Wait for activity on any curl_multi connection
if (curl_multi_select($multiCurl) == -1) {
usleep(100);
}
} while ($active);
// Read and process the responses
foreach ($curlHandles as $handle) {
$response = curl_multi_getcontent($handle);
// Process your response here. Check HTTP status code, response body, etc.
echo $response . PHP_EOL;
// Remove and close the handle
curl_multi_remove_handle($multiCurl, $handle);
curl_close($handle);
}
// Close the cURL multi handle
curl_multi_close($multiCurl);
How to send bulk emails
If you plan to send large amounts of email in PHP with Mailtrap Email Sending API, your best bet would be to use a job queue system.
But first, we need to set up our Bulk Stream. So, navigate to the SMTP/API Settings tab in your Mailtrap account and select API in the Bulk Stream window. There, you can find the host and API Token, which you need to copy and paste into your PHP script.
With these Bulk Stream credentials set up like this, you can use pretty much any job queue system, such as RabbitMQ, Beanstalkd, etc. For the purposes of this tutorial, I’ll show you how to set up Redis.
If you haven’t already, go ahead and add Redis to your project:
composer require predis/predis
To enqueue a job in Redis with all the necessary information, use the following PHP code snippet:
require 'vendor/autoload.php';
$predis = new Predis\Client();
$emailData = [
'from' => 'from@example.com',
'to' => 'recipient@example.com',
'subject' => 'Your Subject Here',
'html' => '<p>Your HTML content here</p>',
'text' => 'Your plain text here',
// Include any other data required for the email
];
// Enqueue the email data for later processing
$predis->lpush('emailQueue', json_encode($emailData));
Finally, use this PHP script that continuously monitors the Redis queue for new email tasks, clears them from the queue, and sends them via the Mailtrap Email Sending API:
require 'vendor/autoload.php';
$predis = new Predis\Client();
$mailtrapConfig = new Mailtrap\Config('your_mailtrap_api_key');
$mailtrapClient = new Mailtrap\MailtrapClient($mailtrapConfig);
while (true) {
// Block until an email task is available
$emailTask = $predis->brpop('emailQueue', 0);
if ($emailTask) {
$emailData = json_decode($emailTask[1], true); // $emailTask[1] contains the email data
// Send the email using Mailtrap Email Sending API
$email = (new Symfony\Component\Mime\Email())
->from(new Address($emailData['from']))
->to(new Address($emailData['to']))
->subject($emailData['subject'])
->html($emailData['html'])
->text($emailData['text']);
try {
$response = $mailtrapClient->sending()->emails()->send($email);
echo "Email sent: ", var_dump($response), "\n";
} catch (Exception $e) {
echo "Failed to send email: ", $e->getMessage(), "\n";
}
}
}
And don’t forget to run this worker as a background process or as a task in a process manager such as Supervisor.
Test emails before sending
So far, I’ve described various ways of sending mail in PHP, but how do you know your email-sending functionality works as intended? With all of this code in place, it’s crucial to test your emails in PHP before sending them, regardless of the sending method you opt for.
Testing your emails is the industry standard and an inextricable stage of the Email Sending cycle. Luckily, we’ve got you covered with Mailtrap’s holistic Email Testing solution.
With Mailtrap Email Testing you can check, preview, and troubleshoot your emails before you send them out. Essentially, you can inspect HTML/CSS of your email and easily spot faulty lines of code and fix/remove them.
This feature is crucial for checking the compatibility of your template-based HTML/CSS designs and polishing them to perfection without worrying that you’ll send the email live.
You can also share your testing process with your team members, create new projects, add multiple projects within, and more with Mailtrap Email Testing.
Most importantly, setting up Mailtrap Email Testing is a no-brainer as the solution can easily be integrated either via SMTP credentials or the official PHP SDK.
SMTP
To test your emails with a fake SMTP server, all you have to do is specify Mailtrap’s SMTP credentials in your transport method:
Host: sandbox.smtp.mailtrap.io
Port: 25 or 465 or 587 or 2525
Username: unique for each Mailtrap inbox
Password: unique for each Mailtrap inbox
TLS: Optional (STARTTLS on all ports)
You can also integrate Mailtrap with various PHP libraries and frameworks. For example, here’s a PHPMailer integration sample:
$phpmailer = new PHPMailer();
$phpmailer->isSMTP();
$phpmailer->Host = 'sandbox.smtp.mailtrap.io';
$phpmailer->SMTPAuth = true;
$phpmailer->Port = 587;
$phpmailer->Username = '1a2b3c4d5e6f7g';
$phpmailer->Password = '1a2b3c4d5e6f7g';
And here’s how your MAILER_DSN
would look like if you were to integrate Mailtrap with Symfony framework, for instance:
MAILER_DSN=smtp://username:password@sandbox.smtp.mailtrap.io:587/?encryption=tls
Once you send out your test email, you can check the Mailtrap virtual inbox after a few moments, and you should see the following message:
API
To integrate Mailtrap Email Testing API with your PHP-based app, you can use the following code:
?php
use Mailtrap\Config;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Mailtrap\Helper\ResponseHelper;
use Mailtrap\MailtrapClient;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;
require __DIR__ . '/../vendor/autoload.php';
/**********************************************************************************************************************
******************************************* EMAIL TESTING ************************************************************
**********************************************************************************************************************
*/
/**
* Email Testing API
*
* POST https://sandbox.api.mailtrap.io/api/send/{inbox_id}
*/
try {
// your API token from here https://mailtrap.io/api-tokens
$apiKey = getenv('MAILTRAP_API_KEY');
$mailtrap = new MailtrapClient(new Config($apiKey));
$email = (new Email())
->from(new Address('mailtrap@example.com', 'Mailtrap Test'))
->replyTo(new Address('reply@example.com'))
->to(new Address('email@example.com', 'Jon'))
->cc('mailtrapqa@example.com')
->addCc('staging@example.com')
->bcc('mailtrapdev@example.com')
->subject('Best practices of building HTML emails')
->text('Hey! Learn the best practices of building HTML emails and play with ready-to-go templates. Mailtrap's Guide on How to Build HTML Email is live on our blog')
->html(
'<html>
<body>
<p><br>Hey</br>
Learn the best practices of building HTML emails and play with ready-to-go templates.</p>
<p><a href="https://mailtrap.io/blog/build-html-email/">Mailtrap's Guide on How to Build HTML Email</a> is live on our blog</p>
<img src="cid:logo">
</body>
</html>'
)
->embed(fopen('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg', 'r'), 'logo', 'image/svg+xml')
->attachFromPath('README.md')
;
// Headers
$email->getHeaders()
->addTextHeader('X-Message-Source', '1alf.com')
->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client'))
;
// Custom Variables
$email->getHeaders()
->add(new CustomVariableHeader('user_id', '45982'))
->add(new CustomVariableHeader('batch_id', 'PSJ-12'))
;
// Category (should be only one)
$email->getHeaders()
->add(new CategoryHeader('Integration Test'))
;
// Required param -> inbox_id
$response = $mailtrap->sandbox()->emails()->send($email, 1000001); // <--- you should use your inbox_id here (otherwise you will get 401)
// print all possible information from the response
var_dump($response->getHeaders()); //headers (array)
var_dump($response->getStatusCode()); //status code (int)
var_dump(ResponseHelper::toArray($response)); // body (array)
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
And of course, don’t forget to replace placeholder values, such as the API token and other credentials with the actual data provided from your Mailtrap account.
For more information about Mailtrap Email Testing API, its functionalities and the various operations it allows you to perform (e.g., Testing and QA Automation, testing automated sequences, etc.) please refer to the official documentation.
Wrapping up
And with that, we conclude our tutorial on how to send emails in PHP!
I went over the most popular email-sending options in PHP and described how you can do it with a third-party mail service like Mailtrap, which can help you save time, money, and effort.
If you’re interested in learning more, be sure to check out our blog where you can read more PHP-related articles, such as: