- 后端
- Web框架
【免费下载链接】symfony
The Symfony PHP framework
Mailjet Notifier Bridge(symfony/mailjet-notifier)是 Symfony Notifier 组件为 Mailjet 为骨架,完整讲解 DSN 的构造规则(FROM与AUTH_TOKEN两个核心参数),并深入 MailjetTransport.php、MailjetTransportFactory.php 及配套测试,还原短信从SmsMessage到 Mailjetv4/sms-sendAPI 的完整调用链。读完本文,你将能在自己的 Symfony 项目中独立配置、发送并通过测试验证 Mailjet 短信通道。
概览:这个 Bridge 解决什么问题
Symfony Notifier 是一个与 Mailer 平行的消息通知组件,它把「通知发送」抽象为统一的Notifier接口,通过 DSN(Data Source Name)字符串驱动不同的第三方服务。Mailjet Bridge 就是其中专门负责 Mailjet SMS 的那一块拼图:
- 它以
mailjet://为 scheme,通过一个 DSN 即可完成鉴权与发件人配置; - 内部封装了对 Mailjet SMS API(
POST /v4/sms-send)的 HTTP 调用、鉴权头注入、响应状态校验与错误翻译; - 与组件内其他 SMS 通道(如 Twilio、Vonage 等)保持一致的 API 形态:面向
SmsMessage,返回SentMessage。
从 composer.json 可以看到,本 Bridge 的依赖为symfony/http-client(^7.4|^8.0)与symfony/notifier(^8.2),并要求 PHP >= 8.4.1。
安装 Bridge
在项目根目录执行 Composer 安装命令:
composer require symfony/mailjet-notifier安装完成后,Symfony 的 Notifier 组件会自动发现该 Bridge(其 composer 类型为symfony-notifier-bridge,且MailjetTransportFactory实现自TransportFactoryInterface的自动发现机制)。如果你的项目没有使用 Symfony Flex 自动配置,需要确保 Notifier 组件已正确注册 transport 工厂。
DSN 配置:FROM 与 AUTH_TOKEN 两个关键参数
原文档给出的 DSN 示例是接入 Mailjet 短信的唯一入口,格式如下:
MAILJET_DSN=mailjet://FROM:AUTH_TOKEN@default其中:
AUTH_TOKEN:你的 Mailjet SMS 鉴权令牌(auth token),对应 DSN 中的密码位(password);FROM:字母数字组成的发件人 ID(alphanumeric sender ID),对应 DSN 中的用户名位(user);@default:主机占位。default是 Notifier 组件约定俗成的占位主机,工厂解析时会把default视作「未指定主机」,从而回落到 Bridge 内置的默认主机(见下文源码分析)。
在.env中配置好后,直接使用即可:
use Symfony\Component\Notifier\Notifier; use Symfony\Component\Notifier\Recipient\SmsRecipient; $notifier = new Notifier(...); // 由容器注入 $notifier->send(new SmsMessage('+33612345678', 'Hello Mailjet!'));DSN 的解析规则(源码视角)
mailjet://FROM:AUTH_TOKEN@default由 Notifier 组件的 Dsn.php 解析。它本质上是对parse_url()结果的结构化封装:
scheme→mailjet(工厂用它判断是否支持该通道);user→FROM(发件人 ID);pass→AUTH_TOKEN(鉴权令牌,构造时以#[\SensitiveParameter]标记防止敏感信息泄露到异常堆栈);host→default(占位符);- query 部分 → 可选 DSN 选项(例如
?ssl=0)。
在 MailjetTransportFactory.php 的create()方法中,取值逻辑与底层 AbstractTransportFactory 严格对应:
$authToken = $this->getPassword($dsn); $from = $this->getUser($dsn); $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); $port = $dsn->getPort(); return (new MailjetTransport($authToken, $from, $this->client, $this->dispatcher)) ->setHost($host)->setPort($port)->setSsl($this->getSsl($dsn));要点拆解:
getUser()/getPassword()在字段缺失时会抛出IncompleteDsnException。测试 MailjetTransportFactoryTest.php 中incompleteDsnProvider正好验证了这一行为:DSNmailjet://authtoken@default(缺少 FROM,即用户位)会报Invalid "mailjet://authtoken@default" notifier DSN: Password is not set.。注意此处「Password」异常信息来自底层getUser()的提示文案,实际含义是用户位缺失。- 只有 scheme 为
mailjet才会被接受,否则抛出UnsupportedSchemeException(getSupportedSchemes()返回['mailjet'])。 setSsl($this->getSsl($dsn))支持通过?ssl=DSN 选项控制是否走 HTTPS,详见下文。
可选 DSN 选项:ssl
自 8.2 版本起(见 CHANGELOG.md),该 Bridge 支持sslDSN 选项,允许在需要时通过明文 HTTP 发送请求:
MAILJET_DSN=mailjet://FROM:AUTH_TOKEN@default?ssl=0- 默认(未指定
ssl时):走 HTTPS,即getHttpScheme()返回https; ssl=0:走明文 HTTP。
其取值由 AbstractTransportFactory 的getSsl()通过$dsn->getBooleanOption('ssl')解析,支持布尔语义的字符串(0/1/true/false等)。
发送原理:MailjetTransport 源码剖析
MailjetTransport.php 是本 Bridge 的核心发送类,继承自AbstractTransport。它定义了几件关键事:
1. 默认主机与端点
protected const HOST = 'api.mailjet.com';默认 API 主机为api.mailjet.com。当 DSN 中 host 为default时,工厂不调用setHost(),Transport 会回落到该常量。__toString()返回mailjet://{from}@{host}形式,便于日志与调试中还原该 transport 的身份。
2. 支持的消息类型
public function supports(MessageInterface $message): bool { return $message instanceof SmsMessage; }该通道只支持SmsMessage。在doSend()中,如果传入的不是SmsMessage,会抛出UnsupportedMessageTypeException。测试 MailjetTransportTest.php 的supportedMessagesProvider/unsupportedMessagesProvider分别验证了SmsMessage被接受、ChatMessage与自定义DummyMessage被拒绝的行为。
3. HTTP 请求的组装(v4/sms-send)
$endpoint = \sprintf('%s://%s/v4/sms-send', $this->getHttpScheme(), $this->getEndpoint()); $response = $this->client->request('POST', $endpoint, [ 'auth_bearer' => $this->authToken, 'json' => [ 'From' => $message->getFrom() ?: $this->from, 'To' => $message->getPhone(), 'Text' => $message->getSubject(), ], ]);对应到 Mailjet SMS API:
- 请求方法/路径:
POST https://api.mailjet.com/v4/sms-send(或自定义主机 +ssl=0时的 HTTP 版本); - 鉴权:通过
auth_bearer注入AUTH_TOKEN(Bearer Token 认证); - 请求体:
From:发件人 ID,优先取SmsMessage上显式设置的from,否则回落到 DSN 中的FROM($message->getFrom() ?: $this->from)。这一行为对应 CHANGELOG 6.2 的变更 "UseSmsMessage->fromwhen defined";To:收件人手机号,来自$message->getPhone();Text:短信正文,来自$message->getSubject()。
4. 响应校验与错误翻译
try { $statusCode = $response->getStatusCode(); } catch (TransportExceptionInterface $e) { throw new TransportException('Could not reach the remote Mailjet server.', $response, 0, $e); } if (200 !== $statusCode) { $content = $response->toArray(false); $errorMessage = $content['requestError']['serviceException']['messageId'] ?? ''; $errorInfo = $content['requestError']['serviceException']['text'] ?? ''; throw new TransportException(\sprintf('Unable to send the SMS: '.$errorMessage.' (%s).', $errorInfo), $response); } return new SentMessage($message, (string) $this);- 网络层失败(无法连接 Mailjet 服务器)被统一包装为
TransportException('Could not reach the remote Mailjet server.'); - 非 200 响应:解析 Mailjet 返回的
requestError.serviceException结构,将messageId与text拼装成可读的错误信息后抛出; - 发送成功:返回
SentMessage,其 transport 标识为(string) $this(即mailjet://FROM@host)。
5. 一个完整的发送示例
use Symfony\Component\Notifier\Bridge\Mailjet\MailjetTransport; use Symfony\Component\Notifier\Message\SmsMessage; use Symfony\Component\HttpClient\HttpClient; $transport = new MailjetTransport( authToken: 'YOUR_AUTH_TOKEN', from: 'YOUR_SENDER_ID', client: HttpClient::create(), ); $message = (new SmsMessage('+33612345678', 'Verification code: 123456')) ->from('YOUR_SENDER_ID'); // 可选:覆盖 DSN 中的发件人 $sent = $transport->send($message);构造函数签名(authToken、from、可选的HttpClientInterface $client与EventDispatcherInterface $dispatcher)可直接使用;实际应用中通常不直接实例化 Transport,而是通过 Notifier 组件按 DSN 自动创建。
结合 Symfony 框架的完整用法
在启用 Notifier 组件的 Symfony 应用中(framework.notifier配置启用后),只需:
- 在
.env中写入 DSN:
MAILJET_DSN=mailjet://FROM:AUTH_TOKEN@default- 在服务中注入
NotifierInterface并发送短信:
use Symfony\Component\Notifier\NotifierInterface; use Symfony\Component\Notifier\Message\SmsMessage; use Symfony\Component\Notifier\Recipient\SmsRecipient; final class SmsService { public function __construct(private NotifierInterface $notifier) { } public function sendOtp(string $phone, string $code): void { $message = new SmsMessage($phone, sprintf('Your OTP is %s', $code)); $recipient = new SmsRecipient($phone); $this->notifier->send($message, $recipient); } }Notifier会根据 DSN 的 scheme 自动匹配MailjetTransportFactory并创建 transport(supports()依据 AbstractTransportFactory 的 scheme 匹配实现)。若 DSN 写错 scheme、缺少 FROM 或 AUTH_TOKEN,均会在创建阶段即抛出异常,方便快速定位配置问题。
测试验证:Factory 与 Transport 的双层保障
仓库中 Tests 目录提供了两套测试,可作为集成验证与二次开发的参照:
- MailjetTransportFactoryTest.php:继承
AbstractTransportFactoryTestCase,验证工厂的 DSN 创建、scheme 支持矩阵、缺参报错与不支持的 scheme。例如createProvider中mailjet://Mailjet@host.test(缺 token 的显示形态)与完整 DSN 的映射关系;supportsProvider断言mailjet://Mailjet:authtoken@default被支持、somethingElse://不被支持。 - MailjetTransportTest.php:继承
TransportTestCase,使用MockHttpClient在无真实网络条件下验证 transport 的__toString()输出(mailjet://Mailjet@host.test)、支持/不支持的消息类型矩阵。
版本演进速览
根据 CHANGELOG.md,该 Bridge 的关键演进:
- 5.4:新增本 Bridge(
Add the bridge); - 6.2:当
SmsMessage定义了from时优先使用之(Use SmsMessage->from when defined); - 8.2:新增
sslDSN 选项,允许通过明文 HTTP 发送请求(Add the ssl DSN option to send requests over plain HTTP)。
这解释了 DSN 中 FROM 的取值优先级与?ssl=选项的来源:前者由SmsMessage->getFrom() ?: $this->from实现,后者由工厂的setSsl($this->getSsl($dsn))落地。
小结
Mailjet Notifier Bridge 用极简的 DSN 完成了「鉴权 + 发件人 + 主机」三项配置:
FROM(user 位)与AUTH_TOKEN(password 位)缺一不可,缺失时工厂直接抛错;- 发送统一走
POST /v4/sms-send,Bearer Token 鉴权,请求体为{From, To, Text}; SmsMessage的from可覆盖 DSN 中的发件人 ID;?ssl=0可切换明文 HTTP(8.2+);- 成功返回
SentMessage,失败统一抛出携带 Mailjet 错误详情的TransportException。
相关实现与测试均可在当前仓库中直接查阅:MailjetTransport.php、MailjetTransportFactory.php、MailjetTransportFactoryTest.php、MailjetTransportTest.php,以及 Notifier 组件底层的 Dsn.php 与 AbstractTransportFactory.php。
- 后端
- Web框架
【免费下载链接】symfony
The Symfony PHP framework
相关推荐
QuickRecorder 教程:5 分钟用好这款不足 10MB 的 macOS 录屏工具,免虚拟声卡内录系统音
QuickRecorder 教程:5 分钟用好这款不足 10MB 的 macOS 录屏工具,免虚拟声卡内录系统音 QuickRecorder 是一款轻量 mac
后端Web框架Dagger TypeScript SDK 中 FunctionCallArgValueID 类型别名解析:对象唯一标识符的声明与实现
Dagger TypeScript SDK 中 FunctionCallArgValueID 类型别名解析:对象唯一标识符的声明与实现 FunctionCall
后端Web框架Symfony Notifier 接入 Contact Everyone 短信服务:DSN 配置、消息选项与发送原理
Symfony Notifier 接入 Contact Everyone 短信服务:DSN 配置、消息选项与发送原理 本指南基于 Symfony 官方仓库中的
后端Web框架
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考