How to use PHPMailer to send emails: The Complete Implementation Guide

How to Use PHPMailer to Send Emails: A Complete Setup and Troubleshooting Guide | UniOne
Alexey Kachalov Alexey Kachalov 12 march 2025, 11:36 85
For experts

Let’s face it, almost every website needs to send emails now and then. Whether it’s confirming user registrations, delivering password reset links, or sending out monthly newsletters, email functionality is a must. 

PHP developers have long wrestled with the built-in mail() function, which works for most basic needs but quickly shows its limitations as projects become more complex, especially when it comes to handling attachments, HTML content, and using external SMTP servers.

This is where PHPMailer comes into play. As one of PHP’s most downloaded libraries, PHPMailer makes the frustrating task of email delivery easier. 

In this guide, we'll take you through everything you need to know about using PHPMailer in your projects. We'll cover the basics, like installation and configuration, but we won't stop there. You'll learn how to create visually appealing HTML emails, attach files securely, manage recipient lists, and troubleshoot common issues.

Prerequisites

  • Basic knowledge of PHP
  • PHP interpreter installed on your local machine
  • Composer installed on your local machine (optional)

What Is PHPMailer And Why Use It Instead Of The PHP Mail() Function?

PHPMailer is an open-source PHP library specially designed to simplify the process of sending emails from PHP applications. First released in 2001, it has grown to become one of the most widely used PHP libraries, with over 70 million installs on Packagist and over 20k stars on GitHub, which is pretty impressive!

Adding to its popularity, it is also included in many PHP frameworks and CMS platforms like WordPress, Drupal, and Laravel.

Its architecture offers a clean, object-oriented interface for email functionality, handling the complexities of email specifications, including support for HTML emails, attachments, and SMTP authentication.

At this point, it may already be clear that most developers prefer PHPMailer over PHP’s native mail() function. To understand the “why” better, let's look at the limitations of mail() in the next section.

The Limitations of PHP’s Native mail() function

PHP’s built-in mail() function might seem like a straightforward solution for sending emails, but it has several issues that make it quite problematic for modern applications:

  • Inconsistent Behavior: The mail() function behaves differently across different SMTP server configurations. Programmers have to rely on the system admin for proper configuration.
  • Limited HTML Support: Sending HTML emails with mail() requires manual creation of MIME headers and message body parts. This task is time-consuming and prone to errors.
  • Poor Attachment Handling: Adding attachments involves even more complex MIME encoding that must be implemented manually.
  • Character Encoding Issues: The mail() function struggles with international character sets and UTF-8 encoding. This often results in garbled text.
  • No SMTP Choices: It relies on the server's local mail transfer agent (MTA) rather than connecting directly to the necessary SMTP servers, which is too limiting.
  • No Proper Error Handling: The function provides minimal feedback about delivery problems, making debugging difficult.

Let's look at the sample code that demonstrates how hard it is to send a very basic HTML email:

 

// Note the complexity required to send a simple HTML email with PHP's mail() function
$to = 'recipient@example.com';
$subject = 'Test Email';

// Headers required for HTML email
$headers = "From: sender@example.com\r\n";
$headers .= "Reply-To: sender@example.com\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=UTF-8\r\n";

// HTML message
$message = '
<html>
<head>
  <title>Test Email</title>
</head>
<body>
  <h1>Hello!</h1>
  <p>This is a test email sent using the PHP mail() function.</p>
</body>
</html>
';

// Send email
$success = mail($to, $subject, $message, $headers);

if (!$success) {
    $errorMessage = error_get_last()['message'];
    echo "Email sending failed: " . $errorMessage;
}

As you can see, even a basic HTML email requires multiple headers to be added manually, and adding attachments or handling multiple recipients would make this code significantly more complex.

Now that we have examined the limitations of PHP mail(), let's look at the advantages of using PHPMailer in the next section.

Advantages of Using PHPMailer

