转载请注明文章出处: 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
函数缘由,最后给出了使用第三方类库发送邮件的代码示例。
感谢阅读,欢迎评论指正!