laravel框架使用阿里云短信发送消息操作示例


Posted in PHP onFebruary 15, 2020

本文实例讲述了laravel框架使用阿里云短信发送消息操作。分享给大家供大家参考,具体如下:

最新需要用到发送短信的功能,所以就在网上搜索一些写好的扩展。

扩展地址:

https://github.com/MissMyCat/aliyun-sms

通过composer安装:

composer require mrgoon/aliyun-sms dev-master

在 config/app.php 中 providers 加入:

Mrgoon\AliSms\ServiceProvider::class,

有需求的可以自行添加 aliases。

然后在控制台运行 :

php artisan vendor:publish

默认会在 config 目录下创建一个 aliyunsms.php 文件:

<?php
return [
  'access_key' => env('ALIYUN_SMS_AK'), // accessKey
  'access_secret' => env('ALIYUN_SMS_AS'), // accessSecret
  'sign_name' => env('ALIYUN_SMS_SIGN_NAME'), // 签名
];

然后在 .env 中配置相应参数:

ALIYUN_SMS_AK=
ALIYUN_SMS_AS=
ALIYUN_SMS_SIGN_NAME=

为了能够方便的发送短信,我们可以在 app 目录下,创建一个Services目录,并添加 AliyunSms.php 文件。

<?php
namespace App\Services;
use Mrgoon\AliSms\AliSms;
/**
 * 阿里云短信类
 */
class AliyunSms
{
  //验证码
  const VERIFICATION_CODE = 'verification_code';
  //模板CODE
  public static $templateCodes = [
    self::VERIFICATION_CODE => 'SMS_XXXXXXXXXX',
  ];
  /**
   * 发送短信
   */
  public static function sendSms($mobile, $scene, $params = [])
  {
    if (empty($mobile)) {
      throw new \Exception('手机号不能为空');
    }
    if (empty($scene)) {
      throw new \Exception('场景不能为空');
    }
    if (!isset(self::$templateCodes[$scene])) {
      throw new \Exception('请配置场景的模板CODE');
    }
    $template_code = self::$templateCodes[$scene];
    try {
      $ali_sms = new AliSms();
      $response = $ali_sms->sendSms($mobile, $template_code, $params);
      if ($response->Code == 'OK') {
        return true;
      }
      throw new \Exception($response->Message);
    } catch (\Throwable $e) {
      throw new \Exception($e->getMessage());
    }
  }
}

为了能够记录每次短信发送的状态,我们可以创建一个 sms_logs 表。

