Yii框架不内置短信能力,必须对接第三方HTTP接口;发送成功关键在请求合规、参数动态校验及响应真实解析,需严格按平台要求构造POST请求(如MD5签名、urlencode中文、时间戳误差≤5分钟),并用共享缓存实现两级限频与验证码存储,同时精准解析返回状态码而非模糊匹配。

Yii 框架本身不内置短信能力,必须对接第三方 HTTP 短信接口。能否发成功,关键不在框架写法,而在请求构造是否合规、参数是否动态校验、响应是否真实解析——很多项目卡在返回 “提交成功” 但运营商没收到,其实是接口返回了 success=false 却被当成真成功。
怎么构造一个能过签名校验的 POST 请求
多数国内短信平台(如互亿无线、阿里云、腾讯云)要求 POST 提交 + application/x-www-form-urlencoded 编码 + MD5 或 HMAC-SHA256 动态签名。硬拼 URL 或用 file_get_contents() 直接 GET 是高危操作。
- 必须对
content做urlencode(),尤其含中文时,否则签名值错、平台拒收 -
password或apikey绝不能明文参与拼串再 MD5;要按文档顺序拼接字段(如account+mobile+content+time+key),再哈希 - 时间戳字段(如
time)需与服务器时间误差 ≤ 5 分钟,否则签名失效 - 示例(互亿无线风格):
$signStr = $account . $mobile . $content . $time . $apiKey; $signature = md5($signStr); $data = [ 'account' => $account, 'mobile' => $mobile, 'content' => urlencode($content), 'time' => $time, 'sign' => $signature ]; $result = Yii::$app->httpClient->post('https://106.ihuyi.com/webservice/sms.php?method=Submit', $data)->getBody();
为什么缓存验证码和限制频率不能只靠 session
session是单机绑定的,微服务或负载均衡下会失效;且无法防刷(同一 IP 多账号注册)。必须用共享存储做两级控制:
- 第一级:手机号维度限频(如 60 秒内最多 1 条),用
cache存键sms:freq:138****1234,TTL 设为 60 - 第二级:IP 维度限频(如 1 小时内最多 5 条),键为
sms:ip:123.123.123.123,TTL 设为 3600 - 验证码本体必须存
cache(非 session),键为sms:code:138****1234,TTL 严格设为 300(5 分钟),且读取后立即delete
如何判断“发送成功”不是假成功
几乎所有平台返回的都是 XML 或 JSON 字符串,但状态字段名五花八门:code、result、status、submitResult,且「0」或「1」含义可能相反。
- 不要依赖
strpos($result, 'success') !== false这类模糊匹配 - 必须解析响应体,检查具体字段:互亿无线看
<code>2表示成功;阿里云看"Code":"OK";腾讯云看"ErrorCode":"" - 记录原始响应体到日志(哪怕只存前 200 字符),出问题时才能对照文档查错
- 若返回
code=40(余额不足)、code=43(签名不合法)、code=53(内容含敏感词),要透出明确提示,而不是笼统报“发送失败”
真正容易被忽略的是:短信网关返回“提交成功”只代表消息进了平台队列,不代表已触达手机。要监控最终送达率,得靠回执推送(MO)或主动轮询发送报告接口,这部分在 Yii 中需单独起异步任务处理,不能塞在同步请求里。











