提交 7982d0cf 编写于 作者: 李光春's avatar 李光春

ThinkPHP6七牛云短信扩展包

上级
## v6.0.0 / 2021-06-24
- ThinkPHP6七牛云短信扩展包
\ No newline at end of file
MIT License
Copyright (c) 2021 李光春
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
<img align="right" width="100" src="https://kodo-cdn.dtapp.net/04/999e9f2f06d396968eacc10ce9bc8a.png" alt="www.dtapp.net"/>
<h1 align="left"><a href="https://www.dtapp.net/">ThinkPHP6七牛云短信扩展包</a></h1>
📦 ThinkPHP6七牛云短信扩展包
[![Code Health](https://hn.devcloud.huaweicloud.com/codecheck/v1/codecheck/task/codehealth.svg?taskId=9046b097b0164105b8a3078be4d2fa95)](https://hn.devcloud.huaweicloud.com/codecheck/project/b7a03c9ea96e40cb93fed6e23a27a7be/codecheck/task/9046b097b0164105b8a3078be4d2fa95/detail)
[![Latest Stable Version](https://poser.pugx.org/dtapps/think-qiniu-sms/v/stable)](https://packagist.org/packages/dtapps/think-qiniu-sms)
[![Latest Unstable Version](https://poser.pugx.org/dtapps/think-qiniu-sms/v/unstable)](https://packagist.org/packages/dtapps/think-qiniu-sms)
[![Total Downloads](https://poser.pugx.org/dtapps/think-qiniu-sms/downloads)](https://packagist.org/packages/dtapps/think-qiniu-sms)
[![License](https://poser.pugx.org/dtapps/think-qiniu-sms/license)](https://packagist.org/packages/liguangchun/think-library)
[![PHP Version](https://img.shields.io/badge/php-%3E%3D7.1-8892BF.svg)](http://www.php.net/)
[![Build Status](https://travis-ci.org/GC0202/ThinkLibrary.svg?branch=6.0)](https://travis-ci.org/dtapps/think-qiniu-sms)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/dtapps/think-qiniu-sms/badges/quality-score.png?b=6.0)](https://scrutinizer-ci.com/g/dtapps/think-qiniu-sms/?branch=6.0)
[![Code Coverage](https://scrutinizer-ci.com/g/dtapps/think-qiniu-sms/badges/coverage.png?b=6.0)](https://scrutinizer-ci.com/g/dtapps/think-qiniu-sms/?branch=6.0)
## 依赖环境
1. PHP7.1 版本及以上
## 托管
- 国外仓库地址:[https://github.com/dtapps/think-qiniu-sms](https://github.com/dtapps/think-qiniu-sms)
- 国内仓库地址:[https://gitee.com/dtapps/think-qiniu-sms](https://gitee.com/dtapps/think-qiniu-sms)
- Packagist
地址:[https://packagist.org/packages/dtapps/think-qiniu-sms](https://packagist.org/packages/dtapps/think-qiniu-sms)
### 开发版
```text
composer require dtapps/think-qiniu-sms ^6.x-dev -vvv
```
### 稳定版
```text
composer require dtapps/think-qiniu-sms ^6.0.* -vvv
```
## 更新
```text
composer update dtapps/think-qiniu-sms -vvv
```
## 删除
```text
composer remove dtapps/think-qiniu-sms -vvv
```
## 获取电脑Mac地址服务使用示例
```php
use dtapps\qiniu\sms\SmsService;
list($ret, $err) = SmsService::instance()
->setParam([
"template_id" => "",
"mobiles" => [""],
"parameters" => [
"code" => ""
],
])
->accessKey("")
->secretKey("")
->toArray();
var_dump($ret);
var_dump($err);
```
\ No newline at end of file
{
"name": "dtapps/think-qiniu-sms",
"description": "ThinkPHP6七牛云短信扩展包",
"keywords": [
"dtapp",
"qiniu",
"sms",
"think",
"thinkphp"
],
"time": "2020-06-23",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Chaim",
"email": "gc@liguangchun.cn",
"homepage": "https://api.dtapp.net"
}
],
"homepage": "https://www.dtapp.net",
"require": {
"php": ">=7.1",
"ext-json": "*",
"topthink/framework": "^6.0"
},
"autoload": {
"psr-4": {
"dtapps\\qiniu\\sms\\": "src"
}
},
"extra": {
"think": {
"services": [
"dtapps\\qiniu\\sms\\Service"
]
}
},
"repositories": {
"packagist": {
"type": "composer",
"url": "https://mirrors.aliyun.com/composer/"
}
}
}
<?php
declare (strict_types=1);
namespace dtapps\qiniu\sms;
/**
* Class Config
* @package dtapps\qiniu\sms
*/
final class Config
{
const SDK_VER = '1.0.0';
const SMS_HOST = "https://sms.qiniuapi.com";
const SMS_VERSION = 'v1';
}
\ No newline at end of file
<?php
declare (strict_types=1);
namespace dtapps\qiniu\sms;
/**
* Class Error
* @package dtapps\qiniu\sms
*/
final class Error
{
private $url;
private $response;
public function __construct($url, $response)
{
$this->url = $url;
$this->response = $response;
}
public function code()
{
return $this->response->statusCode;
}
public function getResponse()
{
return $this->response;
}
public function message()
{
return $this->response->error;
}
}
\ No newline at end of file
<?php
declare (strict_types=1);
namespace dtapps\qiniu\sms;
/**
* Class Request
* @package dtapps\qiniu\sms
*/
final class Request
{
public $url;
public $headers;
public $body;
public $method;
public function __construct($method, $url, array $headers = array(), $body = null)
{
$this->method = strtoupper($method);
$this->url = $url;
$this->headers = $headers;
$this->body = $body;
}
}
\ No newline at end of file
<?php
declare (strict_types=1);
namespace dtapps\qiniu\sms;
/**
* Class Response
* @package dtapps\qiniu\sms
*/
final class Response
{
public $statusCode;
public $headers;
public $body;
public $error;
private $jsonData;
public $duration;
/** @var array Mapping of status codes to reason phrases */
private static $statusTexts = array(
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
208 => 'Already Reported',
226 => 'IM Used',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
307 => 'Temporary Redirect',
308 => 'Permanent Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
425 => 'Reserved for WebDAV advanced collections expired proposal',
426 => 'Upgrade required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
506 => 'Variant Also Negotiates (Experimental)',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
510 => 'Not Extended',
511 => 'Network Authentication Required',
);
/**
* @param int $code 状态码
* @param double $duration 请求时长
* @param array $headers 响应头部
* @param string|null $body 响应内容
* @param string|null $error 错误描述
*/
public function __construct(int $code, float $duration, array $headers = array(), string $body = null, string $error = null)
{
$this->statusCode = $code;
$this->duration = $duration;
$this->headers = $headers;
$this->body = $body;
$this->error = $error;
$this->jsonData = null;
if ($error !== null) {
return;
}
if ($body === null) {
if ($code >= 400) {
$this->error = self::$statusTexts[$code];
}
return;
}
if (self::isJson($headers)) {
try {
$jsonData = self::bodyJson($body);
if ($code >= 400) {
$this->error = $body;
if ($jsonData['error'] !== null) {
$this->error = $jsonData['error'];
}
}
$this->jsonData = $jsonData;
} catch (\InvalidArgumentException $e) {
$this->error = $body;
if ($code >= 200 && $code < 300) {
$this->error = $e->getMessage();
}
}
} elseif ($code >= 400) {
$this->error = $body;
}
}
public function json()
{
return $this->jsonData;
}
private static function bodyJson($body)
{
return json_decode((string)$body, true);
}
public function xVia()
{
$via = $this->headers['X-Via'];
if ($via === null) {
$via = $this->headers['X-Px'];
}
if ($via === null) {
$via = $this->headers['Fw-Via'];
}
return $via;
}
public function xLog()
{
return $this->headers['X-Log'];
}
public function xReqId()
{
return $this->headers['X-Reqid'];
}
/**
* @return bool
*/
public function ok(): bool
{
return $this->statusCode >= 200 && $this->statusCode < 300 && $this->error === null;
}
public function needRetry()
{
$code = $this->statusCode;
if ($code < 0 || ($code / 100 === 5 and $code !== 579) || $code === 996) {
return true;
}
}
private static function isJson($headers)
{
return array_key_exists('content-type', $headers) || array_key_exists('Content-Type', $headers) &&
strpos($headers['Content-Type'], 'application/json') === 0;
}
}
\ No newline at end of file
<?php
declare (strict_types=1);
namespace dtapps\qiniu\sms;
use think\App;
use think\Container;
/**
* 自定义服务基类
* Class Service
* @package asw
*/
abstract class Service
{
/**
* 应用实例
* @var App
*/
protected $app;
/**
* Service constructor.
* @param App $app
*/
public function __construct(App $app)
{
$this->app = $app;
$this->initialize();
}
/**
* 初始化服务
* @return static
*/
protected function initialize(): Service
{
return $this;
}
/**
* 静态实例对象
* @param array $var 实例参数
* @param boolean $new 创建新实例
* @return static
*/
public static function instance(array $var = [], bool $new = false): Service
{
return Container::getInstance()->make(static::class, $var, $new);
}
}
\ No newline at end of file
<?php
declare (strict_types=1);
namespace dtapps\qiniu\sms;
use think\exception\HttpException;
/**
* Class SmsService
* @package dtapps\qiniu\sms
*/
class SmsService extends Service
{
private $accessKey, $secretKey, $body;
private $method = "POST";
private $headers = [
"content-type" => "application/json"
];
private $baseURL = Config::SMS_HOST . "/" . Config::SMS_VERSION . "/message";
/**
* @param string $value
* @return $this
*/
public function accessKey(string $value): self
{
$this->accessKey = $value;
return $this;
}
/**
* @param string $value
* @return $this
*/
public function secretKey(string $value): self
{
$this->secretKey = $value;
return $this;
}
/**
* 请求参数
* @param array $params
* @return $this
*/
public function setParam(array $params): self
{
$this->body = json_encode($params);
return $this;
}
/**
* 请求接口
* @param string $type
* @return $this
*/
private function setUrl(string $type): self
{
$this->baseURL .= $type;
return $this;
}
/**
* 请求类型
* @param string $type
* @return $this
*/
private function setMethod(string $type): self
{
$this->method = $type;
return $this;
}
/**
* @param $name
* @param $value
* @return $this
*/
private function withHeader($name, $value): self
{
$this->headers = array_merge($this->headers, [$name => $value]);
return $this;
}
/**
* 返回数组数据
* @return array
*/
public function toArray(): array
{
//首先检测是否支持curl
if (!extension_loaded("curl")) {
throw new HttpException(404, '请开启curl模块!');
}
// 签名
$this->headers["Authorization"] = $this->authorizationV2($this->baseURL, $this->method, $this->body, $this->headers["content-type"]);
$this->headers['Content-Type'] = $this->headers["content-type"];
// 准备请求
$request = new Request($this->method, $this->baseURL, $this->headers, $this->body);
// 发送请求
$ret = $this->sendRequest($request);
if (!$ret->ok()) {
return array(null, new Error($this->baseURL, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
/**
* 网络请求
* @param $request
* @return Response
*/
public function sendRequest($request): Response
{
$t1 = microtime(true);
$ch = curl_init();
$options = array(
CURLOPT_USERAGENT => $this->userAgent(),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_CUSTOMREQUEST => $request->method,
CURLOPT_URL => $request->url,
);
// Handle open_basedir & safe mode
if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
$options[CURLOPT_FOLLOWLOCATION] = true;
}
if (!empty($request->headers)) {
$headers = array();
foreach ($request->headers as $key => $val) {
array_push($headers, "$key: $val");
}
$options[CURLOPT_HTTPHEADER] = $headers;
}
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
if (!empty($request->body)) {
$options[CURLOPT_POSTFIELDS] = $request->body;
}
curl_setopt_array($ch, $options);
$result = curl_exec($ch);
$t2 = microtime(true);
$duration = round($t2 - $t1, 3);
$ret = curl_errno($ch);
if ($ret !== 0) {
$r = new Response(-1, $duration, array(), null, curl_error($ch));
curl_close($ch);
return $r;
}
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = $this->parseHeaders(substr($result, 0, $header_size));
$body = substr($result, $header_size);
curl_close($ch);
return new Response($code, $duration, $headers, $body, null);
}
private function userAgent(): string
{
$sdkInfo = "QiniuPHP/" . Config::SDK_VER;
$systemInfo = php_uname("s");
$machineInfo = php_uname("m");
$envInfo = "($systemInfo/$machineInfo)";
$phpVer = phpversion();
return "$sdkInfo $envInfo PHP/$phpVer";
}
/**
* @param $raw
* @return array
*/
private function parseHeaders($raw): array
{
$headers = array();
$headerLines = explode("\r\n", $raw);
foreach ($headerLines as $line) {
$headerLine = trim($line);
$kv = explode(':', $headerLine);
if (count($kv) > 1) {
$kv[0] = $this->ucwordsHyphen($kv[0]);
$headers[$kv[0]] = trim($kv[1]);
}
}
return $headers;
}
/**
* @param $str
* @return array|string|string[]
*/
private function ucwordsHyphen($str)
{
return str_replace('- ', '-', ucwords(str_replace('-', '- ', $str)));
}
/**
* @param $url
* @param $method
* @param null $body
* @param null $contentType
* @return string
*/
public function authorizationV2($url, $method, $body = null, $contentType = null): string
{
$urlItems = parse_url($url);
$host = $urlItems['host'];
if (isset($urlItems['port'])) {
$port = $urlItems['port'];
} else {
$port = '';
}
$path = $urlItems['path'];
if (isset($urlItems['query'])) {
$query = $urlItems['query'];
} else {
$query = '';
}
//write request uri
$toSignStr = $method . ' ' . $path;
if (!empty($query)) {
$toSignStr .= '?' . $query;
}
//write host and port
$toSignStr .= "\nHost: " . $host;
if (!empty($port)) {
$toSignStr .= ":" . $port;
}
//write content type
if (!empty($contentType)) {
$toSignStr .= "\nContent-Type: " . $contentType;
}
$toSignStr .= "\n\n";
//write body
if (!empty($body)) {
$toSignStr .= $body;
}
$sign = $this->sign($toSignStr);
return 'Qiniu ' . $sign;
}
private function sign($data): string
{
$hmac = hash_hmac('sha1', $data, $this->secretKey, true);
return $this->accessKey . ':' . $this->base64_urlSafeEncode($hmac);
}
private function base64_urlSafeEncode(string $data): string
{
$find = array('+', '/');
$replace = array('-', '_');
return str_replace($find, $replace, base64_encode($data));
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册