ThinkPHP 6.0 官方不内置邮件组件,必须手动集成 PHPMailer;config/mail.php 是唯一被框架默认加载的配置入口,需显式调用 env() 读取 .env 变量,否则配置无效;PHPMailer 实例必须每次新建,不可复用或单例化,避免状态污染;QQ/163 邮箱须用授权码、匹配端口与 SSL/TLS 模式;中文主题和附件名需 MIME 编码,非仅设置 CharSet。

ThinkPHP 6.0 官方不提供内置邮件发送能力,必须手动集成 PHPMailer;直接写 new \PHPMailer\PHPMailer\PHPMailer() 而不隔离配置和生命周期,90% 的线上发信失败都源于此。
为什么 config/mail.php 必须存在且不能只靠 .env
TP6 的邮件逻辑不自动读取 .env 中的 MAIL_HOST 等变量——框架没做这层映射。你得在 config/mail.php 里显式调用 env() 或直接写死(不推荐)。
- 正确写法示例:
'host' => env('MAIL_HOST', 'smtp.qq.com'),'password' => env('MAIL_PASSWORD', '') - 如果只改了
.env却没在config/mail.php里用env()读取,配置完全不生效 -
config/mail.php是唯一被框架服务类、封装层、队列任务默认加载的配置入口,漏掉它等于没配
PHPMailer 实例必须每次新建,不能复用或单例化
PHPMailer 内部维护 SMTP 连接状态、附件缓存、收件人列表等可变属性。复用同一个实例发多封邮件,会导致附件叠加、收件人错乱、SSL 上下文冲突。
- 发一封邮件就
new \PHPMailer\PHPMailer\PHPMailer(true)一次,true启用异常模式 - 别在服务类里用
private static $instance缓存它,也别在app/provider.php中绑定为单例 - 异步任务(如
think-queue)中尤其要注意:每个 job 必须独立 new,否则上一个 job 的附件可能出现在下一个里
QQ 邮箱/163 邮箱发信失败的三大硬坑
不是代码问题,是平台策略+配置组合导致的静默失败。调试时先看这些:
立即学习“PHP免费学习笔记(深入)”;
- QQ 邮箱必须开启「POP3/IMAP/SMTP 服务」,并用「授权码」而非登录密码——填错密码报
SMTP Error: Could not authenticate,但提示语不会告诉你“你该去邮箱设置里开授权码” - 端口与
SMTPSecure必须严格匹配:465 → 'ssl',587 → 'tls';填成'ssl'+587会卡在连接阶段,无明确报错 - 云服务器(阿里云/腾讯云轻量)默认封禁
25端口,且部分地区出口 IP 被 Gmail/Outlook 拉黑;优先换用腾讯企业邮(smtp.exmail.qq.com:465)或网易邮箱大师(smtp.qiye.163.com:465)
中文附件名和 HTML 主题乱码的修复点
$mail->CharSet = 'UTF-8' 只影响正文,对附件名、邮件主题无效。这两处乱码要单独处理:
- 主题编码:用
"=?UTF-8?B?" . base64_encode($subject) . "?="包裹,别依赖$mail->Subject = $subject直接赋值 - 附件名编码:必须用
mb_encode_mimeheader($filename, 'UTF-8', 'B'),且确保 PHP 版本 ≥ 8.0、mbstring扩展已启用 - HTML 正文里含中文时,
$mail->isHTML(true)后还得设$mail->msgHTML($html),别用$mail->Body = $html直接赋值,否则内联样式和图片引用易失效
最麻烦的不是连不上 SMTP,而是 DNS 解析失败(Docker 容器里没配 resolv.conf)、时区不一致导致 SSL 证书校验失败、或 OpenSSL 版本太低不支持 TLS 1.2 —— 这些没法靠改几行 PHP 解决,得查运行环境本身。



















