ThinkPHP 6.1+ 必须在 config/mail.php 中显式调用 env() 读取 SMTP 参数,因底层 symfony/mailer 仅识别该文件返回的数组结构,不自动映射 .env 中的 MAIL_* 变量;照搬 TP5 写法会导致 smtp 驱动连接失败或 500 错误。

ThinkPHP 6 项目中,.env 文件里的邮件参数**不会自动被读取生效**,必须在 config/mail.php 中显式调用 env() 函数才能加载——这是 TP6.1+ 的硬性要求,不是可选项。
为什么直接写 .env 就不生效?
TP6 底层使用的是 symfony/mailer,它只认 config/mail.php 返回的数组结构;框架不会主动扫描 MAIL_* 环境变量并映射到配置项。很多人照搬 TP5 写法,在 .env 里配了 MAIL_HOST 却没在 PHP 配置文件里读取,结果本地调试时用 log 驱动“看似能发”,一换 smtp 就报 Connection could not be established 或 500 错误。
config/mail.php 必须这样写(TP6.1+)
确保 config/mail.php 返回一个完整数组,所有 SMTP 字段都通过 env() 获取:
<?php return [
'default' => env('MAIL_DRIVER', 'smtp'),
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example App'),
],
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'host' => env('MAIL_HOST', 'smtp.qq.com'),
'port' => (int) env('MAIL_PORT', 587),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'auth_mode' => null,
],
],
];
-
env('MAIL_PORT')强制转成整数,否则某些 SMTP 扩展会拒绝字符串端口 -
MAIL_ENCRYPTION必须严格匹配:用 465 端口就填ssl,用 587 就填tls;OpenSSL 3.0+ 下混用会卡在stream_socket_enable_crypto()报错 -
MAIL_USERNAME和MAIL_PASSWORD在 QQ/163 邮箱场景下必须是授权码,不是登录密码
常见错误现象与排查点
以下问题基本都源于 config/mail.php 没写对或环境变量未加载:
- 调用
Mail::to(...)->send(...)时抛出Swift_TransportException,但错误信息里没有具体连接失败原因 → 检查MAIL_ENCRYPTION和MAIL_PORT是否成对匹配 - 本地
.env改了参数,重启服务后仍用旧值 → ThinkPHP 启动时已缓存配置,需清空runtime/cache/或执行php think clear - 生产环境用
env('MAIL_DRIVER', 'log')切换驱动但无效 → 确保MAIL_DRIVER=smtp已写入对应环境的.env,且config/mail.php中的'default'行确实用了env() - QQ 邮箱报
Failed to authenticate on SMTP server→ 不是密码错,大概率是没开 POP3/IMAP/SMTP 服务,或授权码过期
最易被忽略的一点:TP6 的 env() 函数依赖 vlucas/phpdotenv 加载机制,如果项目手动调用了 Dotenv\Dotenv::createUnsafeImmutable() 且路径不对,.env 根本没被读取——此时所有 env() 都回退默认值,连 MAIL_DRIVER 都可能变成 smtp 导致 log 驱动失效。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











