PHP实战PHP学习后端xuexi

「PHP开发APP接口实战012」发送短信验证码之生成并缓存验证

2018-03-07  本文已影响35人  一念觀心

配置参数

  1. Memcache 配置, 在 /app/config/config.ini 文件添加以下代码:
[memcache]
host = 127.0.0.1
port = 11211
prefix = api

在 Visual NMP 中,默认已经安装了 Memcache, 可直接使用。如服务未开启,直接打开即可。 默认连接端口:11211。缓存键名前缀:prefix = api,方便区分项目,可随意设置, 一般设置为项目名。
「PHP开发APP接口实战001」开发环境搭建

  1. 短信配置, 在 /app/config/config.php 文件添加以下代码:
    'sms' => [
        'times' => 3, // 同一手机一小时内发送短信次数, 0 为不限制
        'interval' => 60, // 同一手机两次发送间隔时间(单位:秒), 0 为不限制
        'valid_time' => 300, // 短信验证码有效时间(单位:秒), 0 为久有效
    ],

创建 Memcache 操作类 XMemcache

/app/library 目录下创建文件 XMemcache.php, 添加以下代码:

<?php

/**
 * 缓存
 */
class XMemcache
{

    public static $instance;
    private $memcache = null; // Memcache 对象
    private $config = null; // 配置参数
    private $tag = null; // 标识

    private function __construct($tag = null)
    {
        // 初始化 Memcache 对象
        $this->memcache = new Memcache();
        // 加载配置参数
        $this->config = Config::instance()->get('memcache', 'ini');
        // 连接Memcache服务器
        $this->memcache->addServer($this->config['host'], $this->config['port']);
        $this->tag = $tag;
    }

    /**
     * @param null $tag  缓存标识
     * @return XMemcache
     */
    public static function instance($tag = null)
    {
        if (!self::$instance) self::$instance = new self($tag);
        return self::$instance;
    }

    /**
     * 添加缓存,若不存在则追加,若不存在则新增
     * @param $key 键名
     * @param $value 缓存内容
     * @param int $timeout 0永久有效,604800 7天,最大不能超过30天
     * @param int $iszip
     * @return mixed
     */
    public function append($key, $value, $timeout = 604800, $iszip = 0)
    {
        if ($values = $this->get($key)) {
            $values[] = $value;
        } else {
            $values = [$value];
        }
        $this->set($key, $values, $timeout, $iszip);
    }

    /**
     * 设置缓存
     * @param $key 键名
     * @param $value 缓存内容
     * @param int $timeout 0永久有效,604800 7天,最大不能超过30天
     * @param int $iszip
     * @return mixed
     */
    public function set($key, $value, $timeout = 604800, $iszip = 0)
    {
        $value = serialize($value);
        return $this->memcache->set($this->formatKey($key), $value, $iszip, $timeout);
    }

    /**
     * 根据KEY获得缓存内容
     * @param $key
     * @return mixed
     */
    public function get($key)
    {
        $value = $this->memcache->get($this->formatKey($key));
        return unserialize($value);
    }

    /**
     * 删除指定缓存
     * @param $key
     * @return mixed
     */
    public function delete($key)
    {
        return $this->memcache->delete($this->formatKey($key));
    }

    /**
     * 清空缓存
     * @return mixed
     */
    public function flush()
    {
        return $this->memcache->flush();
    }

    /**
     * 重写缓存键名,格式: [prefix]:[tag]:[key]
     * @param $key
     * @return string
     */
    private function formatKey($key)
    {
        return $this->config['prefix'] . ':' . $this->tag . ':' . $key;
    }

}

这里重写了一些 Memcache 常用的操作函数。 如:设置缓存 set(), 追加缓存 append(), 获取缓存 get(), 删除缓存 delete(), 清空缓存 flush()
值得注意的是,我们还对缓存键名进行了重写,方便区分项目和模块。

生成短信验证码

  1. /app/library 目录下创建文件 SMS.php, 添加以下代码:
<?php

/**
 * 短信验证码
 */
class SMS
{

    public static $instance;

    // 配置参数
    private $config = null;

    private function __construct()
    {
        // 加载短信配置参数
        $this->config = Config::instance()->get('sms');
    }

    public static function instance()
    {
        if (!self::$instance) self::$instance = new self();
        return self::$instance;
    }
  
}

这里实现了实例化时,自己加载配置参数。

  1. 增加验证码改送函数 send(), 用于外部调用。如:
     /**
     * 发送短信验证码
     * @param $mobile
     * @return array
     * @throws Exception
     */
    public function send($mobile)
    {

    }

此函数里面分四步走:

  1. 验证指定手机号,当前是否可以发送验证码
  2. 生成四位数字验证码,并配置上生成时间
  3. 调用 XMemcache 缓存验证码
  4. 调用第三方接口,发送验证码,并返回发送状态。(市场上有许多发送第三方平台,都有)
  1. 这里调换一下顺序,先讲解生成和缓存验证码。
    首先,添加函数generateCode(), 随机生成4位数字验证码
    /**
     * 随机生成4位数字验证码
     * @return int
     */
    private function generateCode()
    {
        return rand(1000, 9999);
    }

