用 curl 手写最简 post 请求调通短信 api 是最稳起点,需严格校验 content-type、手机号格式、签名时间戳、utf-8 编码及 redis 存储验证码,设 curl 超时并捕获 0/403/503 错误。

用 curl 调通短信 API 是最稳的起点
PHP 自身没有内置短信发送能力,所有方案都得走 HTTP 请求调第三方 API。别碰那些封装过深的 SDK,尤其带自动重试、队列、模板管理的——你只是发个验证码,它们反而容易掩盖参数错、签名错、编码错这些真实问题。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 先用
curl手写一个最简 POST 请求,目标是拿到{"code":0,"msg":"OK"}这类明确响应 - 务必检查 Content-Type:多数厂商要求
application/json,但也有老接口要application/x-www-form-urlencoded - 注意手机号格式:有的要带国家码(
+8613800138000),有的只认纯数字(13800138000),错一位就返回“号码格式错误” - 签名和时间戳必须严格按文档生成,比如
sha256(secret+timestamp+nonce),少拼一个字段就{"code":1015,"msg":"签名验证失败"}
json_encode() 前必须过滤中文和特殊字符
验证码内容里如果含中文(比如“【XX平台】您的验证码是1234”),而 API 文档又没明说支持 UTF-8,大概率会因编码不一致被截断或报错 {"code":1003,"msg":"参数非法"}。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 发前用
mb_convert_encoding($content, 'UTF-8', 'auto')强制转码 - 用
json_last_error()检查json_encode()是否成功,常见失败原因是字符串含不可见控制符(如 \x00) - 如果厂商只接受 GBK 模板,那就别用
json_encode(),改用http_build_query()+iconv('UTF-8', 'GBK//IGNORE', $content)
验证码不能直接塞进 $_SESSION 就完事
Session 写入延迟、跨域失效、GC 清理早于用户输入,都会导致“明明发了码,校验时却说不存在”。这不是短信问题,是状态同步没兜住。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 发码成功后,立刻用
session_write_close()强制刷写,避免后续跳转或 AJAX 请求读不到 - 更稳妥的做法:把验证码和手机号一起存进 Redis,设 5 分钟 TTL,校验时只查 Redis —— 不依赖 PHP Session 生命周期
- 别用
$_SESSION['sms_code']这种裸键名,加上手机号哈希后缀,比如$_SESSION['sms_code_'.md5($phone)],防撞
测试环境必须模拟超时和 403 错误
线上跑通不代表可靠。短信网关常在高峰限流,返回 403 Forbidden 或卡住 30 秒才超时,PHP 默认 curl 无超时设置,整个页面就挂住。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 强制设
curl_setopt($ch, CURLOPT_TIMEOUT, 5)和curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3) - 捕获
curl_error($ch)和curl_getinfo($ch, CURLINFO_HTTP_CODE),对 0(超时)、403、503 单独打日志,别吞掉 - 本地测试时用
sleep(6)模拟网关卡死,看你的超时逻辑是否真生效
真正难的不是调通一次,而是当运营商通道切换、签名规则突变、手机号段新增限制时,你的错误捕获能不能准确定位到是「签名算法错了」还是「新号段要白名单」——这两者日志长得几乎一样,都报 code=1015。











