短信驗證碼、通知類消息是 Web 應用標配。本文展示在 Yii 1.x 中封裝 smsto (假設為某短信服務商) 接口的完整流程,含參數構建、GB2312 編碼轉換、URL 編碼、HTTP 請求發送與響應處理。
<?php
class SmsComponent extends CApplicationComponent {
// 接口配置 (建議放入 params.php)
private $uid = 'zyd';
private $password = '123456';
private $gateway = 'https://sms.zyd.com/msgsend.ashx';
/**
* 發送短信
* @param string $mobile 手機號
* @param string $message 短信內容
* @return string|false 接口原始響應 / 失敗 false
*/
public function send($mobile, $message) {
// 1. 參數校驗
if (!preg_match('/^1\d{10}$/', $mobile)) {
Yii::log("無效手機號: $mobile", CLogger::LEVEL_WARNING);
return false;
}
if (empty($message)) return false;
// 2. 編碼轉換:內部 UTF-8 → 接口要求 GB2312 → URL 編碼
$encodedMessage = urlencode(mb_convert_encoding($message, 'GB2312', 'UTF-8'));
// 3. 構建請求 URL (GET 方式)
$params = [
'USERNAME' => $this->uid,
'PASSWORD' => $this->password,
'MOBILE' => $mobile,
'CONTENT' => $encodedMessage,
'SEQ' => 1000, // 流水號,可用於狀態報告匹配
];
$url = $this->gateway . '?' . http_build_query($params);
// 4. 發送請求 (設置超時防止阻塞)
$ctx = stream_context_create([
'http' => [
'timeout' => 10, // 10 秒超時
'ignore_errors' => true, // 獲取非 2xx 響應體
]
]);
$response = @file_get_contents($url, false, $ctx);
// 5. 響應處理
if ($response === false) {
Yii::log("短信發送失敗 (網絡/超時): $mobile", CLogger::LEVEL_ERROR);
return false;
}
// 6. 記錄日誌供審計
Yii::log("短信發送: mobile=$mobile, response=$response", CLogger::LEVEL_INFO);
return $response;
}
}
return array(
'components' => array(
'sms' => array(
'class' => 'application.components.SmsComponent',
// 可在這裡覆蓋 uid/password/gateway
),
),
);
// 控制器中
public function actionSendVerifyCode() {
$mobile = $_POST['mobile'];
$code = sprintf('%06d', mt_rand(0, 999999));
$msg = "您的驗證碼是 $code,10 分鐘內有效,請勿泄露。";
if (Yii::app()->sms->send($mobile, $msg)) {
// 存入緩存/Session,設置 10 分鐘過期
Yii::app()->cache->set("sms_code_$mobile", $code, 600);
echo CJSON::encode(['success' => true]);
} else {
echo CJSON::encode(['success' => false, 'error' => '發送失敗,請稍後重試']);
}
}
$message = "中文內容"; // 步驟 1: 內部 UTF-8 → 接口要求 GB2312 $gb2312 = mb_convert_encoding($message, 'GB2312', 'UTF-8'); // 步驟 2: URL 編碼 (空格→+ 或 %20,中文→%XX%XX) $final = urlencode($gb2312); // 發送後接口端:urldecode → GB2312 解碼 → 入庫/下發
注意:若接口文檔要求 UTF-8,則跳過 mb_convert_encoding 直接 urlencode。
stream_context_create(['http'=>['timeout'=>10]]) 防止接口掛起導致 PHP-FPM 進程佔用。// 同一手機號 60 秒內只允許發送一次
$key = "sms_limit_$mobile";
if (Yii::app()->cache->get($key)) {
throw new CHttpException(429, '請求過於頻繁');
}
Yii::app()->cache->set($key, 1, 60);
預留路由 /sms/callback 接收服務商 POST 狀態報告:
public function actionCallback() {
$seq = $_POST['SEQ'];
$status = $_POST['STATUS']; // 0 成功, 非 0 失敗
$mobile = $_POST['MOBILE'];
// 更新發送記錄表、觸發業務邏輯
Yii::log("短信狀態報告: seq=$seq, mobile=$mobile, status=$status");
echo 'OK'; // 告知服務商已接收
}
class MultiChannelSms extends CApplicationComponent {
private $channels = [
['class' => 'SmsComponent', 'weight' => 70], // 主通道 70%
['class' => 'BackupSmsComponent', 'weight' => 30], // 備用 30%
];
public function send($mobile, $message) {
// 加權隨機選擇通道
$channel = $this->selectChannel();
return $channel->send($mobile, $message);
}
}
Yii 整合短信接口核心在於:組件化封裝、編碼鏈路正確、超時保護、日誌審計、防刷限流、狀態報告回調。將發送邏輯下沉至 Component,業務層僅需一行 Yii::app()->sms->send() 即可調用,實現高內聚、低耦合、易測試、易擴展。