[wiki] How to send email via Gmail SMTP in Yii2 framework


  1. Gmail won't unblock your domain... thanks Google
  2. How to send emails to @gmail.com boxes anyway?
  3. 1. Setup a helper @gmail.com account
  4. 2. Add custom component in your configuration file
  5. 3. Add helper function
  6. 4. Usage
  7. 5. Know the limits
  8. 6. Gmail is not your friend

One of my sites has been flooded with spam bots and as a result - Gmail gave my mailing domain a bad score and I couldn't 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:

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',
],
],
],
];

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;
}

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;
}

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...