Files
wordpress-theme-aksrt/src/Sms/SMS.php
T

137 lines
4.3 KiB
PHP

<?php
/**
* @package AKSRT
*/
namespace AKSRT\Sms;
defined('ABSPATH') || exit;
/**
* ZibLL-style SMS dispatcher and send-rate limiting.
*/
class SMS {
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function get_provider_config(): array {
if (class_exists('\\AKSRT\\Settings\\Settings')) {
return \AKSRT\Settings\Settings::get_sms_settings();
}
return ['sms_sdk' => 'null', 'sms_number_limit_ch' => true];
}
/**
* Phone-number validation follows the selected domestic/international mode.
*/
public static function is_phonenumber(string $phone, ?bool $domestic_only = null): bool {
if ($domestic_only === null) {
$config = self::get_instance()->get_provider_config();
$domestic_only = !empty($config['sms_number_limit_ch']);
}
if ($domestic_only) {
return (bool) preg_match('/^1[3-9]\d{9}$/', $phone);
}
return (bool) preg_match('/^(?:\d{5,}|\+\d{6,}|\+?\d{2,}[-\s]\d{2,}[0-9\-\s]{0,15})$/', $phone);
}
/**
* Send a six-digit verification code. The caller stores it after success.
*
* @return array{success: bool, message: string, remaining_time?: int}
*/
public function send_code(string $phone, string $code): array {
$config = $this->get_provider_config();
$sdk = sanitize_key((string) ($config['sms_sdk'] ?? 'null'));
if ($sdk === '' || $sdk === 'null') {
return ['success' => false, 'message' => __('短信接口未启用', 'aksrt')];
}
if (!self::is_phonenumber($phone, !empty($config['sms_number_limit_ch']))) {
return ['success' => false, 'message' => __('手机号码格式有误', 'aksrt')];
}
$provider = SMSProviderFactory::get($sdk);
if ($provider === null) {
return ['success' => false, 'message' => __('暂无短信接口,请检查短信服务商设置', 'aksrt')];
}
$missing = $provider->validate_config($config);
if ($missing) {
return ['success' => false, 'message' => sprintf(__('短信接口缺少配置:%s', 'aksrt'), implode(', ', $missing))];
}
// [防刷机制]验证码发送限制(后台「扩展&增强 → 网站安全」)。
$limit = \AKSRT\Extensions\SendLimit::check($phone);
if (empty($limit['ok'])) {
return [
'success' => false,
'message' => (string) $limit['message'],
'remaining_time' => (int) $limit['remaining'],
];
}
$result = $provider->send_code($phone, $code, $config);
if (empty($result['success'])) {
return ['success' => false, 'message' => (string) ($result['message'] ?? __('验证码发送失败,请稍后重试', 'aksrt'))];
}
$cooldown = \AKSRT\Extensions\SendLimit::record($phone);
return [
'success' => true,
'message' => __('验证码短信已发送', 'aksrt'),
'remaining_time' => $cooldown > 0 ? $cooldown : 60,
];
}
public function get_provider_status(): array {
$config = $this->get_provider_config();
$key = sanitize_key((string) ($config['sms_sdk'] ?? 'null'));
$enabled = $key !== '' && $key !== 'null';
$provider = SMSProviderFactory::get($key);
$status = [
'enabled' => $enabled,
'provider' => $enabled ? $key : 'none',
'configured' => false,
'available' => false,
'verified' => false,
'transport' => '',
'missing_fields' => [],
];
if ($provider !== null) {
$status = array_merge($status, $provider->get_status($config));
}
return $status;
}
/** Send the reference theme's fixed 888888 test message without storing a code. */
public function test_send(string $phone): array {
$config = $this->get_provider_config();
$sdk = sanitize_key((string) ($config['sms_sdk'] ?? 'null'));
if ($sdk === '' || $sdk === 'null') {
return ['success' => false, 'message' => __('短信接口未启用', 'aksrt')];
}
if (!self::is_phonenumber($phone, !empty($config['sms_number_limit_ch']))) {
return ['success' => false, 'message' => __('手机号码格式有误', 'aksrt')];
}
$provider = SMSProviderFactory::get($sdk);
if ($provider === null) {
return ['success' => false, 'message' => __('暂无短信接口,请检查短信服务商设置', 'aksrt')];
}
$missing = $provider->validate_config($config);
if ($missing) {
return ['success' => false, 'message' => sprintf(__('短信接口缺少配置:%s', 'aksrt'), implode(', ', $missing))];
}
return $provider->send_code($phone, '888888', $config);
}
}