本书未发布

42. 发送短信

未匹配的标注

简介

在本节里,我们先完成短信发送功能,然后在下一节介绍如何把该功能应用到用户注册表单里。

需求分解

和一般网站和 APP 发送的验证短信一样,用户注册时我们发送的验证短信也有个时效性( 60 秒),如果用户不在 60 秒内完成注册验证收到的验证码将过期。因为给用户手机发送的验证码只有 60 秒时间,所以在开发过程中我们不打算创建数据模型和数据表来存储该信息,而是用 缓存 来在存储给用户发送的验证码。在使用缓存存储验证码时,我们选择的缓存类型是 redis

另外,社区里目前已经有很多优秀的 SMS 扩展包,例如 easy-sms 就是其中之一。easy-sms安正超 写的一个短信发送扩展包,该组件对接了国内绝大多数短信服务平台的短信发送功能,接下来我们将使用该组件实现短信发送功能。

扩展包

安装EasySms:

$ composer require overtrue/easy-sms

安装完成后,我们在 .env 文件里添加使用云片平台的配置参数,如下行代码所示我们把 API key 和 『签名』 都存储在配置文件里。

.env

.
.
.
# 云片
YUNPIAN_API_KEY=215fexxxxxxxxxxxx476c39
YUNPIAN_SIGNATURE=【Lbbs社区】

.env.example 中也加入相同配置示例,提交到版本库,方便以后部署:

.env.example

.
.
.
# 云片
YUNPIAN_API_KEY=
YUNPIAN_SIGNATURE=

接下来创建 easy-sms 需要的配置文件:

config/easysms.php

<?php
return [
    // HTTP 请求的超时时间(秒)
    'timeout' => 5.0,

    // 默认发送配置
    'default' => [
        // 网关调用策略,默认:顺序调用
        'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class,

        // 默认可用的发送网关
        'gateways' => [
            'yunpian',
        ],
    ],
    // 可用的网关配置
    'gateways' => [
        'errorlog' => [
            'file' => '/tmp/easy-sms.log',
        ],
        'yunpian' => [
            'api_key' => env('easysms.yunpian_api_key'),
            'signature' => env('easysms.yunpian_signature'),
        ],
    ],
];

缓存配置

如前面分析所说,我们决定使用 redis 来存储给注册手机发送的验证码,所以我们把项目缓存配置修改成支持多种格式。

首先,在 .env 文件里添加 redis 配置信息:

.env

.
.
.
[REDIS]
HOST=127.0.0.1
PORT=6379
EXPIRE=0
PREFIX=think_bbs_

同样,我们需要把上面这些配置添加到 .env.sample 文件里:

.env.sample

.
.
.
[REDIS]
HOST=
PORT=
EXPIRE=
PREFIX=

接下来修改项目缓存配置文件,在这里我们修改的 应用配置 ,使整个项目支持多种缓存格式。

config/cache.php

return [
    // 使用复合缓存类型
    'type'  =>  'complex',
    // 默认使用的缓存
    'default'   =>  [
        // 全局缓存有效期(0为永久有效)
        'expire'=>  0,
        // 驱动方式
        'type'   => 'file',
        // 缓存保存目录
        'path'   => '../runtime/default',
    ],
    // 文件缓存
    'file'   =>  [
        // 驱动方式
        'type'   => 'file',
        // 设置不同的缓存保存目录
        'path'   => '../runtime/file/',
    ],
    // redis缓存
    'redis'   =>  [
        // 驱动方式
        'type'   => 'redis',
        // 全局缓存有效期(0为永久有效)
        'expire'=>  env('cache.redis_expire', 0),
        // 缓存前缀
        'prefix'=>  env('cache.redis_prefix', 'think_'),
        // 服务器地址
        'host'       => env('cache.redis_host', '127.0.0.1'),
        'port'       => env('cache.redis_port', 6379),
    ],
];

数据模型

我们创建一个模型类专门负责短信发送功能:

$ php think make:model common/Sms

Sms类代码如下:

application/common/model/Sms.php

<?php

namespace app\common\model;

use think\facade\Cache;
use Overtrue\EasySms\EasySms;
use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;

