事情的開始
前陣子突然發現承租的主機好像沒有發信給自己。不管是 Drupal 系統的留言通知,或是自己寫的一些 PHP 程式,都沒有寄各種通知信給我,上站一測,果然如此,這可不得了,立刻通知主機商,請他們處理。過去也有類似的經驗,他們通常很快就處理好了。
主機商不久來信,信中說是 GMail 擋了我的信,系統本身並沒有問題,還要我參考這一篇:
https://support.google.com/mail/answer/81126#authentication
我再上站測試,果然寄信到 outlook.com 的信箱沒有問題,寄到 gmail.com 就石沈大海,心中隱隱覺得不妙。
上面那個網址,我看了半天,也不知是怎麼回事。上網查了一些資料,猜想大概是站上的註冊信與廣告信太多,gmail 可能懷疑我的主機有問題,因此要求進一步的認證,否則就不接受這個主機寄出來的信。
於是上網向各方求助,也請主機商再指導一番。他們來信說,我使用的都是 php mail(),建議我改用 smtp 的方式寄信,另外也要我啟用 SPF 和 DKIM,一些朋友也是如此建議,雖然不太懂,但至少有進一步研究的方向了。
SPF 和 DKIM 的設定
php mail() 和其用法
php mail() 就是一般我在 php 上寄信的程式,原本都用的很開心,現在才知道因為 mail() 程式是透過程式直接寄信,收信端無法反向查詢驗證,所以經常會有被阻擋當成垃圾信件的情況,很多主機已經不接收這類的信了,要改成使用 smtp 才行。
在此先記錄一下自己過去是如何使用 mail 來寄信。
<?php
if (! utf8mail("寄信者@gmail.com", "收信者@gmail.com", "主旨內容", "信件內容\n這是第二行"))
{
//.... error
}
function utf8mail($from, $to, $subject, $message)
{
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/plain;charset=utf-8\r\n";
$headers .= "From: $from\nReply-To: $from\n";
$headers .= "X-Mailer: PHP/".phpversion();
$bool = mail($to, $subject, $message, $headers);
return $bool;
}
?>
使用 PHPMailer 用 smtp 寄信
SMTP 是簡單郵件傳輸協定(Simple Mail Transfer Protocol) ,這是在 Internet 上傳送 email 的一個協定,意思就是可以用它來寄信。
再度上網查詢,得知可以安裝一個套件 PHPMailer 就可以在 PHP 使用 smtp 寄信。問題來了,我不是主機的擁有者,無法直接安裝此套件。所幸後來找到有人有類似的問題,找到了只要複製幾個重要的檔案,就可以執行的方法了。
下載 PHPMailer v6.1.3
撰寫由 GMail SMTP 寄信的程式 (PHPMailer v6.1.3)
<?php// Import PHPMailer classes into the global namespace// These must be at the top of your script, not inside a functionuse PHPMailer\PHPMailer\PHPMailer;use PHPMailer\PHPMailer\SMTP;use PHPMailer\PHPMailer\Exception;// Load Composer's autoloader// require 'vendor/autoload.php';// 範例原本如上,但因為我沒有使用安裝的方式,所以就採用如下require '/xxx/PHPMailer-6.1.3/src/Exception.php';require '/xxx/PHPMailer-6.1.3/src/PHPMailer.php';require '/xxx/PHPMailer-6.1.3/src/SMTP.php';// 產生 Mailer 實體$mail = new PHPMailer(true);try {//Server settings// 產生 Debug 訊息$mail->SMTPDebug = SMTP::DEBUG_SERVER;$mail->isSMTP();// SMTP 伺服器的設定,以及驗證資訊$mail->Host = 'smtp.gmail.com';$mail->SMTPAuth = true;// 底下也可以使用 PHPMailer::ENCRYPTION_SMTPS$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;$mail->Port = 587;// SMTP 驗證的使用者資訊$mail->Username = 'xxx@gmail.com';$mail->Password = 'xxx';// 信件內容的編碼方式,這在範例中沒有,但若不加中文會有問題$mail->CharSet = "utf-8";$mail->Encoding = "base64";// 寄信者與收信者$mail->setFrom('xxx@gmail.com', 'xxx');$mail->addAddress('xxx@abc.com', 'xxx');// 其他收信者(可不加名稱), 回信地址, CC副本, BCC 密件副本//$mail->addAddress('xxx@example.com');//$mail->addReplyTo('xxx@example', 'xxx');//$mail->addCC('cc@example.com');//$mail->addBCC('bcc@example.com');// 附件//$mail->addAttachment('/var/tmp/file.tar.gz');//$mail->addAttachment('/tmp/image.jpg', 'new.jpg');// 信件內容$mail->isHTML(true); // 若是純文字的信,此處用 false$mail->Subject = 'Here is the subject 中文標題主旨';$mail->Body = '使用中文';$mail->Body .= '<span style="color:red">紅色字</span>';$mail->send();echo '送出成功';} catch (Exception $e) {echo "送出失敗: {$mail->ErrorInfo}";}?>
舊版:下載 PHPMailer v5.2.27
PHPMailer 可在此下載
https://github.com/PHPMailer/PHPMailer/releases
因為我使用的是 PHP 5.3 ,所以選 PHPMailer 5.2.27 版。(當時以為 PHPMailer 6.X 是給 PHP 6.X 使用的)
解壓縮後,取出這四個檔案:
class.phpmailer.php
class.pop3.php
class.smtp.php
PHPMailerAutoload.php
在 php 程式碼的目錄中建一個子目錄,取名為 phpmailer,將這四個檔案放進去。
舊版:撰寫由 GMail SMTP 寄信的程式 (PHPMailer v5.2.27)
在 PHPMailer 的套件中就有不少例子,底下則是我抄自網路,修改後測試成功的程式碼。
<?php
require_once("phpmailer/PHPMailerAutoload.php");
// 產生 Mailer 實體
$mail = new PHPMailer();
// 設定為 SMTP 方式寄信
$mail->IsSMTP();
// SMTP 伺服器的設定,以及驗證資訊
$mail->Host = "smtp.gmail.com";
$mail->Port = 465;
$mail->SMTPAuth = true;
$mail->SMTPSecure = 'ssl';
// 信件內容的編碼方式
$mail->CharSet = "utf-8";
// 信件處理的編碼方式
$mail->Encoding = "base64";
// SMTP 驗證的使用者資訊
$mail->Username = "myemail@gmail.com"; //mail的帳號(需要完整的mail帳號,含@後都要填寫)
$mail->Password = "password"; //密碼
// 信件內容設定
$mail->From = "myemail@gmail.com"; //需要與上述的使用者資訊相同mail
$mail->FromName = "myemail"; //此顯示寄件者名稱
$mail->Subject = "信件標題"; //信件主旨
$mail->Body = "這是一封測是信!"; //信件內容
$mail->IsHTML(true);
// 收件人
$mail->AddAddress("someone@gmail.com", "someone"); //此為收件者的電子信箱及顯示名稱
// 顯示訊息
if(!$mail->Send()) {
echo "Mail error: " . $mail->ErrorInfo;
} else {
echo "Mail sent";
}
?>
執行後就可以用 gmail 的 smtp 主機寄信了。
有一點要注意,若是要用 gmail 的主機寄信,則 gmail 要設定成「低安全性應用程式存取權」,可在登錄 Google 帳號後,瀏覽此頁說明。
https://myaccount.google.com/lesssecureapps
撰寫由網站主機寄信的程式
上面是由 gmail smtp 寄信的測試,不過實際上還是希望由租用主機的 smtp 寄出,因此要修改一些資料。
首先要在租用的主機上建立一個信箱帳號,我以前都沒有建,因為我都直接把信件轉到 gmail,這次為了要使用主機的 smtp,因此建了帳號,設了密碼,再把上面測試的 php 程式碼修改相關的部份,所幸也測試成功了。
以上就是自行撰寫的 PHP 程式可以使用 smtp 寄信了。
在 Drupal 系統中使用 smtp 寄信
前面是介紹直接用 php 撰寫用 smtp 寄信的程式,目前遇到的問題,還有一個是需要讓 Drupal 系統使用 smtp 寄信。
在查詢的過程中,找到二個模組可以做到,分別是 SMTP Authentication Support 和 phpmailer。
模組 SMTP Authentication Support
SMTP Authentication Support 可在這裡下載。
https://www.drupal.org/project/smtp
依一般模組的安裝方式處理後,還要下載 PHPMailer 的程式,在此模組的 INSTALL.txt 有詳細說明,它是建議放在這裡
sites/all/libraries/phpmailer
我一切處理好之後,測試是失敗的。主要是出現這個訊息:
PHP Fatal error: Class 'SMTP' not found in ….class.phpmailer.php on line ….
後來也發現模組中有補丁要處理,因為懶得再深入,就打算試另一個模組。
模組 phpmailer
phpmailer 可在這裡下載。
https://www.drupal.org/project/phpmailer
我用的是 Drupal 6,所以選擇 phpmailer-6.x-3.1 版。後來發現它需要依靠 Libraries API 模組,所以我就選了不需要 Libraries 的 phpmailer-6.x-2.2 版。
這一版的 README.txt 裡面有說明,同樣要下載 PHPMailer for PHP5/6 套件,解開後將其中這二個檔案
class.phpmailer.php
class.smtp.php
放在模組本身的 phpmailer 子目錄裡面,這個子目錄在安裝模組時已經先有預先準備好了。
不過在測試時,同樣有問題,訊息也是
PHP Fatal error: Class 'SMTP' not found in ….class.phpmailer.php on line ….
查了一些資料,我猜測可能當時的 phpmailer 套件是可以用的,但我下載比較新的版本,裡面還有
PHPMailerAutoload.php
有人說這是比較好的載入點,要改成這個。
不過我也不知怎麼改,明明也有 class.smtp.php,裡面就有 'SMTP' 類別,不知為何沒有捉到?
於是我用暴力解決法,把 class.smtp.php 的內容全部複製到 class.phpmailer.php 裡面,我就不信它還找不到。
這一次果然就成功了,順利寄出測試信了。 :)
我想之前使用 SMTP Authentication Support 也是類似的問題,但已經不想回頭去測試了。
設定模組 phpmailer
我把 Drupal 設定的畫面也記錄下來。同時啟用翻譯,看起來比較親切。
這是最基本的主機設定。
這是要使用 gmail 的帳號和密碼。
設定寄件者
測試寄件
PHP 錯誤小插曲
這裡再記錄過程中遇到的二個小插曲。
is_a() 的錯誤
第一個是在測試 PHPMailer 時就發生了。錯誤訊息如下:
Strict Standards: is_a(): Deprecated. Please use the instanceof operator in ......
問題發生在這一行:
if (is_a($this->smtp, 'SMTP')) {
查到資料說,is_a 這個函數在 PHP 5.2.x 是不能用的,可以用 instanceof 來取代,也就是改成如下,注意這個用法中的 SMTP 沒有使用引號。
if ($this->smtp instanceof SMTP)) {
改過之後就可以使用了。
有人說 is_a() 有點危險,所以不再使用,但在 PHP 5.3.x 又可以用了。
goto() 的錯誤
我原本系統的 PHP 也是使用 5.2,後來想改成 5.3,但有一個新的錯誤訊息如下。
Parse error: syntax error, unexpected T_GOTO, expecting T_STRING in …
查詢後得知,在 PHP 5.3.x ,goto 這個名稱變成保留字,所以要把程式中用到使用 goto 名稱的自訂函數要改掉,這樣問題就解決了。
至此,PHP + SMTP 的技能終於解鎖成功。 ^_^
- 瀏覽次數:14693
發表新回應