How to Send Email using Gmail API with PHPMailer

Emails are essential for any website. Using emails you can interact with the users for various reasons. With the help of email, you can acknowledge them for some actions, urge users to come back to the website by promoting some exciting offers, and keep readers in the loop by sending them new updates, information, etc.

When it comes to PHP-powered websites, the mail() function is used to send an email. You can use this method as follows.

<?php
$to = 'nobody@example.com';
$subject = 'the subject';
$message = 'hello';
$headers = array(
    'From' => 'webmaster@example.com',
    'Reply-To' => 'webmaster@example.com',
    'X-Mailer' => 'PHP/' . phpversion()
);
// To send HTML mail, the Content-type header must be set
$headers[] = 'MIME-Version: 1.0';
$headers[] = 'Content-type: text/html; charset=iso-8859-1';

mail($to, $subject, $message, $headers);

Isn’t it easy? Unfortunately, there are some limitations to using the mail() function.

  • It’s hard to send attachments in an email.
  • Your emails most probably end up in spam.
  • Couldn’t work if the server is not configured correctly.

SMTP Server

The user can easily overcome the above limitations by using the SMTP server. An SMTP(Simple Mail Transfer Protocol) server is an application used to send outgoing mail between email senders and receivers.

For PHP applications, you can use PHPMailer and Symfony Mailer libraries that allow you to send emails through SMTP servers.

    The SMTP server would fix all drawbacks mentioned above. However, they also have issues when it comes to security.

    In most cases, while using an SMTP server you need to pass the login credentials. If you intend to use Gmail SMTP or Hosting’s SMTP, you require to pass your login information – username, and password. This leads to a major security threat if your server gets hacked. The hacker would then get your login details.

    There are some providers like Mailjet that give you separate API keys instead of login credentials for SMTP servers.

    One can choose providers like Mailjet. But it requires you to enroll in their service. Most of the services provide a free plan for the limited emails that can be sent. Based on your requirement, you can go for either free or premium plans.

    But as a developer, you prefer to integrate a service that is both free and reliable. I’d recommend using Google APIs for your email operations. Apart from the Gmail SMTP server, Google also provides Gmail XOAUTH2. With the combination of PHPMailer and Gmail XOAUTH2, you can send your emails free of cost.

    PHPMailer

    The PHPMailer is one of the most popular libraries used for sending emails from PHP applications. Popular CMS like WordPress, Drupal, and Joomla uses this library under the hood for managing emails.

    Let’s install this library using the below command:

    composer require phpmailer/phpmailer

    Later, we also need to deal with Google OAuth. To handle OAuth, install the league/oauth2-google library.

    composer require league/oauth2-google

    Upon installing libraries, copy vendor/phpmailer/phpmailer/get_oauth_token.php and paste it into the project’s root directory. You’ll require this file to complete the Google OAuth flow and grab the refresh token.

    To integrate the Google OAuth, you need to register the application with Google and get credentials. While registering the app, it requires setting an Authorized redirect URL. Here, we will use the path of get_oauth_token.php file. Let’s say this path is http://localhost/artisansweb/get_oauth_token.php .

    Note: Like PHPMailer, you can also use the Symfony Mailer library to send emails. Refer to our article How to Send Email using Gmail API in PHP.

    Register an Application with Google

    Follow the steps below to register an application with Google. At the end, you should copy your client ID and client secret keys.

    • Go to the Google Developer Console.
    • Create a new project. You can also select an existing project.
    • Add a name to your project. Google Console will generate a unique Project ID for it.
    • Your project will appear on top of the left sidebar.
    • Click on Library. You will see a list of Google APIs.
    • Enable Gmail API.
    • Click on the Credentials. Select Oauth Client id under Create credentials. Choose the radio button for the Web Application.
    • Give the Name. Under Authorized JavaScript origins enter your domain URL. In the ‘Authorized redirect URIs’ add the link of the redirect URL. In my case, I passed the URL http://localhost/artisansweb/get_oauth_token.php
    • Click on the Create button. You will get a client ID and client secret in the pop-up. Copy these details. We will need it in a moment.
    gmail_xoauth2

    Basic Configuration

    The PHPMailer library requires an OAuth refresh token to perform the task. So, it is better to store it in a database. Run the below SQL to create the oauth_tokens table.

    CREATE TABLE `oauth_tokens` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `provider` varchar(255) NOT NULL,
      `provider_value` text NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

    Now, to interact with this database table let’s create a DB class that will insert/update, and fetch the refresh token.

    class-db.php

    <?php
    class DB {
        private $dbHost     = "DB_HOST";
        private $dbUsername = "DB_USERNAME";
        private $dbPassword = "DB_PASSWORD";
        private $dbName     = "DB_NAME";
       
        public function __construct(){
            if(!isset($this->db)){
                // Connect to the database
                $conn = new mysqli($this->dbHost, $this->dbUsername, $this->dbPassword, $this->dbName);
                if($conn->connect_error){
                    die("Failed to connect with MySQL: " . $conn->connect_error);
                }else{
                    $this->db = $conn;
                }
            }
        }
       
        public function is_table_empty() {
            $result = $this->db->query("SELECT id FROM oauth_tokens WHERE provider = 'google'");
            if($result->num_rows) {
                return false;
            }
       
            return true;
        }
       
        public function get_refresh_token() {
            $sql = $this->db->query("SELECT provider_value FROM oauth_tokens WHERE provider = 'google'");
            $result = $sql->fetch_assoc();
            return $result['provider_value'];
        }
       
        public function update_refresh_token($token) {
            if($this->is_table_empty()) {
                $sql = sprintf("INSERT INTO oauth_tokens(provider, provider_value) VALUES('%s', '%s')", 'google', $this->db->real_escape_string($token));
                $this->db->query($sql);
            } else {
                $sql = sprintf("UPDATE oauth_tokens SET provider_value = '%s' WHERE provider = '%s'", $this->db->real_escape_string($token), 'google');
                $this->db->query($sql);
            }
        }
    }

    Generate OAuth Refresh Token for Gmail API

    In the previous steps, you have copied get_oauth_token.php in the root directory. This file will give you a refresh token. As we need to store the refresh token, I am including class-db.php file and the code for token insertion into it. Also, I am modifying the original file to keep a code related to Google only.

    get_oauth_token.php

    <?php
    
    /**
     * PHPMailer - PHP email creation and transport class.
     * PHP Version 5.5
     * @package PHPMailer
     * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
     * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
     * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
     * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
     * @author Brent R. Matzelle (original founder)
     * @copyright 2012 - 2020 Marcus Bointon
     * @copyright 2010 - 2012 Jim Jagielski
     * @copyright 2004 - 2009 Andy Prevost
     * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
     * @note This program is distributed in the hope that it will be useful - WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     * FITNESS FOR A PARTICULAR PURPOSE.
     */
    
    /**
     * Get an OAuth2 token from an OAuth2 provider.
     * * Install this script on your server so that it's accessible
     * as [https/http]://<yourdomain>/<folder>/get_oauth_token.php
     * e.g.: http://localhost/phpmailer/get_oauth_token.php
     * * Ensure dependencies are installed with 'composer install'
     * * Set up an app in your Google/Yahoo/Microsoft account
     * * Set the script address as the app's redirect URL
     * If no refresh token is obtained when running this file,
     * revoke access to your app and run the script again.
     */
    
    namespace PHPMailer\PHPMailer;
    
    /**
     * Aliases for League Provider Classes
     * Make sure you have added these to your composer.json and run `composer install`
     * Plenty to choose from here:
     * @see http://oauth2-client.thephpleague.com/providers/thirdparty/
     */
    //@see https://github.com/thephpleague/oauth2-google
    use League\OAuth2\Client\Provider\Google;
    //@see https://packagist.org/packages/hayageek/oauth2-yahoo
    use Hayageek\OAuth2\Client\Provider\Yahoo;
    //@see https://github.com/stevenmaguire/oauth2-microsoft
    use Stevenmaguire\OAuth2\Client\Provider\Microsoft;
    //@see https://github.com/greew/oauth2-azure-provider
    use Greew\OAuth2\Client\Provider\Azure;
    
    if (!isset($_GET['code']) && !isset($_GET['provider'])) {
        ?>
    <html>
    <body>
        <a href='?provider=Google'>Google</a><br>
    </body>
    </html>
        <?php
        exit;
    }
    
    require 'vendor/autoload.php';
    require_once 'class-db.php';
    
    session_start();
    
    $providerName = '';
    $clientId = 'GOOGLE_CLIENT_ID';
    $clientSecret = 'GOOGLE_CLIENT_SECRET';
    
    if (array_key_exists('provider', $_GET)) {
        $providerName = $_GET['provider'];
        $_SESSION['provider'] = $providerName;
    } elseif (array_key_exists('provider', $_SESSION)) {
        $providerName = $_SESSION['provider'];
    }
    
    //If this automatic URL doesn't work, set it yourself manually to the URL of this script
    $redirectUri = (isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
    //$redirectUri = 'http://localhost/PHPMailer/redirect';
    
    $params = [
        'clientId' => $clientId,
        'clientSecret' => $clientSecret,
        'redirectUri' => $redirectUri,
        'accessType' => 'offline'
    ];
    
    $options = [];
    $provider = null;
    
    switch ($providerName) {
        case 'Google':
            $provider = new Google($params);
            $options = [
                'scope' => [
                    'https://mail.google.com/'
                ]
            ];
            break;
    }
    
    if (null === $provider) {
        exit('Provider missing');
    }
    
    if (!isset($_GET['code'])) {
        //If we don't have an authorization code then get one
        $authUrl = $provider->getAuthorizationUrl($options);
        $_SESSION['oauth2state'] = $provider->getState();
        header('Location: ' . $authUrl);
        exit;
        //Check given state against previously stored one to mitigate CSRF attack
    } elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
        unset($_SESSION['oauth2state']);
        unset($_SESSION['provider']);
        exit('Invalid state');
    } else {
        unset($_SESSION['provider']);
        //Try to get an access token (using the authorization code grant)
        $token = $provider->getAccessToken(
            'authorization_code',
            [
                'code' => $_GET['code']
            ]
        );
        //Use this to interact with an API on the users behalf
        //Use this to get a new access token if the old one expires
        //echo 'Refresh Token: ', $token->getRefreshToken();
    
        $db = new \DB();
        if($db->is_table_empty()) {
            $db->update_refresh_token($token->getRefreshToken());
            echo "Refresh token inserted successfully.";
        }
    }
    

    Before running this file, make sure you have passed your client ID and client secret to the variables $clientId and $clientSecret respectively.

    When you run this script on a browser, it’ll ask you to complete Authorization. Once you complete it, you should get the refresh token stored in the database. That means you are good to go ahead to send your email.

    Send Email using Gmail API with PHPMailer

    You are now ready with the refresh token and Google app credentials. Next, using the code provided by the PHPMailer library you can send your emails as follows.

    <?php
    use PHPMailer\PHPMailer\PHPMailer;
    use PHPMailer\PHPMailer\SMTP;
    use PHPMailer\PHPMailer\OAuth;
    use League\OAuth2\Client\Provider\Google;
    
    
    require_once 'vendor/autoload.php';
    require_once 'class-db.php';
    
    $mail = new PHPMailer();
    $mail->isSMTP();
    $mail->Host = 'smtp.gmail.com';
    $mail->Port = 465;
    
    //Set the encryption mechanism to use:
    // - SMTPS (implicit TLS on port 465) or
    // - STARTTLS (explicit TLS on port 587)
    $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
    
    $mail->SMTPAuth = true;
    $mail->AuthType = 'XOAUTH2';
    
    $email = 'GOOGLE_EMAIL'; // the email used to register google app
    $clientId = 'GOOGLE_CLIENT_ID';
    $clientSecret = 'GOOGLE_CLIENT_SECRET';
    
    $db = new DB();
    $refreshToken = $db->get_refresh_token();
    
    //Create a new OAuth2 provider instance
    $provider = new Google(
        [
            'clientId' => $clientId,
            'clientSecret' => $clientSecret,
        ]
    );
    
    //Pass the OAuth provider instance to PHPMailer
    $mail->setOAuth(
        new OAuth(
            [
                'provider' => $provider,
                'clientId' => $clientId,
                'clientSecret' => $clientSecret,
                'refreshToken' => $refreshToken,
                'userName' => $email,
            ]
        )
    );
    
    $mail->setFrom($email, 'FROM_NAME');
    $mail->addAddress('RECIPIENT_EMAIL', 'RECIPIENT_NAME');
    $mail->isHTML(true);
    $mail->Subject = 'Email Subject';
    $mail->Body = '<b>Email Body</b>';
    
    //send the message, check for errors
    if (!$mail->send()) {
        echo 'Mailer Error: ' . $mail->ErrorInfo;
    } else {
        echo 'Message sent!';
    }

    Make sure to replace placeholders with their actual values. Upon running this code, the recipient should receive an email.

    I hope you understand sending emails using Gmail API with PHPMailer. This tutorial should help you to resolve the most common problem of email delivery. Try it and let me know your thoughts in the comment section below.

    Related Articles

    If you liked this article, then please subscribe to our YouTube Channel for video tutorials.

    8 thoughts on “How to Send Email using Gmail API with PHPMailer

    1. Fatal error: Uncaught exception ‘BadMethodCallException’ with message ‘Required parameter not passed: “refresh_token”‘

      could you help?

    2. how can i refresh tokens automatically (without need to login on 0Auth screen) when his time life expires?

    3. I have been having problems with invalid grant do you have resources links for trouble shooting?

    Leave a Reply

    Your email address will not be published. Required fields are marked *