# 盛迪嘉邮件代理服务PHP SDK **Repository Path**: lushaoming/sdjgroup-mail-proxy-sdk-php ## Basic Information - **Project Name**: 盛迪嘉邮件代理服务PHP SDK - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-12 - **Last Updated**: 2026-10-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 邮件代理服务 PHP SDK [![Latest Version on Packagist](https://img.shields.io/packagist/v/sdjgroup/mail-proxy-php.svg?style=flat-square)](https://packagist.org/packages/sdjgroup/mail-proxy-php) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) 面向**邮件代理服务**的框架无关 PHP SDK。几行代码就能把一封(或多个收件人、带附件的)邮件 交给代理服务,由服务负责入库、按收件人拆分并最终通过 SMTP 投递。 > English docs: [README.md](README.md)。 ## 为什么用它 - **几行代码发一封** —— 链式构建,或直接构造 `MailMessage` 对象。 - **多收件人、多附件** —— 服务端会为每个收件人各存一条队列记录,各自独立跟踪与投递。 - **附件处理正确** —— 以 `multipart/form-data` 上传,字段命名符合服务端要求;内存中的 附件字节会落到临时文件,无论成败都会被清理。 - **类型化异常** —— 所有失败都是 `MailException`;业务错误带上原始错误码、HTTP 状态与 `MailError` 枚举。 - **本地先行校验** —— 收件人数量、附件数量与大小在上传之前就检查,不浪费带宽。 - **零依赖** —— 不需要 Guzzle、不需要任何 PSR 包,只要 `ext-curl` 与 `ext-json`。 - **框架无关** —— 原生 PHP、Laravel、ThinkPHP、Symfony、Hyperf 都可以;HTTP 传输是可替换接口。 ## 环境要求 | 项 | 版本 | |---|---| | PHP | `>= 8.2` | | ext-curl | 任意 | | ext-json | 任意 | ## 安装 ```bash composer require sdjgroup/mail-proxy-php ``` ## 快速开始 ```php use Sdjgroup\MailProxy\MailProxyClient; $client = MailProxyClient::create( host: 'http://192.168.57.69:8083', sourceFrom: 'crm', // 调用来源,服务端用它区分调用方 ); $result = $client->mail() ->from('default_sender') // 发件人管理里登记的 key ->subject('月度账单') ->content('您好,附件是本月账单,请查收。') ->to('zhangsan@example.com', '张三') ->to('lisi@example.com') ->attachFile('/path/to/账单.pdf') ->send(); echo $result->total; // 2 —— 每个收件人一条队列记录 print_r($result->ids()); // [128, 129] —— 对应的 scheduled_mails.id ``` 接口本身**不带鉴权**(服务对服务调用),调用方靠 `source_from` 标识自己。可以在客户端上 配一次,也可以单封覆盖:`->sourceFrom('order-service')`。 ## 收件人 每个收件人都会成为一条独立队列记录、拥有独立 ID,因此发给三个人的邮件是三次可分别跟踪的投递。 ```php $client->mail() ->to('zhangsan@example.com', '张三') // 邮箱必传,名称可选 ->to('lisi@example.com') ->toMany(new Recipient('wangwu@example.com', '王五')); ``` 服务端会把相同邮箱去重(`A@x.com` 与 `a@x.com` 视为同一人),单次调用最多 200 个收件人。 ## 附件 ```php use Sdjgroup\MailProxy\Attachment; $client->mail() // …发件人、主题、正文、收件人… ->attachFile('/path/to/账单.pdf') // 本地文件 ->attachFile('/path/to/明细.xlsx', '十月明细.xlsx') // 改名上传 ->attach(Attachment::fromContents($pdfBytes, '报表.pdf', 'application/pdf')); ``` 几点说明: - 服务端**不把附件内容存进数据库**,只存**名称、大小与 md5**;文件字节存在代理服务自己的 磁盘上,投递完成后即删除。 - 单次限制:**20 个附件**、**单个 20MB**、**总计 50MB**。 - 字段名是 `attachments[0]`、`attachments[1]`…… SDK 会替你处理好,这一点很关键:服务跑在 Swow 引擎上,multipart 由它自己解析、不展开 PHP 数组语法,写成 `attachments[]` 会**静默 只保留最后一个文件**。 ## 定时发送 ```php $client->mail() // … ->scheduledAt(1750000000) // Unix 时间戳 ->at(new DateTimeImmutable('2026-10-13 09:00')) // 或直接给时间对象 ->send(); ``` 两者都不传,表示由投递进程尽快发出。 ## 代理(proxy) 邮件代理服务通常在内网,所以传输层**默认不走任何正向代理**,除非你显式配置。 这个默认值是有意为之。libcurl 会默默读取 `http_proxy` / `https_proxy` 环境变量,因此在设置 了这些变量的机器上(公司办公机、CI 流水线),一个不做处理的客户端会把请求甩进那个代理, 最终报出的连接错误与真正的原因毫无关系。显式关闭代理,才能保证任何环境下行为一致。 如果确实需要**透过**正向代理访问邮件服务: ```php $config = new Config( host: 'http://192.168.57.69:8083', sourceFrom: 'crm', proxy: 'http://10.0.0.1:3128', ); $client = new MailProxyClient($config); ``` `Config::fromArray()` 里对应 `proxy` 键。如果你自行替换了传输实现,这一条需要你 自己处理 —— 见「自定义传输」。 ## 错误处理 ```php use Sdjgroup\MailProxy\Exception\MailApiException; use Sdjgroup\MailProxy\Exception\MailException; use Sdjgroup\MailProxy\MailError; try { $client->mail()->/* … */->send(); } catch (MailApiException $e) { // 服务端返回了业务错误 $e->getErrorCode(); // 45002 $e->getMessage(); // "发件人已停用:marketing" $e->error(); // MailError::SenderUnavailable $e->isRetryable(); // false } catch (MailException $e) { // MailValidationException(参数不合法,未发出任何请求) // 或 MailHttpException(请求根本没送达) } ``` | 异常 | 触发场景 | |---|---| | `MailValidationException` | 消息不符合契约;**没有上传任何内容**,`getErrors()` 给出全部问题 | | `MailHttpException` | DNS、连接、超时、TLS 失败;请求未到达服务 | | `MailApiException` | 服务端拒绝,带上业务码与对应的 `MailError` | | 错误码 | 含义 | |---|---| | `42200` | 参数校验失败 | | `45002` | 发件人不存在或已停用 | | `45003` | 收件人数量超限 | | `45004` | 附件数量或大小超限 | | `45005` | 附件落盘失败 | | `50000` | 服务端异常(可重试) | ## 自定义传输 `HttpClientInterface` 是唯一的扩展点: ```php $client = new MailProxyClient(new Config($host, 'crm'), $myTransport); ``` 注意传输实现必须发送 `multipart/form-data`,且字段名要原样透传 —— 详见接口注释。 ## 测试 ```bash composer test # 单元测试,不联网 composer analyse # PHPStan level 8 composer cs-check # 代码风格 composer check # 以上全部 ``` 集成测试会连接真实部署,未配置环境变量时自动跳过: ```bash MAIL_PROXY_HOST=http://192.168.57.69:8083 \ MAIL_PROXY_SOURCE=crm \ MAIL_PROXY_SENDER_KEY=default_sender \ MAIL_PROXY_TO=you@example.com \ vendor/bin/phpunit --testsuite Integration ``` 注意:它**真的会把邮件排进队列**,投递端随后会发出。 ## 许可 MIT,见 [LICENSE](LICENSE)。