轉載請註明文章出處: tlanyan.me/php-review-…php
發送郵件是網站的經常使用功能,用戶激活、找回密碼等場景常須要發送郵件到用戶郵箱。本文先回顧發送郵件的相關概念,再給出使用PHP發送郵件的示例代碼。linux
從功能上看,短信和郵件相似,用途常是通知和安全校驗。發送短信(基本上)須要向供應商付費,因此短信供應商有動力提供清晰的文檔,易用的接口方便用戶接入。通常而言,發送短信的是:web
流程簡單易懂,接入和使用也十分便捷,基本上一兩小時內就能對接和測試好。用戶無需考慮訊息在通信過程當中的編碼、尋址下發等細節,缺點是要付費。數據庫
郵件通常是免費服務,相關支持沒那麼到位,這也要理解。各類編程語言發送郵件的類庫很多,從信源角度看基本能夠分紅兩類:從本機發送和從第三方郵件服務商發送。爲了理解郵件發送的流程,先介紹一些相關概念。編程
大部分接觸到互聯網的人都有使用郵件的經驗,但基本上限於郵件客戶端、網頁端和提供商這幾個概念。做爲一個開發,理解本節中的如下概念能更好的幫你掌握郵件通信中的細節。swift
MUA : Mail User Agent,郵件用戶代理。用戶代理是開發中常常接觸到的詞,主要指 理解人的意圖並表明用戶向資源方請求的工具。例如瀏覽器是最經常使用的用戶代理,以HTTP/HTTPS協議格式向web服務器發送請求,並解析響應,渲染後呈現給用戶。郵件用戶代理,常見的是Foxmail、Outlook這類工具,人們寫好郵件後,按格式封裝郵件內容與郵件服務器通信。瀏覽器
MTA : Mail Transfer Agent,郵件傳輸代理,幫用戶收發郵件的程序。常說的郵件服務器指的就是MTA,開源的程序有sendmail,postfix,QMail等。安全
MRA : Mail Retrieval Agent,郵件收取代理,將用戶的郵件從郵件服務器取回本地。郵件客戶端是常見的MRA。服務器
SMTP : Simple Mail Transfer Protocol,簡單郵件傳輸協議。用戶與郵件服務器、郵件服務器互相傳遞郵件均使用該協議(默認明文,可以使用SSL\TLS加密)。cookie
POP3/IMAP : Post Office Protocol version 3/Internet Message Access Protocol,郵局協議版本3或網絡信息獲取協議,客戶端從服務端獲取郵件時使用的協議。
用戶A(163郵箱)向用戶B(Gmail郵箱)發信,用戶B獲取信件的過程涉及到上述的概念。流程和概念關係可用以下簡圖表示:
用戶A --發送郵件--> 用戶B
M|S M|I
U|M R|M
A|T A|A
|P |P
v v
MTA(163)--轉發(SMTP)->MTA(gmail)
複製代碼
注:上圖給出的是郵件發送的大致流程,其餘MSA、MDA、ESMTP、SMTPS等可能會出如今整個流程中,但不影響郵件收發的理解。下文中會提到的縮寫和概念會註明,其餘請自行查詢。
Linux下發送郵件的軟件主要是sendmail和postfix,它們在系統中充當上文概念中的MTA/MDA(Mail Delivery Agent,郵件投遞代理)角色。它幫助用戶向外發送郵件,接收郵件投遞到用戶信箱(默認位置/var/spool/mail/用戶名)。
sendmail是老牌的郵件軟件,知名度很是高。可是Wietse(Wietse Zweitze Venema)用的不爽,因而有了postfix。postfix命令(幾乎)兼容於sendmail,但更高效和安全(後綴fix的由來),是目前大部分Linux發行版的默認郵件收發軟件,推薦使用postfix而非sendmail(本博客多年前有篇文章寫如何配置sendmail,那時年少無知見識少,打算抽空把那篇文章改一下)。
postfix的主要配置文件是/etc/postfix/main.cf
,配置文件的註釋很是全,選項基本是自解釋的。最重要的幾個配置是:myhostname
、myorigin
、inet_interfaces
、inet_protocols
以及mydestination
(若是你打算收外網來信的話)。須要注意inet_interfaces
配置爲localhost
時,inet_protocols
的值應爲ipv4,不然可能會出現相似postfix: fatal: parameter inet_interfaces: no local interface found for ::1
的錯誤提示。
與郵件相關的幾個經常使用postfix命令是:
postquque
,查看郵件發送隊列。postqueue -p
可取代sendmail
中的mailq
命令,postqueue -f
刷新隊列(強制嘗試發送隊列中的郵件)。
postcat
,查看未發送郵件的信息。例如postcat -q xxxx
(xxxx是postqueue或者mailq顯示的未發送隊列ID)可查看郵件的詳細信息,postcat -b -q xxxxx
只查看郵件正文。
postsuper
,超級用戶纔可以使用的郵件管理程序。postsuper -d xxxx
,刪除隊列ID爲xxxxx的郵件;postsuper -h xxxxx
,暫停隊列ID爲xxxx的郵件發送,等。
以上介紹對於發送郵件基本已足夠。注意,mail命令發送的郵件能投遞的前提是postfix
正在運行(ps aux | grep postfix | grep -v grep輸出不爲空)。
有了postfix,配置好後能夠對外發送郵件,也能收取外網發送過來的郵件,但限於命令行操做。想用foxmail等客戶端收發郵件,須要讓服務器支持POP3/IMAP協議。開源的dovecot能夠實現這個功能。dovecot服務於收郵件而非發送,瞭解其對開發中的幫助不大。若是想搭建一套完整的郵件系統(包括網頁端支持、垃圾郵件過濾、病毒查殺、傳輸加密等),建議參考或使用國產開源的 EwoMail。
瞭解postfix對開發中發送郵件幫助有多大?說實話,幾乎沒有幫助。緣由是爲了防止垃圾郵件氾濫,各大雲服務器廠商屏蔽了25端口(Google Cloud連465都幹掉了)。亞馬遜雲經過申請還有放行的可能(但有速率和每日額度限制),其餘廠商幾乎不會讓你使用本身的域名從本機直接發送郵件。封禁25端口,必須使用第三方的郵件服務,幾乎是業界的標準作法。
聰明的人可能想到,使用465加密端口(基於SMTPS,SMTP over SSL協議)或587端口(SMTP over STARTTLS協議)發送郵件,是否是就能繞開限制了?阿里雲/騰訊雲等廠商並不封禁465端口,發送郵件可使用該端口而無需申請。但注意465和587端口是客戶端和郵件服務器通信使用的端口,郵件服務器之間通信使用25端口。你能夠經過465端口鏈接到Gmail郵箱對外發送郵件,但沒法讓postfix使用465端口投遞郵件到hotmail郵件服務器。
總結來講,sendmail/postfix做爲垃圾和欺詐郵件氾濫前的郵件服務器軟件,對業界貢獻很大。隨着雲服務器的盛行,幾乎沒法以指向本機的域名向外發送郵件,sendmail/postfix除了在本機內發送提醒郵件,用處已然不大。要對外發送郵件,要麼自建機房,要麼使用第三方郵件系統。
做爲PHP開發中,瞭解sendmail/postfix仍是有點用處。mail
函數默認使用sendmail/postfix發送郵件,瞭解相關配置,就能知道爲啥能工做/爲啥不能工做。
簡單來講,要讓PHP自帶的mail函數正常工做,須要作如下事情:
發送效率低、非面向對象的調用方式,配置麻煩以及雲服務器廠商的封鎖,是使用mail
函數的最大阻礙。因此作PHP以來,本人並未直接用過mail
函數。
發個郵件要了解這麼多,會讓人以爲很心累。說好的PHP是最好的語言呢?
PHP發送郵件也能夠很簡單,推薦方式就是使用Swift Mailer
或PHPMailer
等類庫。引入這些類庫後,註冊第三方郵箱(好比Gmail、QQ等),填好用戶名密碼,配置好STMP地址和端口,就能像發送短信同樣輕鬆發送郵件。固然這些類庫也支持使用sendmail/postfix發送郵件,但我想你不會再這樣作了。
以Swift Mailer
爲例,直接上代碼說明使用PHP發送郵件也是一個很是簡單的事情!
首先,在項目中引入Swift Mailer
:
composer require "swiftmailer/swiftmailer:^6.0"
複製代碼
而後準備好郵件內容(以文本文件爲例,不帶附件):
$message = (new Swift_Message('Test Message'))
->setFrom(['tlanyan@tlanyan.me' => 'tlanyan'])
->setTo(['tlanyan1@tlanyan.me'])
->setBody('Hello, this is a test mail from Swift Mailer!');
複製代碼
接着,設置好郵件傳輸方式(使用Gmail郵箱):
$transport = (new Swift_SmtpTransport('smtp.gmail.com', 465, 'ssl'))
->setUsername('username')
->setPassword('password');
複製代碼
或者使用sendmail/postfix的方式(不推薦):
$transport = (new Swift_SendmailTransport());
複製代碼
最後,使用transport
構造mailer
實例,發送郵件:
$mailer = new Swift_Mailer($transport);
$result = $mailer->send($message);
複製代碼
老闆不再用擔憂發送郵件收不到了,So easy!
本文先回顧了發送郵件的相關概念,說明不推薦使用內置的mail
函數緣由,最後給出了使用第三方類庫發送郵件的代碼示例。
感謝閱讀,歡迎評論指正!