PHP回顾系列目录
- PHP基础
- web请求
- cookie
- web响应
- session
- 数据库操作
- 加解密
- Composer
- 创建自己的Composer包
发送邮件是网站的常用功能,用户激活、找回密码等场景常需要发送邮件到用户邮箱。本文先回顾发送邮件的相关概念,再给出使用PHP发送邮件的示例代码。
发送短信
从功能上看,短信和邮件类似,用途常是通知和安全校验。发送短信(基本上)需要向供应商付费,所以短信供应商有动力提供清晰的文档,易用的接口方便用户接入。一般而言,发送短信的是:
- 寻找供应商,例如阿里大鱼、聚合数据等;
- 注册账户,获取appid和appkey;
- 申请模板;
- 查看接口文档,集成到应用中;
- 调用API发送短信。
流程简单易懂,接入和使用也十分便捷,基本上一两小时内就能对接和测试好。用户无需考虑讯息在通讯过程中的编码、寻址下发等细节,缺点是要付费。
邮件一般是免费服务,相关支持没那么到位,这也要理解。各种编程语言发送邮件的类库不少,从信源角度看基本可以分成两类:从本机发送和从第三方邮件服务商发送。为了理解邮件发送的流程,先介绍一些相关概念。
相关概念
大部分接触到互联网的人都有使用邮件的经验,但基本上限于邮件客户端、网页端和提供商这几个概念。作为一个开发,理解本节中的以下概念能更好的帮你掌握邮件通讯中的细节。
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加密)。
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等可能会出现在整个流程中,但不影响邮件收发的理解。下文中会提到的缩写和概念会注明,其他请自行查询。
postfix
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的邮件发送;postsuper -d ALL
,删除队列中的所有邮件。
以上介绍对于发送邮件基本已足够。注意,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的mail函数
作为PHP开发中,了解sendmail/postfix还是有点用处。mail
函数默认使用sendmail/postfix发送邮件,了解相关配置,就能知道为啥能工作/为啥不能工作。
简单来说,要让PHP自带的mail函数正常工作,需要做以下事情:
- 申请域名,在DNS解析中设置MX记录,指向本机(非合法主机(FQDN, Fully Qualified Domain Name)发送的邮件都会被当做垃圾邮件直接丢弃);
- 安装sendmail/postfix,配置软件并运行;
- 配置防火墙、安全组,放行端口。
发送效率低、非面向对象的调用方式,配置麻烦以及云服务器厂商的封锁,是使用mail
函数的最大阻碍。所以做PHP以来,本人并未直接用过mail
函数。
PHP发送邮件
发个邮件要了解这么多,会让人觉得很心累。说好的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(['qiquanji@qiquanji.com' => 'qiquanji'])
->setTo(['qiquanji1@qiquanji.com'])
->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
函数原因,最后给出了使用第三方类库发送邮件的代码示例。
感谢阅读,欢迎评论指正!
参考
- http://cn.linux.vbird.org/linux_server/0380mail.php
- http://doc.ewomail.com/ewomail/285649
- http://php.net/manual/en/function.mail.php
- https://swiftmailer.symfony.com
原文链接:https://www.qiquanji.com/post/4896.html
本站声明:网站内容来源于网络,如有侵权,请联系我们,我们将及时处理。
微信扫码关注
更新实时通知