然后,在 send() 函数中添加代码:

        $item = [
            'code' => $this->generateCode(), // 生成短信验证码
            'time' => time(), // 生成时间
            'verified' => 0,  // 验证状态: 0 未验证, 1 已验证
        ];

这里除了生成验证码,同时初始化生成时间time, 验证状态verified,用于验证发送时间和检查验证码是否已验证。

  1. 缓存验证码,在 send() 函数中添加代码:
        // 缓存短信验证码
        XMemcache::instance('sms')->append($mobile, $item, 3600);

这里完成了几个工作:

  • $item 存于以指定手机号为键名的缓存下
  • 同一手机号多次发送,都存在同一键名下,用于统计1小时内验证码发送次数。
  • 缓存有效时间设置为 1 小时
  1. 现在我们再回来讲解验证是否允许向指定手机号发送验证码。

验证规则:

  • 同一手机两次发送间隔时间1分钟(可配置间隔时间)
  • 同一手机1小时内最多只能发送3次验证码(可配置发送次数)

首先,添加函数 getCacheCodes()validateSend(), 如:

    /**
     * 获取1小时内发送的验证码
     * @param $mobile
     * @return null
     */
    private function getCacheCodes($mobile)
    {
        $codes = XMemcache::instance('sms')->get($mobile);

        if (!$codes)
            return [];

        foreach ($codes as $index => $item) {
            // 过滤发送超过1小时的验证码
            if (time() - $item['time'] > 3600) {
                unset($codes[$index]);
            }
        }

        // 重置数组索引
        $codes = array_values($codes);

        // 更新缓存
        XMemcache::instance('sms')->set($mobile, $codes);
        return $codes;
    }

    /**
     * 验证是不否允许发送
     * @param $mobile
     * @throws Exception
     */
    private function validateSend($mobile)
    {
        $codes = $this->getCacheCodes($mobile);
        if ($this->config['times'] > 0 && count($codes) >= $this->config['times']) {
            throw new Exception('一小时内最多只能发送' . $this->config['times'] . '次短信验证码');
        }

        $lastCode = end($codes);
        if ($this->config['interval'] > 0 && time() - $lastCode['time'] <= $this->config['interval']) {
            throw new Exception('发送频率太快');
        }
    }
  1. 函数 getCacheCodes() 获取指定手机1小时内发送的验证码。
  2. 函数 validateSend() 实现:
    验证一小时内向同一手机发送短信验证码次数是否超过了配置次数;
    验证上次发送验证码是否已经超过配置时间;

然后在 send() 函数中,所有代码之前插入代码 $this->validateSend($mobile);
send() 函数完整代码:

    /**
     * 发送短信验证码
     * @param $mobile
     * @return array
     * @throws Exception
     */
    public function send($mobile)
    {

        // 验证短信发送次数
        $this->validateSend($mobile);

        $item = [
            'code' => $this->generateCode(), // 生成短信验证码
            'time' => time(), // 生成时间
            'verified' => 0,  // 验证状态: 0 未验证, 1 已验证
        ];

        // 缓存短信验证码
        XMemcache::instance('sms')->append($mobile, $item, 3600);

        // 发送短信验证码
        /* ... 调用第三方接口 ... */

        return $item;
    }
  1. 再在控制器 SmsController 类的 sendAction() 函数中加入以下代码:
        // 发送验证码
        $result = SMS::instance()->send($this->getPost('user_mobile'));

        if ($result) {
//            Output::instance($this->response)->success(’发送成功‘);
            Output::instance($this->response)->success((object)$result);
        } else {
            Output::instance($this->response)->fail('发送失败');
        }

这里没有真正实现调用第三方接口,而是直接返回了发送的验证码,以供测试使用。正式代码,只返回发送状态。

SmsController.php 完整代码:

<?php

class SmsController extends BaseController
{

    /**
     * 发送短验证码
     */
    public function sendAction()
    {
        // 验证请求方法是否是POST
        $this->isPost();

        // 验证请求参数
        XValidationSms::send($this->getPost());

        // 发送验证码
        $result = SMS::instance()->send($this->getPost('user_mobile'));

        if ($result) {
//            Output::instance($this->response)->success(’发送成功‘);
            Output::instance($this->response)->success((object)$result);
        } else {
            Output::instance($this->response)->fail('发送失败');
        }

    }
}

接口调试示例

{
    "status": "1",
    "value": "发送成功"
}

开发调试时返回数据 :

{
    "status": "1",
    "item": {
        "code": "1139",
        "time": "1520663958",
        "verified": "0"
    }
}
接口调试示例图

示例代码下载
链接:https://pan.baidu.com/s/1gvyi8eX3JdlEUzzndpLVoQ 密码:839z

上一篇下一篇

猜你喜欢

热点阅读