CREATE TABLE `sms_logs` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
 `type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '类型(0:短信验证码,1:语音验证码,2:短信消息通知)',
 `mobile` varchar(16) NOT NULL DEFAULT '' COMMENT '手机号',
 `code` varchar(12) NOT NULL DEFAULT '' COMMENT '验证码',
 `checked` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否验证(0:未验证,1:已验证)',
 `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态(0:未发送,1:已发送,2:发送失败)',
 `reason` varchar(255) NOT NULL DEFAULT '' COMMENT '失败原因',
 `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
 `operator_id` int(11) NOT NULL DEFAULT '0' COMMENT '操作人ID',
 `ip` varchar(16) NOT NULL DEFAULT '' COMMENT '操作IP',
 `created` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
 `updated` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='短信表';

然后针对该表,我们创建一个 SmsLog 模型来管理。

<?php
namespace App\Models;
use App\Services\AliyunSms;
class SmsLog extends Model
{
  protected $fillable = [
    'type',
    'mobile',
    'code',
    'checked',
    'status',
    'reason',
    'remark',
    'operator_id',
    'ip',
  ];
  //类型(0:短信验证码,1:语音验证码,2:短信消息通知)
  const TYPE_CODE = 0;
  const TYPE_VOICE = 1;
  const TYPE_MESSAGE = 2;
  //是否验证(0:未验证,1:已验证)
  const CHECKED_UNVERIFIED = 0;
  const CHECKED_VERIFIED = 1;
  //状态(0:未发送,1:已发送,2:发送失败)
  const STATUS_NO_SEND = 0;
  const STATUS_SEND = 1;
  const STATUS_FAIL = 2;
  //短信发送间隔时间,默认60秒
  const SEND_INTERVAL_TIME = 60;
  /**
   * 检测短信验证码
   */
  protected function checkCode($mobile, $code)
  {
    if (!$mobile) {
      throw new \Exception('手机号不能为空');
    }
    if (!checkMobile($mobile)) {
      throw new \Exception('手机号不正确');
    }
    if (!$code) {
      throw new \Exception('验证码不能为空');
    }
    $sms_log = $this->where([
      ['type', self::TYPE_CODE],
      ['mobile', $mobile],
      ['status', self::STATUS_SEND],
      ['checked', self::CHECKED_UNVERIFIED],
    ])->orderBy('created', 'desc')->first();
    if (!$sms_log) {
      throw new \Exception('验证码不存在,请重新获取');
    }
    if ($code != $sms_log->code) {
      throw new \Exception('验证码错误');
    }
    $sms_log->checked = self::CHECKED_VERIFIED;
    $sms_log->save();
    return true;
  }
  /**
   * 检测短信频率
   */
  protected function checkRate($mobile)
  {
    if (!$mobile) {
      throw new \Exception('手机号不能为空');
    }
    $sms_log = $this->where([
      ['mobile', $mobile],
      ['status', self::STATUS_SEND],
    ])->orderBy('created', 'desc')->first();
    $now = time();
    if ($sms_log) {
      if (($now - strtotime($sms_log->created)) < self::SEND_INTERVAL_TIME) {
        throw new \Exception('短信发送太频繁,请稍后再试');
      }
    }
    return true;
  }
  /**
   * 发送短信验证码
   */
  protected function sendVerifyCode($mobile)
  {
    self::checkRate($mobile);
    $code = mt_rand(1000, 9999);
    $sms_log = $this->create([
      'type' => self::TYPE_CODE,
      'mobile' => $mobile,
      'code' => $code,
      'checked' => self::CHECKED_UNVERIFIED,
      'status' => self::STATUS_NO_SEND,
      'ip' => getRealIp(),
    ]);
    try {
      AliyunSms::sendSms($mobile, AliyunSms::VERIFICATION_CODE, ['code' => $code]);
      $sms_log->status = self::STATUS_SEND;
      $sms_log->save();
      return true;
    } catch (\Exception $e) {
      $sms_log->status = self::STATUS_FAIL;
      $sms_log->reason = $e->getMessage();
      $sms_log->save();
      throw new \Exception($e->getMessage());
    }
  }
}

这样,我们就可以在项目中通过 SmsLog::sendVerifyCode() 发送短信了。

getRealIp() 和 checkMobile() 方法为公共方法,存放在 app/Helpers 的 functions.php 中。

/**
 * 获取真实IP地址
 */
function getRealIp()
{
  $ip = false;
  if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown")) {
    $ip = getenv("HTTP_CLIENT_IP");
  } else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown")) {
    $ips = explode(", ", getenv("HTTP_X_FORWARDED_FOR"));
    if ($ip) {
      array_unshift($ips, $ip);
      $ip = false;
    }
    $ipscount = count($ips);
    for ($i = 0; $i < $ipscount; $i++) {
      if (!preg_match("/^(10|172\.16|192\.168)\./i", $ips[$i])) {
        $ip = $ips[$i];
        break;
      }
    }
  } else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown")) {
    $ip = getenv("REMOTE_ADDR");
  } else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")) {
    $ip = $_SERVER['REMOTE_ADDR'];
  } else {
    $ip = "unknown";
  }
  return isIp($ip) ? $ip : "unknown";
}
/**
 * 检查是否是合法的IP
 */
function isIp($ip)
{
  if (preg_match('/^((\d|[1-9]\d|2[0-4]\d|25[0-5]|1\d\d)(?:\.(\d|[1-9]\d|2[0-4]\d|25[0-5]|1\d\d)){3})$/', $ip)) {
    return true;
  } else {
    return false;
  }
}
/**
 * 验证手机号
 */
function checkMobile($mobile)
{
  return preg_match('/^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\d{8}$/i', $mobile);
}

希望本文所述对大家基于Laravel框架的PHP程序设计有所帮助。

PHP 相关文章推荐
PHP 数据结构 算法描述 冒泡排序 bubble sort
Jul 10 PHP
PHP学习笔记 用户注册模块用户类以及验证码类
Sep 20 PHP
php生成略缩图代码
Jul 16 PHP
ThinkPHP缓存方法S()概述
Jun 13 PHP
ThinkPHP多表联合查询的常用方法
Mar 24 PHP
ThinkPHP实现将本地文件打包成zip下载
Jun 26 PHP
php生成shtml类用法实例
Dec 09 PHP
PHP中防止SQL注入方法详解
Dec 25 PHP
php+html5+ajax实现上传图片的方法
May 14 PHP
详解PHP归并排序的实现
Oct 18 PHP
PHP连接MySQL进行增、删、改、查操作
Feb 19 PHP
php图形jpgraph操作实例分析
Feb 22 PHP
laravel框架实现敏感词汇过滤功能示例
Feb 15 #PHP
PHP 枚举类型的管理与设计知识点总结
Feb 13 #PHP
PHP+ajax实现上传、删除、修改单张图片及后台处理逻辑操作详解
Feb 12 #PHP
分享8个Laravel模型时间戳使用技巧小结
Feb 12 #PHP
tp5.0框架隐藏index.php入口文件及模块和控制器的方法分析
Feb 11 #PHP
thinkphp5框架路由原理与用法详解
Feb 11 #PHP
php数组指针函数功能及用法示例
Feb 11 #PHP
You might like
简单的PHP缓存设计实现代码
2011/09/30 PHP
PHP实现取得HTTP请求的原文
2014/08/18 PHP
smarty模板引擎之内建函数用法
2015/03/30 PHP
php生成图片缩略图的方法
2015/04/07 PHP
JS解密入门 最终变量劫持
2008/06/25 Javascript
jquery获取特定name所有选中的checkbox,支持IE9标准模式
2013/03/18 Javascript
js换图片效果可进行定时操作
2014/06/09 Javascript
javascript实现简单的进度条
2015/07/02 Javascript
浅谈javascript中关于日期和时间的基础知识
2016/07/13 Javascript
NodeJS远程代码执行
2016/08/28 NodeJs
js仿搜狐视频记录片列表展示效果
2020/05/30 Javascript
干货!教大家如何选择Vue和React
2017/03/13 Javascript
jQuery日程管理控件glDatePicker用法详解
2017/03/29 jQuery
使用node打造自己的命令行工具方法教程
2018/03/26 Javascript
小程序使用watch监听数据变化的方法详解
2019/09/20 Javascript
如何使用原生Js实现随机点名详解
2021/01/06 Javascript
[50:24]VGJ.S vs Pain 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
python自定义异常实例详解
2017/07/11 Python
python Celery定时任务的示例
2018/03/13 Python
Linux下python制作名片示例
2018/07/20 Python
浅析python3字符串格式化format()函数的简单用法
2018/12/07 Python
Python守护进程实现过程详解
2020/02/10 Python
pytorch使用tensorboardX进行loss可视化实例
2020/02/24 Python
python进度条显示之tqmd模块
2020/08/22 Python
HTML5 声明兼容IE的写法
2011/05/16 HTML / CSS
美国批发零售网站:GearXS
2016/07/26 全球购物
美赞臣营养马来西亚旗舰店:Enfagrow马来西亚
2019/07/26 全球购物
成人大专生实习期的自我评价
2013/10/02 职场文书
校领导推荐信
2013/11/01 职场文书
公务员总结性个人自我评价
2013/12/05 职场文书
槐乡的孩子教学反思
2014/04/27 职场文书
Nginx反爬虫策略,防止UA抓取网站
2021/03/31 Servers
Python爬虫之自动爬取某车之家各车销售数据
2021/06/02 Python
Python合并多张图片成PDF
2021/06/09 Python
用python修改excel表某一列内容的操作方法
2021/06/11 Python
Node.js实现断点续传
2021/06/23 Javascript