easyswoole / smtp
EasySwoole mailer component
Requires
- php: >=8.1.0
- ext-mbstring: *
- ext-swoole: >=5.1
Requires (Dev)
- easyswoole/swoole-ide-helper: ^1.2
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
基于 Swoole 协程客户端的轻量 SMTP 邮件发送组件。
支持:
- 纯文本和 HTML 邮件
- To、Cc、Bcc 和 Reply-To
- 单个或多个附件、自定义附件名
- UTF-8 主题、正文与附件名
- SSL/TLS 直连
- 返回状态码或抛出异常两种错误处理模式
环境要求
- PHP 8.1 或更高版本
- Swoole 5.1 或更高版本
- mbstring 扩展
- Composer
检查运行环境:
php -v php -m | grep -E 'swoole|mbstring'
安装
composer require easyswoole/smtp
快速开始
Mailer 内部使用 Swoole\Coroutine\Client,因此 send() 必须在 Swoole 协程环境中调用。
<?php require __DIR__ . '/vendor/autoload.php'; use EasySwoole\Smtp\Mailer; use EasySwoole\Smtp\Request\Text; use Swoole\Coroutine; $mailer = new Mailer(true); $mailer->setHost('smtp.example.com'); $mailer->setPort(465); $mailer->setSsl(true); $mailer->setUsername('account@example.com'); $mailer->setPassword('smtp-password'); $mailer->setFrom('account@example.com', '系统通知'); $mailer->addAddress('user@example.com', '收件人'); $message = new Text(); $message->setSubject('测试邮件'); $message->setBody('这是一封测试邮件。'); Coroutine\run(function () use ($mailer, $message): void { $mailer->send($message); });
在 EasySwoole 等已经运行于协程环境的框架中,直接调用 $mailer->send($message) 即可,不要再次嵌套 Coroutine\run()。
完整示例
下面的例子展示 HTML、多个收件人、抄送、密送、回复地址和多个附件:
<?php require __DIR__ . '/vendor/autoload.php'; use EasySwoole\Smtp\Mailer; use EasySwoole\Smtp\Request\Html; use Swoole\Coroutine; $mailer = new Mailer(true); $mailer->setHost('smtp.example.com'); $mailer->setPort(465); $mailer->setSsl(true); $mailer->setUsername('account@example.com'); $mailer->setPassword('smtp-password'); $mailer->setTimeout(10); $mailer->setFrom('sender@example.com', '订单系统'); $mailer->addAddress('customer@example.com', '客户'); $mailer->addAddress('other@example.com'); $mailer->addCc('sales@example.com', '销售团队'); $mailer->addBcc('audit@example.com'); $mailer->setReplyTo('support@example.com', '客服'); $message = new Html(); $message->setSubject('订单已创建'); $message->setBody('<h1>订单创建成功</h1><p>订单详情请查看附件。</p>'); $message->addAttachment('/absolute/path/order.pdf'); $message->addAttachment('/absolute/path/items.csv', '订单明细.csv'); Coroutine\run(function () use ($mailer, $message): void { $response = $mailer->send($message); echo $response->getMsg(); // ok. });
邮件内容
纯文本邮件
use EasySwoole\Smtp\Request\Text; $message = new Text(); $message->setSubject('纯文本主题'); $message->setBody("第一行\n第二行");
HTML 邮件
use EasySwoole\Smtp\Request\Html; $message = new Html(); $message->setSubject('HTML 主题'); $message->setBody('<h1>Hello</h1><p>邮件正文</p>');
默认字符集为 UTF-8。需要其他字符集时,可以调用:
$message->setCharset('ISO-8859-1');
附件
// 使用源文件名 $message->addAttachment('/absolute/path/report.pdf'); // 指定邮件中显示的文件名 $message->addAttachment('/absolute/path/data.csv', '统计数据.csv'); // 可以继续添加更多附件 $message->addAttachment('/absolute/path/image.png', '截图.png');
附件说明:
- 必须传入本机可读文件的路径,建议使用绝对路径。
- 第二个参数只影响邮件中显示的文件名,不会修改源文件。
- 二进制附件自动使用 Base64 编码。
- MIME 类型根据源文件扩展名识别,无法识别时使用
application/octet-stream。 - 支持空文件。
- 文件不存在、不是普通文件或不可读时,会抛出
EasySwoole\Smtp\Exception\Exception。
收件人与邮件头
$mailer->setFrom('sender@example.com', '发件人'); $mailer->addAddress('to@example.com', '主收件人'); $mailer->addCc('cc@example.com', '抄送人'); $mailer->addBcc('bcc@example.com', '密送人'); $mailer->setReplyTo('reply@example.com', '回复联系人');
addAddress()、addCc()和addBcc()都可以调用多次。- 未调用
setFrom()时,SMTP 登录用户名会作为发件地址。 - Bcc 地址只参与 SMTP 投递,不会写入邮件头,因此其他收件人不可见。
- 未设置 To、但设置了 Cc 或 Bcc 时,To 邮件头会使用
undisclosed-recipients:;。
Mailer 配置
| 方法 | 默认值 | 说明 |
|---|---|---|
new Mailer(bool $enableException = false) |
false |
是否在 SMTP 阶段失败时抛出异常 |
setHost(string $host) |
空 | SMTP 服务器地址,发送前必须设置 |
setPort(int $port) |
465 |
SMTP 服务器端口 |
setSsl(bool $ssl) |
true |
是否使用 SSL/TLS 直连 |
setUsername(string $username) |
空 | SMTP 登录用户名 |
setPassword(string $password) |
空 | SMTP 密码或服务商授权码 |
setFrom(string $address, string $name = '') |
登录用户名 | 设置 envelope sender 和 From 邮件头 |
addAddress(string $address, string $name = '') |
— | 添加 To 收件人 |
addCc(string $address, string $name = '') |
— | 添加 Cc 收件人 |
addBcc(string $address, string $name = '') |
— | 添加 Bcc 收件人 |
setReplyTo(string $address, string $name = '') |
未设置 | 设置 Reply-To 邮件头 |
setTimeout(int $seconds) |
5 |
连接及响应等待时间 |
setMaxPackage(int $bytes) |
2097152 |
Swoole 客户端接收包上限,不是附件大小限制 |
setEnableException(bool $enabled) |
false |
动态切换异常模式 |
SMTP 兼容性
当前实现使用:
EHLOAUTH LOGINMAIL FROM/RCPT TO/DATA- SSL/TLS 直连
常见的 465 端口通常使用 setSsl(true)。若服务商提供普通明文端口,可使用 setSsl(false)。
当前版本不实现 STARTTLS 升级,也不支持 OAuth 或其他 SASL 认证机制。使用 587 等端口前,请确认服务商是否要求 STARTTLS;如果强制要求,则当前版本不能直接连接。
错误处理
返回响应对象
默认模式不会因 SMTP 状态错误抛出异常,而是返回 EasySwoole\Smtp\Protocol\Response:
use EasySwoole\Smtp\Protocol\Response; $mailer = new Mailer(); $response = $mailer->send($message); if ($response->getStatus() !== Response::STATUS_OK) { printf("发送失败:[%d] %s\n", $response->getStatus(), $response->getMsg()); }
异常模式
use EasySwoole\Smtp\Exception\Exception; $mailer = new Mailer(true); try { $mailer->send($message); } catch (Exception $exception) { echo '发送失败:' . $exception->getMessage(); }
异常模式只改变 SMTP 阶段错误的返回方式。附件文件不可读等消息构建错误始终会抛出异常。
状态码
| 状态码 | 常量 | 含义 |
|---|---|---|
0 |
STATUS_OK |
发送成功 |
101 |
STATUS_CONNECT_TIMEOUT |
连接失败或超时 |
102 |
STATUS_NOT_SMTP_PROTOCOL |
未收到有效的 SMTP 欢迎响应 |
103 |
STATUS_RECEIVE_TIMEOUT |
接收服务器响应超时 |
201 |
STATUS_IDENTIFY_SENDER_ERROR |
EHLO 阶段失败 |
202 |
STATUS_AUTH_MODE_ERROR |
服务端不支持当前认证模式 |
203 |
STATUS_USERNAME_ERROR |
用户名认证失败 |
204 |
STATUS_PASSWORD_ERROR |
密码认证失败 |
205 |
STATUS_FROM_MAIL_ERROR |
发件人地址被拒绝 |
206 |
STATUS_RCPT_MAIL_ERROR |
收件人地址被拒绝 |
207 |
STATUS_DATA_START_ERROR |
DATA 命令被拒绝 |
208 |
STATUS_DATA_END_ERROR |
邮件内容提交失败 |
209 |
STATUS_QUIT_ERROR |
SMTP 会话退出失败 |
常见问题
API must be called in the coroutine
说明 send() 在非协程环境中执行。CLI 脚本可用 Coroutine\run() 包裹;EasySwoole 请求处理器等已有协程环境中应直接调用。
连接 587 端口失败
许多服务商的 587 端口要求 STARTTLS,而当前组件仅支持 SSL/TLS 直连或普通连接。请使用服务商提供的 SSL 直连端口,通常是 465。
登录密码正确但认证失败
部分邮箱要求使用单独生成的 SMTP 授权码,而不是网页登录密码。请同时确认服务商已启用 SMTP 服务且支持 AUTH LOGIN。
附件发送失败
确认文件使用绝对路径、进程对文件有读取权限,并检查运行用户与 CLI 用户是否一致。
开发与测试
安装开发依赖并运行测试:
composer install
composer test
也可以直接运行:
vendor/bin/phpunit vendor/bin/phpunit tests/SmtpIntegrationTest.php
测试套件基于 PHPUnit 10,包括:
- 内容编码与 MIME 类型单元测试
- 纯文本、HTML、附件和空附件测试
- SMTP 命令、邮件头、响应与异常测试
- 本地 Swoole 协程 Mock SMTP 集成测试
Mock SMTP 测试会验证 EHLO、AUTH LOGIN、发件人、To/Cc/Bcc、DATA、附件编码、dot-stuffing 和 QUIT 的完整流程。它只监听本机随机端口,不连接真实 SMTP 服务器,也不会向外发送邮件。
License
Apache-2.0