Let’s check out how PHPMailer addresses all these limitations:

  • Explicit SMTP Configuration: Connect directly to the SMTP servers you need with proper authentication, bypassing local mail server configurations.
  • Effortless HTML Emails: Create HTML emails easily with proper MIME encoding and alternative plain text versions.
  • Simplified Attachments: Add file attachments with just a few lines of code, with proper MIME handling.
  • Comprehensive Recipient Management: Easily handle multiple recipients, with support for TO, CC, BCC, and Reply-to addresses.
  • International Character Support: Built-in handling of UTF-8 and other character sets for proper display of international text.
  • Error Handling and Debugging: Detailed exception handling and debugging options for troubleshooting delivery issues.
  • Security Features: Support for TLS/SSL encryption, SMTP authentication, and other security measures.

The following code sample demonstrates how PHPMailer simplifies the process of sending an HTML email:

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require 'vendor/autoload.php';

$mail = new PHPMailer(true);

try {
    // Server settings
    $mail->isSMTP();
    $mail->Host = 'smtp.example.com';
    $mail->SMTPAuth = true;
    $mail->Username = 'user@example.com';
    $mail->Password = 'password';
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
    $mail->Port = 587;

    // Recipients
    $mail->setFrom('from@example.com', 'Sender Name');
    $mail->addAddress('recipient@example.com', 'Recipient Name');

    // Content
    $mail->isHTML(true);
    $mail->Subject = 'Test Email';
    $mail->Body = '<h1>Hello!</h1><p>This is a test email sent using PHPMailer.</p>';
    $mail->AltBody = 'Hello! This is a test email sent using PHPMailer.';

    $mail->send();
    echo 'Message has been sent';
} catch (Exception $e) {
    echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}

Now, you can see that with PHPMailer, you have a more secure and reliable package for sending those mission critical emails. But don’t get excited just yet! We are going to explore the various capabilities of PHPMailer in the upcoming sections.

How to install and configure PHPMailer

Installation Options

You can install PHPMailer in your PHP project using Composer or by manually adding the package files. Both options are described below.

  1. Using Composer(Recommended): To install and configure PHPMailer, you first need to download and install the Composer, which is PHP’s dependency manager. It can be installed on Linux/Unix/macOS and Windows. 

Since I use a Windows system, I'll be working with that through the rest of this PHPMailer tutorial. You can follow along regardless of the system you use.

To check if Composer was installed successfully, run the command:

 

composer -V

You’ll get a response that should look like this:

PHP Mailer Installation Composer Response

After installation, you can either add the following line to your composer.json file:

 

"phpmailer/phpmailer": "^6.9"

Or create a folder, cd into it, and run the command below on your terminal (recommended):

composer require phpmailer/phpmailer

After running the command, you should get a success message in your terminal like this:

PHP Mailer Installation Success Message

Now, open the folder on your preferred IDE. I use VSCode for reference.

  1. Manual Download: if you prefer not to use Composer, you can download PHPMailer directly from the GitHub repository and use the following steps to get it up and running:
  • Step1: Download the latest release as a ZIP file
  • Step 2: Extract the contents to your project directory 
  • Step 3: Include the necessary files in your PHP script

 

require 'path/to/PHPMailer/src/Exception.php';
require 'path/to/PHPMailer/src/PHPMailer.php';
require 'path/to/PHPMailer/src/SMTP.php';

Basic Configuration

You must set up the basic configuration options for PHPMailer to work properly. The configuration setup is described in the PHPMailer documentation. Once you've gathered your SMTP server details and login credentials, plug them into the code, as shown in the example below:

 

<?php
// Import PHPMailer classes into the global namespace
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;

// Include autoloader if using Composer
require 'vendor/autoload.php';

// Create a new PHPMailer instance
$mail = new PHPMailer(true); // true enables exceptions

