短信服務是應用系統核心基礎設施。本文提供基於 XML 協議的短信發送完整封裝類,涵蓋簽名驗證、模板管理、錯誤碼解析、異常處理等生產級要素,適配主流短信服務商(騰訊雲、阿里雲、華為雲等均採用類似協議)。
class Sms {
// 配置屬性(建議從環境變量/配置中心讀取)
private string $userId = 'XXXXX';
private string $password = 'XXXXXX'; // 已 MD5 加密
private string $templateId = 'XXXXXX';
private string $serverUri = 'XXXXXX';
private string $port = 'XXXXXX';
/**
* 發送單條短信
* @param string $message 短信內容(模板變量值)
* @param string $mobile 接收手機號
* @param string $signature 簽名(已在服務商備案)
* @return bool|string true=成功,false=網絡失敗,string=錯誤碼
*/
public function sendOneMsg(string $message, string $mobile, string $signature = 'demo'): bool|string {
$xmlContent = $this->createXmlContent($message, $mobile, $signature);
$xmlResponse = $this->sendHttpRequest($xmlContent);
if (!$xmlResponse) {
return false; // 網絡層失敗:超時、DNS、連接拒絕等
}
return $this->parseResponse($xmlResponse);
}
// ... 私有方法見下文
}
private function createXmlContent(string $message, string $mobile, string $signature): string {
$data = [
'userId' => $this->userId,
'password' => $this->password,
'templateId' => $this->templateId,
'phone' => $mobile,
'port' => $this->port,
'data' => $message,
'signature' => $signature,
];
$dom = new DOMDocument('1.0', 'UTF-8');
$request = $dom->createElement('request');
$dom->appendChild($request);
foreach ($data as $key => $val) {
$element = $dom->createElement($key);
$request->appendChild($element);
$text = $dom->createTextNode($val);
$element->appendChild($text);
}
return $dom->saveXML();
}
private function sendHttpRequest(string $xmlContent): string|false {
$timestamp = time();
$headers = [
'Content-Type: text/xml',
'Content-Length: ' . strlen($xmlContent),
'Cmd: mt',
'TS: ' . $timestamp,
// 簽名算法:MD5(XML內容 + 時間戳 + 密碼) 大寫
'Authorization: ' . strtoupper(md5($xmlContent . $timestamp . $this->password)),
];
$ch = curl_init($this->serverUri);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $xmlContent,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_TIMEOUT => 10, // 連接超時
CURLOPT_CONNECTTIMEOUT => 5, // 總超時
CURLOPT_SSL_VERIFYPEER => false, // 生產環境建議開啟並配置 CA 憑證
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
curl_close($ch);
if ($errno) {
error_log("SMS CURL Error [$errno]: $error");
return false;
}
return $response;
}
private function parseResponse(string $xml): bool|string {
$xmlObj = simplexml_load_string($xml);
if (!$xmlObj) {
error_log("SMS XML Parse Failed: $xml");
return 'PARSE_ERROR';
}
$retCode = (int)($xmlObj->retCode ?? -1);
// 成功碼映射(視具體服務商文檔調整)
$successCodes = [1000, 2000, 'OK', 'SUCCESS'];
if (in_array($retCode, $successCodes, true)) {
return true;
}
// 記錄失敗詳情便於排查
error_log("SMS Send Failed: Code=$retCode, Msg={$xmlObj->retMsg ?? 'Unknown'}");
return (string)$retCode; // 返回具體錯誤碼供上層業務判斷
}
| 維度 | 基礎版缺口 | 增強方案 |
|---|---|---|
| 配置管理 | 硬編碼敏感信息 | 環境變量 + 配置中心 + 密鑰輪換 |
| 並發性能 | 同步阻塞發送 | 異步隊列 + 連接池 + 批量發送接口 |
| 可靠性 | 無重試/降級 | 指數退避重試、熔斷器、失敗進死信隊列人工補發 |
| 監控告警 | 無可觀測性 | 發送量/成功率/延遲/錯誤碼分布儀表盤 + 閾值告警 |
| 安全合規 | 明文傳輸風險 | HTTPS 強制、簽名算法升級(HMAC-SHA256)、IP 白名單 |
| 模板管理 | 模板 ID 硬編碼 | 模板版本化、變量校驗、審核狀態同步 |
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
class ModernSmsClient {
public function __construct(
private ClientInterface $httpClient,
private RequestFactoryInterface $requestFactory,
private string $endpoint,
private string $appId,
private string $appSecret
) {}
public function send(string $mobile, string $templateId, array $params): SmsResult {
$payload = ['mobile' => $mobile, 'template_id' => $templateId, 'params' => $params];
$request = $this->requestFactory->createRequest('POST', $this->endpoint)
->withHeader('Content-Type', 'application/json')
->withHeader('Authorization', $this->generateSignature($payload))
->withBody(new \GuzzleHttp\Psr7\Stream(json_encode($payload)));
$response = $this->httpClient->sendRequest($request);
return $this->parseResult($response);
}
}
| 代碼 | 含義 | 處理建議 |
|---|---|---|
| 1000 | 成功 | 記錄日誌,標記發送成功 |
| 1001 | 認證失敗 | 檢查 AppID/Secret、IP 白名單、簽名算法 |
| 1002 | 模板不存在/未審核 | 同步模板狀態,告警運營 |
| 1003 | 手機號格式錯誤 | 前端校驗 + 後端二次校驗,過濾無效號碼 |
| 1004 | 余額不足 | 觸發充值告警,降級走備用通道 |
| 1005 | 頻率限制 | 令牌桶限流,延遲重試 |
| 9999 | 系統異常 | 記錄完整請求響應,聯繫服務商技術支持 |
這個封裝類提供了短信發送的核心骨架。實際接入時,請仔細閱讀服務商 API 文檔,調整 XML 結構、簽名算法、字段映射、錯誤碼定義,並按上表補全生產級非功能性需求。