[PHP] 使用 Google Client SDK 串接 Gmail API 發信的方法

本篇文章更新時間:2019/08/26
如有資訊過時或語誤之處,歡迎使用 Contact 功能通知。
一介資男的 LINE 社群開站囉!歡迎入群聊聊~
如果本站內容對你有幫助,歡迎使用 BFX Pay 加密貨幣新台幣 贊助支持。


說實在這個主題還真的比較少討論,沒什麼資料可以參考,乾脆自己寫起來筆記。

使用 Gmail API 比傳統 Gmail SMTP 的做法麻煩,但好處是呼叫 API 的速度快且又不會犧牲安全性。

如果使用 Gmail SMTP 帳號密碼都打對的情況下還失敗,可以去檢查一下帳號是否有開啟「低安全性應用程式存取權

WordPress 網站我也都是透過 Post SMTP 這款使用 Gmail API 串接。

那 PHP 要自己實作這功能怎辦? 我的心得是如果可以使用 AWS SES 或是 Mailgun 等專業發信服務就不要自己串了.... 哈哈哈

前置準備

安裝 Google Client 的 SDK 和 PHPMailer 的函式庫。

composer require google/apiclient:"^2.0"
composer require phpmailer/phpmailer

申請 Google API Console 開專案,啟用 Gmail API ,然後申請一個 OAuth 2.0 用戶端 ID。

申請好後會可以下載一個憑證檔案,包含 ID 和 密碼,記得改名檔案為: credentials.json 搭配 Google 提供的 Quickstart 範例,把程式與憑證檔案放在同一個目錄下後指令執行: php -f quickstart.php

期間會請你瀏覽一個連結,登入 Google 帳號、同意授權後取得 code 來之後換 Access Token

這邊我省略了滿多步驟的,像是 OAuth2.0 會需要指定授權的網域和一個回呼網址來接收那個 code

再來是範例中沒有開啟發信的服務,就是讀取信箱而已,所以要自己補上授權範圍(scope

$client->addScope("https://mail.google.com/");

上面的授權權限就是全開,自己要知道自己在幹啥囉~

code 貼回執行中的程式後就會取得授權碼(Access Token),並存回同目錄下的檔案:token.json

到這個步驟,才算是準備好前期所需的資料:函式庫、指定授權的憑證檔案 credentials.jsontoken.json。這兩個檔案我都建議要改檔名,免得被人猜到,或是直接寫進去程式,搭配資料庫來放,現在只是不要太麻煩的紀錄方法而已!

使用 PHPMailer 來協助發信前處理

要寫到這邊可真是碰雷碰到滿臉灰,這邊使用 PHPMailer 的原因不是來發信,而是協助轉換 Gmail API 需要吃的原始 MIME 格式,自己組裝基本上不是一件簡單的事,除非發出去的信就是純文字,不然都會碰到要自己組裝 MIME 格式的問題。

程式範例直接上最快:

$service = new Google_Service_Gmail($client);
try {
    $mail = new PHPMailer();
    $mail->CharSet = "UTF-8";
    $mail->From = "發信端信箱";
    $mail->FromName = "發信端別名";
    $mail->AddAddress("收信端信箱", "收信端別名");
    $mail->AddReplyTo("回信端信箱", "回信端別名");
    $mail->Subject = "信件主題";
    $mail->Body = "信件內文";
    $mail->isHTML(true);
    $mail->preSend();
    $mime = $mail->getSentMIMEMessage();
    $mime = rtrim(strtr(base64_encode($mime), '+/', '-_'), '=');
    $mensaje = new Google_Service_Gmail_Message();
    $mensaje->setRaw($mime);
    $service->users_messages->send('me', $mensaje);
} catch (Exception $e) {
    print($e->getMessage());
}

Gist: Link

如果有更好的方法產生出發信用的 MIME 也非常歡迎留言或來訊跟我分享XD

真正發信的呼叫就只有在這三行

$mensaje = new Google_Service_Gmail_Message();
$mensaje->setRaw($mime);
$service->users_messages->send('me', $mensaje);

其他部分自己都要想辦法組裝出那個 raw data ,生命有限,愛用函式庫~

後話

其實從頭到尾都可以使用 PHPMailer 這套函式庫來處理發信,它還提供取得授權碼的方法,支援 Google, Microsoft 和 Yahoo,就是也是要手工一點去改程式來配對。光是要拿到授權碼就可以難倒不少人了吧!

最後整理發信的那個方法如下:

function send_email($to_name = "", $to_email = "", $subject = "", $body = "", $reply_to = "") {
    if ($to_name == "" || $to_email == "" || $subject == "" || $body == "") {
        return false;
    }
    $client = new Google_Client();
    $client->setAuthConfig('GoogleOAuth的網路應用程式用戶端資訊.json');
    $tokenPath = '神秘的授權檔案.json';
    if (file_exists($tokenPath)) {
        $accessToken = json_decode(file_get_contents($tokenPath), true);
        $client->setAccessToken($accessToken);
    }
    if ($client->isAccessTokenExpired()) {
        // 判斷授權是否過期,過期就更新
        if ($client->getRefreshToken()) {
            $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
        }
        // 把更新過的授權碼資訊寫到檔案裡
        if (!file_exists(dirname($tokenPath))) {
            mkdir(dirname($tokenPath), 0700, true);
        }
        file_put_contents($tokenPath, json_encode($client->getAccessToken()));
    }
    if (file_exists($tokenPath)) {
        $accessToken = json_decode(file_get_contents($tokenPath), true);
        $client->setAccessToken($accessToken);
    }

    //使用 PHPMailer 準備原始信件內容
    $mail = new PHPMailer();
    $mail->CharSet = "UTF-8";
    $mail->From = "發信端信箱";
    $mail->FromName = "發信端名稱";
    $mail->AddAddress($to_email, $to_name);
    $mail->AddReplyTo($reply_to, "");
    $mail->Subject = $subject;
    $mail->Body = $body;
    $mail->isHTML(true);
    $mail->preSend();
    $mime = $mail->getSentMIMEMessage();
    // The message needs to be encoded in Base64URL
    $mime = rtrim(strtr(base64_encode($mime), '+/', '-_'), '=');

    try {
        $msg = new Google_Service_Gmail_Message();
        $msg->setRaw($mime);
        $objGMail = new Google_Service_Gmail($client);
        $objSentMsg = $objGMail->users_messages->send("me", $msg);
        return empty($objSentMsg) ? false : true;
    } catch (Exception $e) {
        // print($e->getMessage());
        return false;
    }
}

Share:

作者: Chun

資訊愛好人士。主張「人人都該為了偷懶而進步」。期許自己成為斜槓到變進度條 100% 的年輕人。[///////////____36%_________]

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *


文章
Filter
Apply Filters
Mastodon