try {
    // Server settings
    $mail->SMTPDebug = SMTP::DEBUG_SERVER;          // Enable verbose debug output
    $mail->isSMTP();                                // Send using SMTP
    $mail->Host       = 'smtp.example.com';         // SMTP server address
    $mail->SMTPAuth   = true;                       // Enable SMTP authentication
    $mail->Username   = 'user@example.com';         // SMTP username
    $mail->Password   = 'password';                 // SMTP password
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // Enable TLS encryption
    $mail->Port       = 587;                        // TCP port to connect to

    // Recipients
    $mail->setFrom('from@example.com', 'Sender Name');
    $mail->addAddress('recipient@example.com', 'Recipient Name');

    // Content
    $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 plain text version for non-HTML mail clients';

    $mail->send();
    echo 'Message has been sent';
} catch (Exception $e) {
    echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}

How to Send emails using PHPMailer and UniOne SMTP

PHPMailer allows you to use any preferred SMTP server to send your emails, either your own or provided by a third party. Using an external SMTP server is recommended as it is more secure and reliable. Throughout this tutorial, we'll dive headfirst into how to send emails using UniOne’s SMTP service.  You can also configure your script for another SMTP server if you opt for an alternative to UniOne.

To get started with UniOne, follow the steps below:

1. Sign up for a UniOne account. This is what your dashboard looks like below after signing up:

UniOne Email SMTP API  Onboarding

2. Add your sender domain. Learn how to do this in this video.

3. Access the SMTP debug tool and sign up to test the SMTP server functionality (more on this later in this tutorial). 

Note: you will need your user ID and API key to sign up if you’ll be going with UniOne’s SMTP settings. Learn more about this in this video. You will also need an email from a confirmed domain for this to work.

4. Add your credentials, such as your host, port, and API key from UniOne, into your PHP code. 

Now that you have your server settings ready, it's time to learn the different ways of sending emails using PHPMailer.

Sending Plain Text Emails

As we already discussed earlier, PHPMailer allows you to send emails in plain text. Here's the implementation:

 

<?php
//Import PHPMailer classes into the global namespace
//These must be at the top of your script, not inside a function
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

//Load Composer's autoloader
require 'vendor/autoload.php';

//Create an instance; passing `true` enables exceptions
$mail = new PHPMailer(true);

try {
    //Server settings
    $mail->SMTPDebug = SMTP::DEBUG_SERVER;  //Enable verbose debug output
    $mail->isSMTP();                                            //Send using SMTP
    $mail->Host       = 'smtp.eu1.unione.io';  //Set the SMTP server to send through
    $mail->SMTPAuth   = true;   //Enable SMTP authentication
    $mail->Username   = 'userID';  //SMTP username
    $mail->Password   = 'secret';  //SMTP password
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;  //Enable implicit TLS encryption
    $mail->Port       = 465;   //TCP port to connect to; use 587 if you have set `SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS`

    //Sender and Recipients
    $mail->setFrom('from@example.com', 'Mailer');
    $mail->addAddress('joe@example.net', 'Joe User');  //Add a recipient
                                                      //Optional name

    //Content
    $mail->isHTML(false);  //Set email format to plain text
    $mail->Subject = 'Here is the subject';
    $mail->Body = 'This is the body in plain text for non-HTML mail clients';

    $mail->send();
    echo 'Message has been sent';
} catch (Exception $e) {
    echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}

 

PHPMailer’s isHTML setting defaults to true. For plain text to work, we set it to false. Once you run the code, you get this or a similar response on your terminal:

PHP Mailer Email Message Sent

You should get similar responses throughout the rest of our tutorial.

Sending File Attachments

Here's the implementation for sending an attachment with PHPMailer:

 

<?php
//Import PHPMailer classes into the global namespace
//These must be at the top of your script, not inside a function
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

//Load Composer's autoloader
require 'vendor/autoload.php';

//Create an instance; passing `true` enables exceptions
$mail = new PHPMailer(true);

