DEV Community

Cover image for How to send email via Gmail SMTP in Yii2 framework
Bartosz Wójcik
Bartosz Wójcik

Posted on • Updated on

How to send email via Gmail SMTP in Yii2 framework

One of my sites has been flooded with spam bots and as a result - Gmail gave my mailing domain a bad score and I cannot send emails to @gmail addresses anymore, not from my email, not from my system, not from any of other domains and websites I host...

Gmail won't unblock your domain... thanks Google

I did remove all the spambots activity from one of my sites, appealed the decision via Gmail support forums, but still, I'm blocked from contacting my customers that has mailboxes at @gmail.com and there seems to be no way to change the domain score back to where it was.

It's been almost 2 weeks and my domain score is stuck at bad in https://postmaster.google.com/

Thanks @Google :(

How to send emails to @gmail.com boxes anyway?

As a result, I had to figure way out to send purchases, expired licenses, and other notifications to my customers.

I'm using PHP Yii2 framework and it turns out it was a breeze.

1. Setup a helper @gmail.com account

We need a @gmail.com account to send the notifications. One thing is important. After you create the account, you need to enable Less Secure Apps Access option:

Gmail options

It allows us to send emails via Gmail SMTP server.

2. Add custom component in your configuration file

In your Yii2 framework directory, modify your configuration file /common/config/Main.php (I'm using Advanced Theme) and include custom mailing component (name it however you want):

<?php
return [
    'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',

    ...

    'components' => [

        'mailerGmail' => [
            'class' => 'yii\swiftmailer\Mailer',
            'viewPath' => '@common/mail',
            'useFileTransport' => false,

            'transport' => [
                'class' => 'Swift_SmtpTransport',
                'host' => 'smtp.gmail.com',
                'username' => 'gmail.helper.account',
                'password' => 'PUT-YOUR-PASSWORD-HERE',
                'port' => '587',
                'encryption' => 'tls',
            ],
        ],
    ],
];
Enter fullscreen mode Exit fullscreen mode

3. Add helper function

I have added a helper function to one of my components registered as Yii::$app->Custom. It returns default mailer instance depending on the delivery email domain name.

I have also updated the code to detect the cases where the email doesn't contain @gmail.com string in it but still is using Gmail MX servers to handle emailing.

Detection is based on checking domain mailing server records using PHP built-in function getmxrr() and if that fails I send remote GET query to Google DNS service API to check the MX records.

////////////////////////////////////////////////////////////////////////////////
//
// get default mailer depending on the provided email address
//
////////////////////////////////////////////////////////////////////////////////

public function getMailer($email)
{
    // detect if the email or domain is using Gmail to send emails
    if (Yii::$app->params['forwardGmail'])
    {
        // detect @gmail.com domain first
        if (str_ends_with($email, "@gmail.com"))
        {
            return Yii::$app->mailerGmail;
        }

        // extract domain name
        $parts = explode('@', $email);
        $domain = array_pop($parts);

        // check DNS using local server requests to DNS
        // if it fails query Google DNS service API (might have limits)
        if (getmxrr($domain, $mx_records))
        {
            foreach($mx_records as $record)
            {
                if (stripos($record, "google.com") !== false || stripos($record, "googlemail.com") !== false)
                {
                    return Yii::$app->mailerGmail;
                }
            }

            // return default mailer (if there were records detected but NOT google)
            return Yii::$app->mailer;
        }

        // make DNS request
        $client = new Client();

        $response = $client->createRequest()
            ->setMethod('GET')
            ->setUrl('https://dns.google.com/resolve')
            ->setData(['name' => $domain, 'type' => 'MX'])
            ->setOptions([
                'timeout' => 5, // set timeout to 5 seconds for the case server is not responding
            ])
            ->send();

        if ($response->isOk)
        {
            $parser = new JsonParser();

            $data = $parser->parse($response);

            if ($data && array_key_exists("Answer", $data))
            {
                foreach ($data["Answer"] as $key => $value)
                {
                    if (array_key_exists("name", $value) && array_key_exists("data", $value))
                    {
                        if (stripos($value["name"], $domain) !== false)
                        {
                            if (stripos($value["data"], "google.com") !== false || stripos($value["data"], "googlemail.com") !== false)
                            {
                                return Yii::$app->mailerGmail;
                            }
                        }
                    }
                }
            }
        }
    }

    // return default mailer
    return Yii::$app->mailer;
}
Enter fullscreen mode Exit fullscreen mode

If the domain ends with @gmail.com or the domain is using Gmail mailing systems the mailerGmail instance is used, otherwise the default mailing component Yii::$app->mailer is used.

4. Usage

    /**
     * Sends an email to the specified email address using the information collected by this model.
     *
     * @return boolean whether the email was sent
     */
    public function sendEmail()
    {
        // find all active subscribers
        $message = Yii::$app->Custom->getMailer($this->email)->compose();

        $message->setTo([$this->email => $this->name]);
        $message->setFrom([\Yii::$app->params['supportEmail'] => "Bartosz Wójcik"]);
        $message->setSubject($this->subject);
        $message->setTextBody($this->body);

        $headers = $message->getSwiftMessage()->getHeaders();

        // message ID header (hide admin panel)
        $msgId = $headers->get('Message-ID');
        $msgId->setId(md5(time()) . '@pelock.com');

        $result = $message->send();

        return $result;
    }
Enter fullscreen mode Exit fullscreen mode

5. Know the limits

This is only the temporary solution and you need to be aware you won't be able to send bulk mail with this method, Gmail enforces some limitations on fresh mailboxes too.

6. Gmail is not your friend

It seems if your domain lands on that bad reputation scale there isn't any easy way out of it. I read on Gmail support forums, some people wait for more than a month for Gmail to unlock their domains without any result and communication back. My domain is not listed in any other blocked RBL lists (spam lists), it's only Gmail blocking it, but it's enough to understand how influential Google is, it can ruin your business in a second without a chance to fix it...

7. Google like to make things harder...

A small update. Now in 2021 you need to also enable access to unverified devices in order to make it work, go to:

https://accounts.google.com/b/0/DisplayUnlockCaptcha

And enable it.

Other solution is to enable 2-Step Verification and then generating so called "App Password", more information about it in this tutorial:

https://medium.com/graymatrix/using-gmail-smtp-server-to-send-email-in-laravel-91c0800f9662

Top comments (0)