提交 896eec44 编写于 作者: 增辉 提交者: GitHub

Merge pull request #1 from overtrue/master

拉取超哥的更新
......@@ -6,6 +6,7 @@ php:
- 7.1
- hhvm
dist: trusty
sudo: false
install: travis_retry composer install --no-interaction --prefer-source
......
......@@ -12,29 +12,58 @@
<a href="https://packagist.org/packages/overtrue/easy-sms"><img src="https://poser.pugx.org/overtrue/easy-sms/license" alt="License"></a>
</p>
## 特点
# 环境需求
1. 支持目前市面多家服务商
1. 一套写法兼容所有平台
1. 简单配置即可灵活增减服务商
1. 内置多种服务商轮询策略、支持自定义轮询策略
1. 统一的返回值格式,便于日志与监控
1. 自动轮询选择可用的服务商
1. 更多等你去发现与改进...
## 平台支持
- [云片](https://www.yunpian.com)
- [Submail](https://www.mysubmail.com)
- [螺丝帽](https://luosimao.com/)
- [阿里大于](https://www.alidayu.com/)
- [容联云通讯](http://www.yuntongxun.com)
- [互亿无线](http://www.ihuyi.com)
- [聚合数据](https://www.juhe.cn)
- [SendCloud](http://www.sendcloud.net/)
## 环境需求
- PHP >= 5.6
# 安装
## 安装
```shell
$ composer require "overtrue/easy-sms"
```
# 使用
## 使用
```php
use Overtrue\EasySms\EasySms;
$config = [
// HTTP 请求的超时时间(秒)
'timeout' => 5.0,
// 默认发送配置
'default' => [
// 网关调用策略,默认:顺序调用
'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class
// 默认可用的发送网关
'gateways' => [
'yunpian', 'alidayu',
],
],
// 可用的网关配置
'gateways' => [
'errorlog' => [
'file' => '/tmp/easy-sms.log',
......@@ -49,10 +78,58 @@ $config = [
];
$easySms = new EasySms($config);
$easySms->send(13188888888, 'hello world!');
$easySms->send(13188888888,
'content' => '您的验证码为: 6379',
'template' => 'SMS_001',
'data' => [
'code' => 6379
],
]);
```
# 定义短信
## 短信内容
由于使用多网关发送,所以一条短信要支持多平台发送,每家的发送方式不一样,但是我们抽象定义了以下公用属性:
- `content` 文字内容,使用在像云片类似的以文字内容发送的平台
- `template` 模板 ID,使用在以模板ID来发送短信的平台
- `data` 模板变量,使用在以模板ID来发送短信的平台
所以,在使用过程中你可以根据所要使用的平台定义发送的内容。
## 发送网关
默认使用 `default` 中的设置来发送,如果某一条短信你想要覆盖默认的设置。在 `send` 方法中使用第三个参数即可:
```php
$easySms->send(13188888888, [
'content' => '您的验证码为: 6379',
'template' => 'SMS_001',
'data' => [
'code' => 6379
],
], ['yunpian', 'juhe']); // 这里的网关配置将会覆盖全局默认值
```
## 返回值
由于使用多网关发送,所以返回值为一个数组,结构如下:
```php
[
'yunpian' => [
'status' => 'success',
'result' => [...] // 平台返回值
],
'juhe' => [
'status' => 'erred',
'exception' => \Overtrue\EasySms\Exceptions\GatewayErrorException 对象
],
//...
]
```
## 定义短信
你可以根本发送场景的不同,定义不同的短信类,从而实现一处定义多处调用,你可以继承 `Overtrue\EasySms\Message` 来定义短信模型:
......@@ -60,11 +137,14 @@ $easySms->send(13188888888, 'hello world!');
<?php
use Overtrue\EasySms\Message;
use Overtrue\EasySms\Contracts\GatewayInterface;
use Overtrue\EasySms\Strategies\OrderStrategy;
class OrderPaidMessage extends Messeage
{
protected $order;
protected $gateways = ['alidayu', 'yunpian']; // 定义本短信的适用平台,覆盖全局配置中的 `enabled_gateways`
protected $strategy = OrderStrategy::class; // 定义本短信的网关使用策略,覆盖全局配置中的 `default.strategy`
protected $gateways = ['alidayu', 'yunpian', 'juhe']; // 定义本短信的适用平台,覆盖全局配置中的 `default.gateways`
public function __construct($order)
{
......@@ -72,19 +152,19 @@ class OrderPaidMessage extends Messeage
}
// 定义直接使用内容发送平台的内容
public function getContent()
public function getContent(GatewayInterface $gateway = null)
{
return sprintf('您的订单:%s, 已经完成付款', $this->order->no);
}
// 定义使用模板发送方式平台所需要的模板 ID
public function getTemplate()
public function getTemplate(GatewayInterface $gateway = null)
{
return 'SMS_003';
}
// 模板参数
public function getData()
public function getData(GatewayInterface $gateway = null)
{
return [
'order_no' => $this->order->no
......@@ -104,17 +184,81 @@ $message = new OrderPaidMessage($order);
$easySms->send(13188888888, $message);
```
# 平台支持
## 各平台配置说明
### [阿里大于](https://www.alidayu.com/)
```php
'alidayu' => [
'app_key' => '',
'sign_name' => '',
],
```
### [云片](https://www.yunpian.com)
- [云片](https://github.com/overtrue/easy-sms/wiki/GateWays---Yunpian)
- [Submail](https://github.com/overtrue/easy-sms/wiki/GateWays---Submail)
- [螺丝帽](https://github.com/overtrue/easy-sms/wiki/GateWays---Luosimao)
- [阿里大鱼](https://github.com/overtrue/easy-sms/wiki/GateWays---AliDayu)
- [容联云通讯](https://github.com/overtrue/easy-sms/wiki/GateWays---Yuntongxun)
- [互亿无线](https://github.com/overtrue/easy-sms/wiki/GateWays---Huyi)
- [聚合数据](https://github.com/overtrue/easy-sms/wiki/GateWays---Juhe)
- SendCloud
```php
'yunpian' => [
'api_key' => '',
],
```
### [Submail](https://www.mysubmail.com)
```php
'submail' => [
'app_id' => '',
'app_key' => '',
'project' => '',
],
```
### [螺丝帽](https://luosimao.com/)
```php
'luosimao' => [
'api_key' => '',
],
```
### [容联云通讯](http://www.yuntongxun.com)
```php
'yuntongxun' => [
'app_id' => '',
'account_sid' => '',
'account_token' => '',
'is_sub_account' => false,
],
```
### [互亿无线](http://www.ihuyi.com)
```php
'huyi' => [
'api_id' => '',
],
```
### [聚合数据](https://www.juhe.cn)
```php
'juhe' => [
'app_key' => '',
],
```
### [SendCloud](http://www.sendcloud.net/)
```php
'sendcloud' => [
'sms_user' => '',
'sms_key' => '',
],
```
# License
## License
MIT
......@@ -33,7 +33,7 @@ class AlidayuGateway extends Gateway
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
* @param \Overtrue\EasySms\Support\Config $config
*
* @return mixed
* @return array
*
* @throws \Overtrue\EasySms\Exceptions\GatewayErrorException;
*/
......@@ -48,7 +48,7 @@ class AlidayuGateway extends Gateway
'sms_type' => 'normal',
'sms_free_sign_name' => $config->get('sign_name'),
'app_key' => $config->get('app_key'),
'sms_template_code' => $config->get('template_code'),
'sms_template_code' => $message->getTemplate(),
'rec_num' => strval($to),
'sms_param' => json_encode($message->getData()),
];
......
......@@ -22,7 +22,7 @@ class ErrorlogGateway extends Gateway
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
* @param \Overtrue\EasySms\Support\Config $config
*
* @return bool
* @return array
*/
public function send($to, MessageInterface $message, Config $config)
{
......@@ -39,6 +39,9 @@ class ErrorlogGateway extends Gateway
json_encode($message->getData())
);
return error_log($message, 3, $this->config->get('file', ini_get('error_log')));
$file = $this->config->get('file', ini_get('error_log'));
$status = error_log($message, 3, $file);
return compact('status', 'file');
}
}
......@@ -32,7 +32,7 @@ class HuyiGateway extends Gateway
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
* @param \Overtrue\EasySms\Support\Config $config
*
* @return mixed
* @return array
*
* @throws \Overtrue\EasySms\Exceptions\GatewayErrorException;
*/
......
......@@ -14,6 +14,11 @@ use Overtrue\EasySms\Exceptions\GatewayErrorException;
use Overtrue\EasySms\Support\Config;
use Overtrue\EasySms\Traits\HasHttpRequest;
/**
* Class JuheGateway.
*
* @see https://www.juhe.cn/docs/api/id/54
*/
class JuheGateway extends Gateway
{
use HasHttpRequest;
......
......@@ -32,7 +32,7 @@ class LuosimaoGateway extends Gateway
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
* @param \Overtrue\EasySms\Support\Config $config
*
* @return mixed
* @return array
*
* @throws \Overtrue\EasySms\Exceptions\GatewayErrorException;
*/
......
<?php
/*
* This file is part of the overtrue/easy-sms.
* (c) overtrue <i@overtrue.me>
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\EasySms\Gateways;
use Overtrue\EasySms\Contracts\MessageInterface;
use Overtrue\EasySms\Exceptions\GatewayErrorException;
use Overtrue\EasySms\Support\Config;
use Overtrue\EasySms\Traits\HasHttpRequest;
/**
* Class SendcloudGateway.
*
* @see http://sendcloud.sohu.com/doc/sms/
*/
class SendcloudGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'http://www.sendcloud.net/smsapi/%s';
/**
* Send a short message.
*
* @param int|string|array $to
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
* @param \Overtrue\EasySms\Support\Config $config
*
* @return array
*
* @throws \Overtrue\EasySms\Exceptions\GatewayErrorException
*/
public function send($to, MessageInterface $message, Config $config)
{
$params = [
'smsUser' => $config->get('sms_user'),
'templateId' => $message->getTemplate(),
'phone' => is_array($to) ? join(',', $to) : $to,
'vars' => $this->formatTemplateVars($message->getData()),
'timestamp' => time(),
];
$params['signature'] = $this->sign($params, $config->get('sms_key'));
$result = $this->post(sprintf(self::ENDPOINT_TEMPLATE, 'send'), $params);
if (!$result['result']) {
throw new GatewayErrorException($result['message'], $result['statusCode'], $result);
}
return $result;
}
/**
* @param array $vars
*
* @return string
*/
protected function formatTemplateVars(array $vars)
{
$formatted = [];
foreach ($vars as $key => $value) {
$formatted[sprintf('%%%s%%', trim($key, '%'))] = $value;
}
return json_encode($formatted);
}
/**
* @param array $params
* @param string $key
*
* @return string
*/
protected function sign($params, $key)
{
ksort($params);
return md5(sprintf('%s&%s&%s', $key, urldecode(http_build_query($params)), $key));
}
}
......@@ -31,7 +31,7 @@ class SubmailGateway extends Gateway
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
* @param \Overtrue\EasySms\Support\Config $config
*
* @return mixed
* @return array
*
* @throws \Overtrue\EasySms\Exceptions\GatewayErrorException;
*/
......
......@@ -36,7 +36,7 @@ class YuntongxunGateway extends Gateway
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
* @param \Overtrue\EasySms\Support\Config $config
*
* @return mixed
* @return array
*
* @throws \Overtrue\EasySms\Exceptions\GatewayErrorException;
*/
......@@ -44,7 +44,7 @@ class YuntongxunGateway extends Gateway
{
$datetime = date('YmdHis');
$endpoint = $this->buildEndpoint('SMS', 'TemplateSMS', $datetime);
$endpoint = $this->buildEndpoint('SMS', 'TemplateSMS', $datetime, $config);
$result = $this->request('post', $endpoint, [
'json' => [
......@@ -70,20 +70,21 @@ class YuntongxunGateway extends Gateway
/**
* Build endpoint url.
*
* @param string $type
* @param string $resource
* @param string $datetime
* @param string $type
* @param string $resource
* @param string $datetime
* @param \Overtrue\EasySms\Support\Config $config
*
* @return string
*/
protected function buildEndpoint($type, $resource, $datetime)
protected function buildEndpoint($type, $resource, $datetime, Config $config)
{
$serverIp = $this->config->get('debug') ? self::DEBUG_SERVER_IP : self::SERVER_IP;
$accountType = $this->config->get('is_sub_account') ? 'SubAccounts' : 'Accounts';
$sig = strtoupper(md5($this->config->get('account_sid').$this->config->get('account_token').$datetime));
$sig = strtoupper(md5($config->get('account_sid').$config->get('account_token').$datetime));
return sprintf(self::ENDPOINT_TEMPLATE, $serverIp, self::SERVER_PORT, self::SDK_VERSION, $accountType, $this->config->get('account_sid'), $type, $resource, $sig);
return sprintf(self::ENDPOINT_TEMPLATE, $serverIp, self::SERVER_PORT, self::SDK_VERSION, $accountType, $config->get('account_sid'), $type, $resource, $sig);
}
}
......@@ -39,9 +39,9 @@ class Messenger
/**
* Send a message.
*
* @param string|array $to
* @param string|array $to
* @param string|array|\Overtrue\EasySms\Contracts\MessageInterface $message
* @param array $gateways
* @param array $gateways
*
* @return array
*/
......@@ -117,6 +117,7 @@ class Messenger
$setting = [];
}
$formatted[$gateway] = $setting;
$globalSetting = $config->get("gateways.{$gateway}", []);
if (is_string($gateway) && !empty($globalSetting) && is_array($setting)) {
......
......@@ -27,7 +27,7 @@ class AlidayuGatewayTest extends TestCase
];
$gateway = \Mockery::mock(AlidayuGateway::class.'[post]', [$config])->shouldAllowMockingProtectedMethods();
$params = [
$expected = [
'method' => 'alibaba.aliqin.fc.sms.num.send',
'format' => 'json',
'v' => '2.0',
......@@ -39,15 +39,28 @@ class AlidayuGatewayTest extends TestCase
'rec_num' => strval(18888888888),
'sms_param' => json_encode(['code' => '123456', 'time' => '15']),
];
$gateway->shouldReceive('post')->with('https://eco.taobao.com/router/rest', \Mockery::subset($params))
->andReturn([
'success_response' => 'mock-result',
], [
'error_response' => ['sub_msg' => 'mock-msg', 'code' => 100],
])
->twice();
$gateway->shouldReceive('post')
->with('https://eco.taobao.com/router/rest', \Mockery::on(function ($params) use ($expected) {
if (empty($params['timestamp']) || empty($params['sign'])) {
return false;
}
unset($params['timestamp'], $params['sign']);
ksort($params);
ksort($expected);
return $params == $expected;
}))
->andReturn([
'success_response' => 'mock-result',
], [
'error_response' => ['sub_msg' => 'mock-msg', 'code' => 100],
])
->twice();
$message = new Message([
'template' => 'mock-template-code',
'data' => ['code' => '123456', 'time' => '15'],
]);
$config = new Config($config);
......
<?php
/*
* This file is part of the overtrue/easy-sms.
* (c) overtrue <i@overtrue.me>
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\EasySms\Tests\Gateways;
use Overtrue\EasySms\Exceptions\GatewayErrorException;
use Overtrue\EasySms\Gateways\SendcloudGateway;
use Overtrue\EasySms\Message;
use Overtrue\EasySms\Support\Config;
use Overtrue\EasySms\Tests\TestCase;
class SendcloudGatewayTest extends TestCase
{
public function testSend()
{
$config = [
'sms_user' => 'mock-user',
'sms_key' => 'mock-key',
];
$gateway = \Mockery::mock(SendcloudGateway::class.'[post]', [$config])->shouldAllowMockingProtectedMethods();
$expected = [
'smsUser' => 'mock-user',
'templateId' => 'mock-tpl-id',
'phone' => 18188888888,
'vars' => json_encode(['%code%' => 1234]),
];
$gateway->shouldReceive('post')
->with(sprintf(SendcloudGateway::ENDPOINT_TEMPLATE, 'send'), \Mockery::on(function ($params) use ($expected, $config) {
$expected['timestamp'] = $params['timestamp'];
ksort($expected);
$signString = [];
foreach ($expected as $key => $value) {
$signString[] = "{$key}={$value}";
}
$signString = join('&', $signString);
$expectedSignature = md5("{$config['sms_key']}&{$signString}&{$config['sms_key']}");
return $params['smsUser'] == $expected['smsUser']
&& $params['templateId'] == $expected['templateId']
&& $params['phone'] == $expected['phone']
&& $params['vars'] == $expected['vars']
&& $params['timestamp'] >= time()
&& $params['signature'] == $expectedSignature
;
}))
->andReturn([
'message' => '操作成功',
'result' => true,
'statusCode' => 200,
], [
'message' => '手机号不存在',
'result' => false,
'statusCode' => 400,
])->times(2);
$message = new Message([
'content' => 'This is a huyi test message.',
'template' => 'mock-tpl-id',
'data' => [
'code' => 1234,
],
]);
$config = new Config($config);
$this->assertSame([
'message' => '操作成功',
'result' => true,
'statusCode' => 200,
], $gateway->send(18188888888, $message, $config));
$this->expectException(GatewayErrorException::class);
$this->expectExceptionCode(400);
$this->expectExceptionMessage('手机号不存在');
$gateway->send(18188888888, $message, $config);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册