try {
    //Server settings
    $mail->SMTPDebug = SMTP::DEBUG_SERVER;
    $mail->isSMTP();
    $mail->Host       = 'smtp.eu1.unione.io';
    $mail->SMTPAuth   = true;
    $mail->Username   = 'userID';
    $mail->Password   = 'secret';
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
    $mail->Port       = 465;

    //Sender and Recipients
    $mail->setFrom('from@example.com', 'Mailer');
    $mail->addAddress('joe@example.net', 'Joe User');

  //Attachments
    $mail->addAttachment('/path/to/file.pdf', ‘Report.pdf);
    $mail->addAttachment('/path/to/image.jpg’);    //Optional name

    //Content
    $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';

    $mail->send();
    echo 'Message has been sent';
} catch (Exception $e) {
    echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}

In the code, we specified the attachment and pointed the local path to the .pdf file. You can specify other file types (like .png or .jpg):

 

    //Attachments
    $mail->addAttachment('/path/to/file.pdf', 'Report.pdf);   //Attachment
    $mail->addAttachment('/path/to/image.jpg');   //Name parameter is optional

And that's it! You can now send your file with the rest of your email.

Sending HTML Emails

Remember when we set our isHTML to false? Well, change of plans :) To be able to send HTML emails, you have to set it to true. 

 

<?php
//Import PHPMailer classes into the global namespace
//These must be at the top of your script, not inside a function
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

//Load Composer's autoloader
require 'vendor/autoload.php';

//Create an instance; passing `true` enables exceptions
$mail = new PHPMailer(true);

try {
    //Server settings
    $mail->SMTPDebug = SMTP::DEBUG_SERVER;
    $mail->isSMTP();
    $mail->Host       = 'smtp.eu1.unione.io';
    $mail->SMTPAuth   = true;
    $mail->Username   = 'userID';
    $mail->Password   = 'secret';
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
    $mail->Port       = 465;

    //Sender and Recipients
    $mail->setFrom('from@example.com', 'Mailer');
    $mail->addAddress('joe@example.net', 'Joe User');


    //Content
    $mail->isHTML(true);  //Set email format to HTML
    $mail->Subject = 'Here is the subject';
    $mail->Body = '  <html>
    <head>
        <title>HTML Email</title>
        <style>
            body { font-family: Arial, sans-serif; }
            .container { padding: 20px; }
            .header { background-color: #4CAF50; color: white; padding: 10px; }
            .content { padding: 15px; }
            .footer { background-color: #f1f1f1; padding: 10px; font-size: 12px; }
        </style>
    </head>
    <body>
        <div class="container">
            <div class="header">
                <h2>Welcome to Our Newsletter</h2>
            </div>
            <div class="content">
                <p>Hello <strong>Recipient</strong>,</p>
                <p>This is an <em>HTML email</em> example sent using PHPMailer.</p>
                <p>You can include <a href="https://example.com">links</a>, formatting, and more.</p>
            </div>
            <div class="footer">
                <p>This email was sent using PHPMailer. Please do not reply.</p>
            </div>
        </div>
    </body>
    </html>
    ';
    $mail->AltBody = 'This is the body in plain text for non-HTML mail clients';

    $mail->send();
    echo 'Message has been sent';
} catch (Exception $e) {
    echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}

I included a basic HTML markup to demonstrate how well you can customize your HTML email. Note that CSS style definitions are included in the head section. For better email clients compatibility, it is advisable to use inline CSS, however this makes the HTML part bloated.

Sending to Multiple Recipients (Including CC and BCC)

Sending emails to multiple recipients is easy and requires just a few extra lines of code:

    // Adding multiple recipients
   
    // Primary recipients
    $mail->addAddress('recipient1@example.com', 'Recipient One');
    $mail->addAddress('recipient2@example.com', 'Recipient Two');
   
    // Carbon Copy recipients
    $mail->addCC('cc1@example.com', 'CC Recipient One');
    $mail->addCC('cc2@example.com', 'CC Recipient Two');
   
    // Blind Carbon Copy recipients
    $mail->addBCC('bcc1@example.com', 'BCC Recipient One');
    $mail->addBCC('bcc2@example.com', 'BCC Recipient Two');

 

In the code above, we included CC (Carbon Copy)  and BCC (Blind Carbon Copy) recipients.

Sending Embedded Images

If you want an image to be shown in your email body, not as an attached file, use the sample code below:

    // Embed an image
    $mail->addEmbeddedImage('/path/to/logo.png', 'company_logo', 'logo.png');

    // Content
    $mail->isHTML(true);
    $mail->Subject = 'Email with Embedded Images';
    $mail->Body = '
        <h2>Welcome to Our Company</h2>
        <p>Here is our logo:</p>
        <img src="cid:company_logo" alt="Company Logo" width="300">
        <p>This image is embedded in the email body.</p>
        ';
    $mail->AltBody = 'Welcome to Our Company. View the HTML version to see our logo.';

Sending an Asynchronous Email

This part may be a little bit tricky, and you don't have to follow along here if you aren't a full-time PHP developer. The first thing we need to know is that PHPMailer operates synchronously by default. This means your application must wait for the email-sending process to complete before continuing execution. This can create noticeable delays in your application, especially when sending multiple emails or emails with attachments.

Unlike server-side languages like Node.js, where asynchronous operations are a fundamental part of the architecture, PHP doesn't natively support non-blocking operations. 

However, you can work around this limitation by using PHP's exec() function to run a separate email script in the background.

Here's how to implement this approach:

  • Step 1: Create a dedicated email-sending script — First, create a standalone script (let's call it send_email_background.php) that handles the email-sending process, and nothing else:

<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

//Load Composer's autoloader
require 'vendor/autoload.php';

// Create a log file to track execution since this runs in the background
$logFile = __DIR__ . '/email_log.txt';
file_put_contents($logFile, date('Y-m-d H:i:s') . " - Background mailer started\n", FILE_APPEND);

$mail = new PHPMailer(true);

try {
    // Set SMTP Configuration

    ...
    // Compose Your Email

    ...
    // Send the email
    $mail->send();
    file_put_contents($logFile, date('Y-m-d H:i:s') . " - Email sent successfully\n", FILE_APPEND);
} catch (Exception $e) {
    file_put_contents($logFile, date('Y-m-d H:i:s') . " - Email error: {$mail->ErrorInfo}\n", FILE_APPEND);
}

 

  • Step 2: Call the script asynchronously from your main application — In your main application script, use the exec() function to call the script without waiting for it to finish:

<?php
// Your main application code

// Trigger the email sending in the background
exec("php /path/to/send_email_background.php > /dev/null 2>&1 &");

// Continue with your application immediately
echo "Your request has been processed. You'll receive a confirmation email shortly.";

 

Important considerations to note are:

  • The exec() function is usually disabled in shared hosting environments for security reasons. Check your PHP configuration (php.ini) to see if it's listed in the disable_functions directive.
  • In some environments, you might need to specify the full path to the PHP binary:

exec("/usr/bin/php /path/to/send_email_background.php > /dev/null 2>&1 &");

Sending Bulk Emails

From an earlier discussion, you may think that sending emails in bulk is easy – just add as much recipients as you need. However, this is not a viable option due to many reasons. For example, you may want to customize the emails to include the addressee’s name in the subject, for example. Next, sending messages to different subscribers may result in different errors which you want to log and act accordingly, and so on. In this section, let's look at how to really send emails in bulk:

 

<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

//Load Composer's autoloader

require 'vendor/autoload.php';

function sendBulkEmails($recipients, $subject, $messageTemplate) {
    // Keep track of success/failure
    $results = [
        'success' => 0,
        'failed' => 0,
        'failures' => []
    ];
   
    // Create a persistent SMTP connection
    $mail = new PHPMailer(true);
   
    try {
        // Configure SMTP settings
        $mail->isSMTP();
        $mail->Host       = 'smtp.eu1.unione.io';
        $mail->SMTPAuth   = true;
        $mail->Username   = 'userID';
        $mail->Password   = 'secret';
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
        $mail->Port       = 465;
        $mail->SMTPKeepAlive = true; // Keep the SMTP connection open between separate emails for faster sending
       
        $mail->setFrom('sender@example.com', 'Sender Name');
        $mail->isHTML(true);
        $mail->Subject = $subject;
       
        // Loop through recipients
        foreach ($recipients as $recipient) {
            // Clear previous recipients
            $mail->clearAddresses();
            $mail->clearAllRecipients();
           
            // Add this recipient
            $mail->addAddress($recipient['email'], $recipient['name']);
           
            // Personalize the message
            $personalizedMessage = str_replace(
                ['{{name}}', '{{email}}'],
                [$recipient['name'], $recipient['email']],
                $messageTemplate
            );
           
            $mail->Body = $personalizedMessage;
           
            // Send the email
            if ($mail->send()) {
                $results['success']++;
            } else {
                $results['failed']++;
                $results['failures'][] = [
                    'email' => $recipient['email'],
                    'error' => $mail->ErrorInfo
                ];
            }
           
            // Optional: Add delay to avoid overloading the server
            // usleep(500000); // Sleep for 0.5 seconds
        }
    } catch (Exception $e) {
        // Handle general exception
        $results['error'] = $mail->ErrorInfo;
    } finally {
        // Close the SMTP connection
        $mail->smtpClose();
    }
   
    return $results;
}

// Example usage
$recipients = [
    ['email' => 'user1@example.com', 'name' => 'User One'],
    ['email' => 'user2@example.com', 'name' => 'User Two'],
    ['email' => 'user3@example.com', 'name' => 'User Three']

    // Add as many recipients as you need!
];

$subject = 'Bulk Email Test';

$messageTemplate = '
<html>
<body>
    <h2>Hello {{name}},</h2>
    <p>This is a personalized message for you at {{email}}.</p>
    <p>Thank you for being a subscriber!</p>
</body>
</html>
';

$results = sendBulkEmails($recipients, $subject, $messageTemplate);
print_r($results);

The code uses PHPMailer's SMTPKeepAlive feature to maintain a persistent connection, avoiding the need to reconnect to the SMTP server for each message. It tracks success and failure rates, implementing proper error handling for each email in the batch. 

As we can see, sending emails with PHP has never been easier. For really large volumes, however, you’ll need to consider a dedicated job queue system using Beanstalkd, RabbitMQ, or Redis. They are helpful for more complex requirements or high-volume email sending.

Error handling and debugging with PHPMailer

When implementing email functionality in production applications, you'll inevitably encounter situations where emails fail to send. This can be annoying! Fortunately,  PHPMailer provides robust debugging options to help you identify and resolve these issues quickly.

Understanding PHPMailer’s Debug Levels

PHPMailer's debugging system exposes the underlying SMTP conversation, giving you visibility into exactly what's happening during the email-sending process. You can activate debugging by setting the SMTPDebug property to different levels. For example:

  // Set debug level before configuring other settings
    $mail = new PHPMailer(true);
    $mail->SMTPDebug = 2; // Most useful level for general troubleshooting

The available debug levels provide increasingly detailed information:

  • Level 0: Off – No output (default)
  • Level 1: Client – Shows only messages sent from the client
  • Level 2: Client & Server – Shows the conversation between client and server (recommended for most troubleshooting)
  • Level 3: Connection details – Adds information about the initial connection, helpful for diagnosing STARTTLS issues
  • Level 4: Low-level data output – Very verbose output for advanced debugging

Common Error Cases and Their Debug Output

Let's examine two common error scenarios and how debugging helps identify them:

  • Example 1: Connection Problems (Invalid Hostname) — When you can’t connect to the SMTP server, the debug output clearly shows the connection failure:

2025-02-28 09:23:45    Connection: opening to smtp.wrongdomain.com:465, timeout=10, options=array()
2025-02-28 09:23:55    Connection failed. Error #2: stream_socket_client(): unable to connect to smtp.wrongdomain.com:465… //shows the rest of the message
2025-02-28 09:23:55    SMTP connect() failed.
Message could not be sent. Mailer Error: SMTP connect() failed.

In this example, the error occurs at the connection stage because I've deliberately used an incorrect hostname (smtp.wrongdomain.com). The debug output shows that the connection attempt timed out, indicating either a wrong hostname, port, or network connectivity issues.

  • Example 2: Authentication Failures — Authentication issues are another common problem that debugging helps identify:

 

2025-02-28 09:31:22    Connection: opening to smtp.eu1.unione.io:465, timeout=300, options=array()
2025-02-28 09:31:22    Connection: opened
2025-02-28 09:31:22    SMTP INBOUND: "220 smtp.eu1.unione.io ESMTP ready"
2025-02-28 09:31:22    SERVER -> CLIENT: 220 smtp.eu1.unione.io ESMTP ready
2025-02-28 09:31:22    CLIENT -> SERVER: EHLO localhost
...
2025-02-28 09:31:23    CLIENT -> SERVER: AUTH LOGIN
2025-02-28 09:31:23    SMTP INBOUND: "334 VXNlcm5hbWU6"
2025-02-28 09:31:23    SERVER -> CLIENT: 334 VXNlcm5hbWU6
2025-02-28 09:31:23    CLIENT -> SERVER: [credentials hidden]
2025-02-28 09:31:23    SMTP INBOUND: "334 UGFzc3dvcmQ6"
2025-02-28 09:31:23    SERVER -> CLIENT: 334 UGFzc3dvcmQ6
2025-02-28 09:31:23    CLIENT -> SERVER: [credentials hidden]
2025-02-28 09:31:24    SMTP INBOUND: "535 5.7.8 Authentication failed: Incorrect username or password"
2025-02-28 09:31:24    SERVER -> CLIENT: 535 5.7.8 Authentication failed: Incorrect username or password
2025-02-28 09:31:24    SMTP ERROR: AUTH command failed: 535 5.7.8 Authentication failed: Incorrect username or password
2025-02-28 09:31:24    SMTP Error: Could not authenticate.
2025-02-28 09:31:24    CLIENT -> SERVER: QUIT
...

Here, the connection to the SMTP server succeeds, but authentication fails. The debug output shows the exact point of failure: the server rejected the username/password combination. Crosscheck your credentials (i.e. user ID and API key), ensure they are correct and you're good to go!

Alternatively, you can check out our dedicated debug tool to find and fix errors.

How to test PHPMailer implementations?

Testing your email implementation is important before going to production. You can use our SMTP debug tool to perform the following tests:

  • SMTP Test
  • SMTP Proxy Test (a powerful tool that lets you debug a real SMTP session with your software and an SMTP provider like UniOne)
  • SMTP Blackhole test

Security considerations when using PHPMailer

Here are a few security considerations when using PHPMailer:

  • Use SMTP with Encryption: Always use SMTP with encrypted connection. You don't want anyone eavesdropping on your emails :)

// Use STARTTLS (more compatibility)
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;

// Or use SSL (recommended)
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->Port = 465;

  • Protect Your Credentials: Never, I repeat – never! hardcode SMTP credentials in your code. Instead, use environment variables or a secure configuration system. For example:

// Using environment variables
$mail->Username = getenv('SMTP_USERNAME');
$mail->Password = getenv('SMTP_PASSWORD');

// Or using a configuration class
$config = Config::getInstance();
$mail->Username = $config->get('smtp.username');
$mail->Password = $config->get('smtp.password');


 

  • Validate All User Input: Always validate and sanitize all user inputs that will be used in emails to prevent injection attacks:

// Bad practice
$userEmail = $_POST['email'];
$userName = $_POST['name'];
$mail->addAddress($userEmail, $userName);

// Good practice
$userEmail = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
$userName = htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8');

if ($userEmail === false) {
    throw new Exception('Invalid email address');
}

$mail->addAddress($userEmail, $userName);

  • Implement Rate Limiting: Implement rate limiting to prevent abuse of your email functionality. For example:

function canSendEmail($userId, $maxEmails = 10, $period = 3600) {
    global $db;
 
    // Check how many emails this user has sent in the defined period
    $stmt = $db->prepare("
        SELECT COUNT(*) as count
        FROM email_log
        WHERE user_id = ? AND attempt_time > DATE_SUB(NOW(), INTERVAL ? SECOND)
    ");
    $stmt->execute([$userId, $period]);
    $result = $stmt->fetch(PDO::FETCH_ASSOC);
 
    return $result['count'] < $maxEmails;
}

// Usage
if (!canSendEmail($userId)) {
    throw new Exception('Email sending limit reached. Please try again later.');
}

  • Use SPF, DKIM, and DMARC: Configure your domain by adding SPF, DKIM, and DMARC records to the DNS to improve deliverability and security (this is also a requirement for using UniOne’s SMTP):
    • SPF (Sender Policy Framework): SPF lets you define which servers can legitimately send emails on behalf of your domain. 
    • DKIM (DomainKeys Identified Mail): DKIM adds a digital signature to your outgoing messages, confirming they haven't been tampered with during delivery. 
    • DMARC (Domain-based Message Authentication): DMARC provides instructions to receiving mail servers about how to handle messages that don't pass SPF or DKIM verification.

When these settings are configured at the DNS level, PHPMailer can be used with DKIM:

// DKIM signing requires the OpenSSL extension
$mail->DKIM_domain = 'example.com';
$mail->DKIM_private = '/path/to/private_key.pem';
$mail->DKIM_selector = 'phpmailer'; // Selector prefix
$mail->DKIM_passphrase = ''; // If your key is protected
$mail->DKIM_identity = $mail->From;

  • Error Handling: Use proper error handling to avoid exposing sensitive information to the user:

try {
    // PHPMailer configuration and sending
    // ...
} catch (Exception $e) {
    // Log the detailed error for administrators
    error_log("Mail error: " . $mail->ErrorInfo);
 
    // Return a generic error to users
    return "We couldn't send your email. Please try again later.";
}

How UniOne Can Help

UniOne works really well alongside PHPMailer. If you're dealing with a growing number of emails or need better tracking and analytics, UniOne gives you the infrastructure without having to build it yourself. You don't even have to worry about setting up your own server – UniOne already has you covered!

Frequently Asked Questions

What is the use of a PHPMailer?

PHPMailer is a code library that enables secure and straightforward email transmission through PHP code from a web server. It functions as a Mail User Agent (MUA) that communicates with a Mail Submission Agent (MSA) server.

Is PHPMailer secure?

Absolutely! It allows for TSL/SSL encrypted connections. However, you are still responsible for the safety of your credentials. Never expose them explicitly in your code.

Which version of PHP is compatible with PHPMailer?

PHPMailer 6.0 requires PHP 5.5 or newer and maintains full compatibility with all newer PHP versions through 8.4.

Related Articles
Blog
For beginners
Personalization in Email Marketing: What Is It and How Does It Work?
Have you ever seen an email in your inbox starting with "Dear [your name]"? Such an email will likel
Valeriia Klymenko
18 october 2022, 14:229 min
Blog
For beginners
How to Create Email Groups in Gmail
Email groups help you keep your teammates and acquaintances in the loop and improve collaboration. I
Denys Romanov
24 may 2024, 11:0310 min
Blog
For beginners
How to Turn Off Email Notifications: Step-by-step Guide
Notifications help you monitor your emails and reply to the important ones on time. However, these n
Valeriia Klymenko
27 november 2023, 13:573 min