PHP 回顾系列目录
PHP 基础 https://tlanyan.me/php-review-php-basics/
web 请求 https://tlanyan.me/php-review-web-request/
https://tlanyan.me/php-review-cookie/
web 响应 https://tlanyan.me/php-review-web-response/
https://tlanyan.me/php-review-session/
数据库操作 https://tlanyan.me/php-review-database/
加解密 https://tlanyan.me/php-review-cipher/
Composer https://tlanyan.me/php-review-composer/
创建自己的 Composer 包 https://tlanyan.me/php-review-create-self-composer-package/
发送邮件是网站的常用功能, 用户激活, 找回密码等场景常需要发送邮件到用户邮箱. 本文先回顾发送邮件的相关概念, 再给出使用 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 命令是:
mail 或 mailx, 发送邮件. tlanyan 用户向 root 发送邮件:
mail -s "Greetings" root@localhost -r tlanyan@localhost
, 接着终端中输入 A nice day!, 然后回车, 按 ctrl+D 结束正文编辑, 邮件就已经发送出去. 登录到 root 账号, 会提示在
/var/spool/mail/root
中有新邮件. 用 tail 或者其他命令可查看邮件的详细信息.
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 http://ewomail.com/ .
了解 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(['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 函数原因, 最后给出了使用第三方类库发送邮件的代码示例.
- 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://juejin.im/post/5ad2ea05f265da238f12fd28