☰
Symfony Notifier 集成 GatewayApi:GatewayApi 短信桥接器完整配置与实战指南
2026/10/3 1:53:05 网站建设 项目流程
  • 后端
  • Web框架

【免费下载链接】symfony

The Symfony PHP framework

项目地址:https://gitcode.com/GitHub_Trending/sy/symfony
点击查看免费下载

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)label7.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

项目地址:https://gitcode.com/GitHub_Trending/sy/symfony
点击查看免费下载
上一篇:Alpine.js开发避坑指南:从安装到部署的20个实战问题解决
下一篇:htmx-go 与现有Go项目集成:逐步迁移到现代化Web架构的完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询