class Sms
{
    protected $easysms;

    public function __construct()
    {
        $cfg = Config::get('easysms.');
        if(!is_array($cfg)){
            $cfg = [];
        }
        $this->easysms = new EasySms($cfg);
    }

    /**
     * 发送短信
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2019-02-15
     * @param    string             $mobile 手机号码
     * @return   Object                     FunctionResult
     */
    public function sendCode($mobile)
    {
        $code = mt_rand(100000, 999999);
        $content = '您的验证码是'.$code.'。如非本人操作,请忽略本短信';
        $this->sendByYunPian($mobile, $content);
        // 短信发送成功后把发送的验证码保存在redis里
        Cache::store('redis')->set($mobile, $code, 60);
        return true;
    }

    /**
     * 云片平台发送短信方法
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2019-06-11
     * @param    string             $mobile  手机号码
     * @param    string             $content 短信内容
     * @return   array                       [description]
     */
    private function sendByYunPian($mobile, $content)
    {
        try {
            $result = $this->easysms->send($mobile, [
                'content'  => $content,
            ]);
        } catch (NoGatewayAvailableException $exception) {
            throw new \Exception($exception->getException('yunpian')->getMessage());
        }

        return $result;
    }
}

代码解读

  • Sms 它不继承 think\Model;
  • 在 Sms 里我们给用户手机发送的验证码是一个 100000 到 999999 之间的一个六位数字验证码;
  • smdCode 方法里的短信内容格式必须和你在云片平台定义的已通过审核模板模板相同,否则短信会发送失败。

测试短信发送功能

接下来我们添加一个控制( action )方法调用上面编写的短信发送功能( 注意本段编写的代码只是为了测试发送功能,在最后一章我们将删除这个控制器 )。

$ php think make:controller index/Demo

新建控制器代码如下:

application/index/controller/Demo.php

<?php
namespace app\index\controller;

use common\model\Sms;
class Demo
{
    public function sms()
    {
        $mobile = '15012335678';
        $sms = new Sms();
        $res = $sms->sendCode($mobile);
        return json($res);
    }
}

调用结果截图

添加测试方法

因为每次调用平台的短信发送功能需要花费一定的费用,而我们在开发测试过程中有可能需要多次调用发送短信功能,所以我们打算通过给项目添加一个配置参数实现在开发模式下跳过调用平台发送接口功能。

首先,在 .env 文件 app 部分里添加一个配置参数ENV记录当前开发模式:

.env

[APP]
ENV=development
NAME=ThinkBBS
.
.
.

同理,在 .env.sample app 部分添加该参数,并且默认设置成 production 。.

.env.sample

[APP]
ENV=production
NAME=ThinkBBS
.
.
.

然后在 config/app.php 文件添加该配置参数:

config/app.php

<?php
return [
    // 部署环境
    'env' => env('app.env', 'production'),
    // 应用名称
    'app_name'               => env('app.name', 'ThinkBBS'),
    .
    .
    .
]

最后,修改 sendCode 方法,当 app.env=production 时才调用短信发送接口。

application/common/model/Sms.php

<?php
.
.
.
use think\facade\Config;

class Sms
{
    ...
    /**
     * 发送短信
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2019-02-15
     * @param    string             $mobile 手机号码
     * @return   boolean                    FunctionResult
     */
    public function sendCode($mobile)
    {
        $app_env = Config::get('app.env');
        if($app_env == 'production'){
            $code = mt_rand(100000, 999999);
            $content = '您的验证码是'.$code.'。如非本人操作,请忽略本短信';
            $this->sendByYunPian($mobile, $content);
        }else{
            $code = 123456;
        }

        Cache::store('redis')->set($mobile, $code, 60);
        return true;
    }
    ...
}

注意: 不要忘记在 Sms 类的顶部引用 think\facade\Config

由于我们现在开发过程中设置的 ENV=development ,所以现在每次调用该方法存储的手机验证码都是 123456 并且不会调用平台短信发送接口。

提交代码

下面把代码纳入到版本管理:

$ git add -A
$ git commit '发送短信'

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~