- 后端
- Web框架
【免费下载链接】symfony
The Symfony PHP framework
GatewayApi(gatewayapi.com)是一个支持国际短信发送的 API 服务。本文围绕本仓库中 GatewayApi Notifier Bridge 的 CHANGELOG 展开,系统梳理该桥接器从 5.3 引入至今的核心演进:DSN 配置、GatewayApiOptions消息选项、SmsMessage发件人解析以及 8.2 新增的sslDSN 选项。读完本文,你将掌握在 Symfony 中通过gatewayapi://方案发送短信的完整配置方法、消息级自定义选项的用法,以及桥接器底层请求构造与错误处理的源码原理。
桥接器概览与演进脉络
GatewayApi 是 Symfony Notifier 组件提供的一个短信桥接器(notifier bridge),对应仓库路径 src/Symfony/Component/Notifier/Bridge/GatewayApi,包名为symfony/gateway-api-notifier(见 composer.json),核心类包括:
GatewayApiTransport:负责实际发送短信的传输层;GatewayApiTransportFactory:根据 DSN 创建传输实例;GatewayApiOptions:为单条消息附加 GatewayApi REST 接口特有参数。
从 CHANGELOG.md 可以清晰看到桥接器的版本演进:
| 版本 | 变更内容 | 实质影响 |
|---|---|---|
| 5.3 | 新增该桥接器 | 首次为 Symfony Notifier 提供 GatewayApi 短信能力 |
| 6.2 | 当SmsMessage定义了from时优先使用它 | 发件人解析逻辑:消息级发件人优先于传输级默认发件人 |
| 6.3 | 使用GatewayApiOptions类 | 引入面向 REST 接口的消息选项模型,替代散落的字符串参数 |
| 7.2 | 为GatewayApiOptions增加label选项 | 支持给短信附加业务标签 |
| 8.2 | 新增sslDSN 选项,可走明文 HTTP | 支持明文 HTTP 请求发送短信 |
该演进脉络与 README.md 中的 DSN 与选项示例一一对应,下文将逐一展开。
DSN 配置与传输工厂解析
标准 DSN 格式
在 Symfony 中通过环境变量配置短信传输,README.md 给出了标准示例:
GATEWAYAPI_DSN=gatewayapi://TOKEN@default?from=FROM其中:
TOKEN:GatewayApi 的 API Token(OAuth 认证凭证);FROM:发件人名称(sender name);default:占位主机名,表示使用桥接器内置的默认 API 主机。
传输工厂如何解析 DSN
DSN 的解析逻辑位于 GatewayApiTransportFactory.php 的create()方法:
public function create(Dsn $dsn): GatewayApiTransport { $scheme = $dsn->getScheme(); if ('gatewayapi' !== $scheme) { throw new UnsupportedSchemeException($dsn, 'gatewayapi', $this->getSupportedSchemes()); } $authToken = $this->getUser($dsn); $from = $dsn->getRequiredOption('from'); $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); $port = $dsn->getPort(); return (new GatewayApiTransport($authToken, $from, $this->client, $this->dispatcher)) ->setHost($host) ->setPort($port) ->setSsl($this->getSsl($dsn)); }要点如下:
- 方案校验:只接受
gatewayapi方案,getSupportedSchemes()返回['gatewayapi'],其他方案抛出UnsupportedSchemeException; - Token 解析:Token 取自 DSN 的用户名部分(
getUser()),对应测试中的gatewayapi://token@...形式; from为必填项:通过getRequiredOption('from')解析,缺失时工厂测试会触发"缺少必需选项"错误(见 GatewayApiTransportFactoryTest.php 中的missingRequiredOptionProvider);- 主机与端口:主机为
default时置空,最终回落到GatewayApiTransport::HOST = 'gatewayapi.com';也可显式指定自定义主机与端口; ssl选项:8.2 新增,由getSsl()解析。
ssl 选项与明文 HTTP(8.2 新增)
8.2 版本在 CHANGELOG.md 中注明:新增sslDSN 选项以支持通过明文 HTTP 发送请求。其底层机制如下:
AbstractTransportFactory::getSsl()(位于 AbstractTransportFactory.php)读取 DSN 选项并转为布尔值:
protected function getSsl(Dsn $dsn): ?bool { return null === $dsn->getOption('ssl') ? null : $dsn->getBooleanOption('ssl'); }AbstractTransport::getHttpScheme()(位于 AbstractTransport.php)决定最终协议:
protected function getHttpScheme(): string { return ($this->ssl ?? static::SSL) ? 'https' : 'http'; }也就是说,默认走 HTTPS;只有显式配置ssl=0(或ssl=false)时才会使用http://。DSN 写法示例:
GATEWAYAPI_DSN=gatewayapi://TOKEN@default?from=FROM&ssl=0使用场景一般是内网代理、调试环境或自定义网关等明确需要明文传输的场合;生产环境应保持默认 HTTPS。
发送短信的完整调用链
传输构造与消息类型约束
GatewayApiTransport.php 定义传输构造与能力:
final class GatewayApiTransport extends AbstractTransport { protected const HOST = 'gatewayapi.com'; public function __construct( #[\SensitiveParameter] private string $authToken, private string $from, ?HttpClientInterface $client = null, ?EventDispatcherInterface $dispatcher = null, ) { ... } }authToken标注了#[\SensitiveParameter],在异常堆栈与日志中会被脱敏处理;supports()仅接受SmsMessage,且消息选项必须是GatewayApiOptions或为空。
测试用例(GatewayApiTransportTest.php)印证了这一点:SmsMessage受支持,ChatMessage与其他消息类型均被拒绝。
doSend 底层请求构造
doSend()是发送的核心,其请求构造逻辑:
$options = $message->getOptions()?->toArray() ?? []; $options['sender'] = $message->getFrom() ?: $this->from; $options['recipients'] = [['msisdn' => $message->getPhone()]]; $options['message'] = $message->getSubject(); $endpoint = \sprintf('%s://%s/rest/mtsms', $this->getHttpScheme(), $this->getEndpoint()); $response = $this->client->request('POST', $endpoint, [ 'auth_basic' => [$this->authToken, ''], 'json' => array_filter($options), ]);对应 GatewayApi 的POST /rest/mtsms接口,要点包括:
- 发件人优先级(6.2 行为):
$message->getFrom() ?: $this->from——若SmsMessage自身定义了from,优先使用消息级发件人,否则回落到 DSN 中的传输级from。这正是 CHANGELOG 6.2 条目"UseSmsMessage->fromwhen defined"的实现; - 收件人结构:
recipients是数组,每项形如['msisdn' => 电话号码]; - 认证方式:HTTP Basic 认证,用户名即 API Token;
- JSON 载荷:
array_filter过滤掉空值,避免发送多余的空白字段; - 端点选择:
getHttpScheme()依据ssl选项决定https或http,主机由getEndpoint()提供。
响应处理与消息 ID
发送后处理逻辑:
try { $statusCode = $response->getStatusCode(); } catch (TransportExceptionInterface $e) { throw new TransportException('Could not reach the remote GatewayApi server.', $response, 0, $e); } if (200 !== $statusCode) { throw new TransportException(\sprintf('Unable to send the SMS: error %d.', $statusCode), $response); } $content = $response->toArray(false); $sentMessage = new SentMessage($message, (string) $this); $sentMessage->setMessageId((string) $content['ids'][0]);- 网络层异常被包装为
TransportException(提示"无法到达 GatewayApi 服务器"); - 非 200 状态码抛出带状态码的
TransportException; - 成功时从响应 JSON 的
ids[0]提取短信 ID 作为SentMessage的消息 ID,测试用MockResponse(json_encode(['ids' => [42]]))验证了返回 ID 为42(见 GatewayApiTransportTest.php)。
端到端发送示例
use Symfony\Component\Notifier\Message\SmsMessage; use Symfony\Component\Notifier\Bridge\GatewayApi\GatewayApiOptions; $sms = new SmsMessage('+1411111111', 'My message'); // 可选:附加 GatewayApi 特有选项(详见下一节) $options = (new GatewayApiOptions()) ->class('standard') ->callbackUrl('https://my-callback-url') ->userRef('user_ref') ->label('label'); $sms->options($options); $texter->send($sms);$texter是通过 Notifier 组件(Texter)绑定的 GatewayApi 传输实例,DSN 中的from将作为默认发件人。
GatewayApiOptions:消息级自定义选项
选项类设计(6.3 引入)
6.3 起桥接器引入GatewayApiOptions类统一承载消息选项(GatewayApiOptions.php),实现MessageOptionsInterface。它通过流畅接口(fluent API)设置键值,最终以toArray()输出:
| 方法 | 写入的请求键 | 说明 |
|---|---|---|
class(string $class) | class | 短信发送等级/类型(如standard),对应 GatewayApi REST 文档中的 class 参数 |
callbackUrl(string $callbackUrl) | callback_url | 发送状态回调地址,异步接收投递状态 |
userRef(string $userRef) | userref | 用户自定义引用,用于关联业务订单或内部编号 |
label(string $label) | label | 7.2 新增的标签选项,用于给短信打业务标签便于统计 |
注意键名的大小写与转换:方法名是 camelCase,写入载荷时被映射为 REST 接口要求的键名(callbackUrl→callback_url、userRef→userref)。这一点在 GatewayApiOptionsTest.php 中有完整断言:
$gatewayApiOptions = (new GatewayApiOptions()) ->class('test_class') ->callbackUrl('test_callback_url') ->userRef('test_user_ref') ->label('test_label'); self::assertSame([ 'class' => 'test_class', 'callback_url' => 'test_callback_url', 'userref' => 'test_user_ref', 'label' => 'test_label', ], $gatewayApiOptions->toArray());与消息的绑定
GatewayApiOptions通过SmsMessage::options()绑定到消息。在doSend()中:
$options = $message->getOptions()?->toArray() ?? [];即消息选项与sender、recipients、message合并为最终 JSON 载荷,并经array_filter去除空值。因此这些选项均是可选的:不设置时请求依然合法,仅发送基础短信。
从测试可见,GatewayApiOptions构造器还支持直接传入数组(new GatewayApiOptions(['from' => 'foo'])),这为程序化构造选项提供了另一种途径。
测试体系与行为验证
该桥接器附带三组测试,覆盖了 DSN 解析、消息约束与发送链路:
- GatewayApiTransportFactoryTest.php:
createProvider验证gatewayapi://token@default?from=Symfony会被解析并渲染为gatewayapi://gatewayapi.com?from=Symfony(即default主机回落到gatewayapi.com,Token 不出现在字符串化结果中);supportsProvider确认只有gatewayapi方案受支持;incompleteDsnProvider验证缺少 Token 时报错;missingRequiredOptionProvider验证缺少from时报错;unsupportedSchemeProvider验证其他方案被拒绝;
- GatewayApiTransportTest.php:验证
SmsMessage支持、ChatMessage等不支持、以及发送成功后消息 ID 的提取; - GatewayApiOptionsTest.php:验证选项键名映射。
GatewayApiTransport::__toString()返回gatewayapi://gatewayapi.com?from=Symfony这种规范化形式(Token 不泄露),SentMessage因此携带可追溯的传输标识。
版本升级注意事项
结合 CHANGELOG.md 与 composer.json(当前要求php >= 8.4.1、symfony/notifier ^8.2、symfony/http-client ^7.4|^8.0),升级时留意:
- 6.2 起发件人解析变化:若同一
SmsMessage同时设置了from,将优先于 DSN 的from,多租户场景下可通过消息级发件人覆盖默认值; - 6.3 起选项模型统一:老代码中的字符串参数散传方式应迁移到
GatewayApiOptions流畅接口; - 7.2 新增
label:需要标签能力(如按营销活动统计)时可使用->label(); - 8.2 新增
ssl:仅在明确需要明文 HTTP 时设置ssl=0,默认 HTTPS 保持不变。
总体上,该桥接器遵循 Symfony Notifier 的统一抽象:DSN 驱动工厂、SmsMessage+ 选项模型、SentMessage回执,开发者无需关心 GatewayApi REST 协议细节即可完成国际短信发送,同时在需要时可通过GatewayApiOptions与ssl选项精细控制请求行为。
- 后端
- Web框架
【免费下载链接】symfony
The Symfony PHP framework
相关推荐
Fluent Bit 依赖的 nghttp2:解析 nghttp2_session_get_next_stream_id 的 HTTP/2 流 ID 分配机制
Fluent Bit 依赖的 nghttp2:解析 nghttp2_session_get_next_stream_id 的 HTTP/2 流 ID 分配机制
后端Web框架Deploying CyberStrikeAI: From Local Quick Start to Production Red-Team Platform
Deploying CyberStrikeAI: From Local Quick Start to Production Red Team Platform
后端Web框架CANN Runtime 实战:用 aclrtBinaryLoadFromData 从内存加载 Kernel 二进制执行 FP16 向量加法
CANN Runtime 实战:用 aclrtBinaryLoadFromData 从内存加载 Kernel 二进制执行 FP16 向量加法 本指南围绕 CAN
后端Web框架
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考