├── .gitignore
├── .scrutinizer.yml
├── .styleci.yml
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml
├── src
├── config
│ └── phpsms.php
└── phpsms
│ ├── PhpSmsException.php
│ ├── PhpSmsServiceProvider.php
│ ├── Sms.php
│ ├── Util.php
│ ├── agents
│ ├── Agent.php
│ ├── AlidayuAgent.php
│ ├── AliyunAgent.php
│ ├── JuHeAgent.php
│ ├── LogAgent.php
│ ├── LuosimaoAgent.php
│ ├── ParasiticAgent.php
│ ├── QcloudAgent.php
│ ├── SendCloudAgent.php
│ ├── SmsBaoAgent.php
│ ├── SubMailAgent.php
│ ├── UcpaasAgent.php
│ ├── YunPianAgent.php
│ └── YunTongXunAgent.php
│ ├── facades
│ └── Sms.php
│ ├── interfaces
│ ├── ContentSms.php
│ ├── ContentVoice.php
│ ├── FileVoice.php
│ ├── TemplateSms.php
│ ├── TemplateVoice.php
│ └── VoiceCode.php
│ └── lib
│ ├── CCPRestSmsSDK.php
│ └── Ucpaas.php
└── tests
├── AgentTest.php
├── ConfigTest.php
├── ProtectedTest.php
├── SerializeTest.php
└── SmsTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | composer.lock
3 | vendor/
4 | demo.php
5 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | filter:
2 | excluded_paths: [tests/*]
3 |
4 | checks:
5 | php:
6 | remove_extra_empty_lines: true
7 | remove_php_closing_tag: true
8 | remove_trailing_whitespace: true
9 | fix_use_statements:
10 | remove_unused: true
11 | preserve_multiple: false
12 | preserve_blanklines: true
13 | order_alphabetically: true
14 | fix_php_opening_tag: true
15 | fix_linefeed: true
16 | fix_line_ending: true
17 | fix_identation_4spaces: true
18 | fix_doc_comments: true
19 |
20 | tools:
21 | external_code_coverage:
22 | timeout: 1200
23 | runs: 3
24 | php_analyzer: true
25 | php_code_coverage: true
26 |
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | preset: recommended
2 |
3 | enabled:
4 | - concat_with_spaces
5 | - strict
6 |
7 | disabled:
8 | - concat_without_spaces
9 | - phpdoc_short_description
10 | - short_array_syntax
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - "5.4"
5 | - "5.5"
6 | - "5.6"
7 | - "7.0"
8 | - "hhvm"
9 |
10 | matrix:
11 | fast_finish: true
12 | allow_failures:
13 | - php: hhvm
14 |
15 | sudo: false
16 |
17 | before_script:
18 | - travis_retry composer self-update
19 | - travis_retry composer install --no-interaction --prefer-source
20 |
21 | script:
22 |
23 | - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
24 |
25 | after_script:
26 | - wget https://scrutinizer-ci.com/ocular.phar
27 | - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
28 |
29 | notifications:
30 | email: false
31 |
32 | # - if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi
33 | # - if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 lan tian peng
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PhpSms
2 | [](https://styleci.io/repos/44543599)
3 | [](https://travis-ci.org/toplan/phpsms)
4 | [](https://scrutinizer-ci.com/g/toplan/phpsms/?branch=master)
5 | [](https://packagist.org/packages/toplan/phpsms)
6 | [](https://packagist.org/packages/toplan/phpsms)
7 |
8 | 可能是目前最聪明、优雅的 php 短信发送库了。
9 |
10 | > phpsms的任务均衡调度功能由[toplan/task-balancer](https://github.com/toplan/task-balancer)提供。
11 |
12 | 特别感谢以下赞助者:
13 |
14 | [](http://www.smsbao.com/)
15 |
16 | # 特点
17 | - 支持内容短信,模版短信,语音验证码,内容语音,模版语音,语音文件。
18 | - 支持发送均衡调度,可按代理器权重值均衡选择服务商发送。
19 | - 支持一个或多个备用代理器(服务商)。
20 | - 支持代理器调度方案热更新,可随时更新/删除/新加代理器。
21 | - 允许推入队列,并自定义队列实现逻辑(与队列系统松散耦合)。
22 | - 灵活的发送前后钩子。
23 | - 内置国内主流服务商的代理器。
24 | - [自定义代理器](#自定义代理器)和[寄生代理器](#寄生代理器)。
25 |
26 | # 服务商
27 |
28 | | 服务商 | 模板短信 | 内容短信 | 语音验证码 | 最低消费 | 最低消费单价 | 资费标准
29 | | ----- | :-----: | :-----: | :------: | :-------: | :-----: | :-----:
30 | | [Luosimao](http://luosimao.com) | × | √ | √ | ¥850(1万条) | ¥0.085/条 | [资费标准](https://luosimao.com/service/sms#sms-price)
31 | | [云片网络](http://www.yunpian.com) | × | √ | √ | ¥55(1千条) | ¥0.055/条 | [资费标准](http://www.yunpian.com/price.html)
32 | | [容联·云通讯](http://www.yuntongxun.com) | √ | × | √ | 充值¥500 | ¥0.055/条 | [资费标准](http://www.yuntongxun.com/price/price_sms.html)
33 | | [SUBMAIL](http://submail.cn) | √ | × | √ | ¥100(1千条) | ¥0.100/条 | [资费标准](https://www.mysubmail.com/chs/store#/message)
34 | | [云之讯](http://www.ucpaas.com/) | √ | × | √ | -- | ¥0.050/条 | [资费标准](http://www.ucpaas.com/service/sms.html)
35 | | [聚合数据](https://www.juhe.cn/) | √ | × | √ | -- | ¥0.035/条 | [资费标准](https://www.juhe.cn/docs/api/id/54)
36 | | [阿里大鱼](https://www.alidayu.com/) | √ | × | √ | -- | ¥0.045/条 | [资费标准](https://www.alidayu.com/service/price)
37 | | [SendCloud](https://sendcloud.sohu.com/) | √ | × | √ | -- | ¥0.048/条 | [资费标准](https://sendcloud.sohu.com/price.html)
38 | | [短信宝](http://www.smsbao.com/) | × | √ | √ | ¥5(50条) | ¥0.040/条(100万条) | [资费标准](http://www.smsbao.com/fee/)
39 | | [腾讯云](https://www.qcloud.com/product/sms) | √ | √ | √ | -- | ¥0.045/条 | [资费标准](https://www.qcloud.com/product/sms#price)
40 | | [阿里云](https://www.aliyun.com/product/sms) | √ | × | × | -- | ¥0.045/条 | [资费标准](https://cn.aliyun.com/price/product#/mns/detail)
41 |
42 | # 安装
43 |
44 | ```php
45 | composer require toplan/phpsms:~1.8
46 | ```
47 |
48 | 开发中版本
49 | ```php
50 | composer require toplan/phpsms:dev-master
51 | ```
52 |
53 | # 快速上手
54 |
55 | ### 1. 配置
56 |
57 | - 配置代理器所需参数
58 |
59 | 为你需要用到的短信服务商(即代理器)配置必要的参数。可以在`config\phpsms.php`中键为`agents`的数组中配置,也可以手动在程序中设置,示例如下:
60 |
61 | ```php
62 | //example:
63 | Sms::config([
64 | 'Luosimao' => [
65 | 'apikey' => 'your api key',
66 | 'voiceApikey' => 'your voice api key',
67 | ],
68 | 'YunPian' => [
69 | 'apikey' => 'your api key',
70 | ],
71 | 'SmsBao' => [
72 | 'username' => 'your username',
73 | 'password' => 'your password'
74 | ]
75 | ]);
76 | ```
77 |
78 | - 配置代理器调度方案
79 |
80 | 可在`config\phpsms.php`中键为`scheme`的数组中配置。也可以手动在程序中设置,示例如下:
81 |
82 | ```php
83 | //example:
84 | Sms::scheme([
85 | //被使用概率为2/3
86 | 'Luosimao' => '20',
87 |
88 | //被使用概率为1/3,且为备用代理器
89 | 'YunPian' => '10 backup',
90 |
91 | //仅为备用代理器
92 | 'SmsBao' => '0 backup',
93 | ]);
94 | ```
95 | > **调度方案解析:**
96 | > 如果按照以上配置,那么系统首次会尝试使用`Luosimao`或`YunPian`发送短信,且它们被使用的概率分别为`2/3`和`1/3`。
97 | > 如果使用其中一个代理器发送失败,那么会启用备用代理器,按照配置可知备用代理器有`YunPian`和`SmsBao`,那么会依次调用直到发送成功或无备用代理器可用。
98 | > 值得注意的是,如果首次尝试的是`YunPian`,那么备用代理器将会只使用`SmsBao`,也就是会排除使用过的代理器。
99 |
100 | ### 2. Enjoy it!
101 |
102 | ```php
103 | require('path/to/vendor/autoload.php');
104 | use Toplan\PhpSms\Sms;
105 |
106 | // 接收人手机号
107 | $to = '1828****349';
108 | // 短信模版
109 | $templates = [
110 | 'YunTongXun' => 'your_temp_id',
111 | 'SubMail' => 'your_temp_id'
112 | ];
113 | // 模版数据
114 | $tempData = [
115 | 'code' => '87392',
116 | 'minutes' => '5'
117 | ];
118 | // 短信内容
119 | $content = '【签名】这是短信内容...';
120 |
121 | // 只希望使用模板方式发送短信,可以不设置content(如:云通讯、Submail、Ucpaas)
122 | Sms::make()->to($to)->template($templates)->data($tempData)->send();
123 |
124 | // 只希望使用内容方式发送,可以不设置模板id和模板data(如:短信宝、云片、luosimao)
125 | Sms::make()->to($to)->content($content)->send();
126 |
127 | // 同时确保能通过模板和内容方式发送,这样做的好处是可以兼顾到各种类型服务商
128 | Sms::make()->to($to)
129 | ->template($templates)
130 | ->data($tempData)
131 | ->content($content)
132 | ->send();
133 |
134 | // 语音验证码
135 | Sms::voice('02343')->to($to)->send();
136 |
137 | // 语音验证码兼容模版语音(如阿里大鱼的文本转语音)
138 | Sms::voice('02343')
139 | ->template('Alidayu', 'your_tts_code')
140 | ->data(['code' => '02343'])
141 | ->to($to)
142 | ->send();
143 | ```
144 |
145 | ### 3. 在laravel和lumen中使用
146 |
147 | * 服务提供器
148 |
149 | ```php
150 | //服务提供器
151 | 'providers' => [
152 | ...
153 | Toplan\PhpSms\PhpSmsServiceProvider::class,
154 | ]
155 |
156 | //别名
157 | 'aliases' => [
158 | ...
159 | 'PhpSms' => Toplan\PhpSms\Facades\Sms::class,
160 | ]
161 | ```
162 |
163 | * 生成配置文件
164 |
165 | ```php
166 | php artisan vendor:publish
167 | ```
168 | 生成的配置文件为config/phpsms.php,然后在该文件中按提示配置。
169 |
170 | * 使用
171 |
172 | 详见API,示例:
173 | ```php
174 | PhpSms::make()->to($to)->content($content)->send();
175 | ```
176 |
177 | # API
178 |
179 | ## API - 全局配置
180 |
181 | ### Sms::scheme([$name[, $scheme]])
182 |
183 | 设置/获取代理器的调度方案。
184 |
185 | > 调度配置支持热更新,即在应用系统的整个运行过程中都能随时修改。
186 |
187 | - 设置
188 |
189 | 手动设置代理器调度方案(优先级高于配置文件),如:
190 | ```php
191 | Sms::scheme([
192 | 'SmsBao' => '80 backup'
193 | 'YunPian' => '100 backup'
194 | ]);
195 | //或
196 | Sms::scheme('SmsBao', '80 backup');
197 | Sms::scheme('YunPian', '100 backup');
198 | ```
199 | - 获取
200 |
201 | 通过该方法还能获取所有或指定代理器的调度方案,如:
202 | ```php
203 | //获取所有的调度方案:
204 | $scheme = Sms::scheme();
205 |
206 | //获取指定代理器的调度方案:
207 | $scheme['SmsBao'] = Sms::scheme('SmsBao');
208 | ```
209 |
210 | > `scheme`静态方法的更多使用方法见[高级调度配置](#高级调度配置)
211 |
212 | ### Sms::config([$name[, $config][, $override]]);
213 |
214 | 设置/获取代理器的配置数据。
215 |
216 | > 参数配置支持热更新,即在应用系统的整个运行过程中都能随时修改。
217 |
218 | - 设置
219 |
220 | 手动设置代理器的配置数据(优先级高于配置文件),如:
221 | ```php
222 | Sms::config([
223 | 'SmsBao' => [
224 | 'username' => ...,
225 | 'password' => ...,
226 | ]
227 | ]);
228 | //或
229 | Sms::config('SmsBao', [
230 | 'username' => ...,
231 | 'password' => ...,
232 | ]);
233 | ```
234 | - 获取
235 |
236 | 通过该方法还能获取所有或指定代理器的配置参数,如:
237 | ```php
238 | //获取所有的配置:
239 | $config = Sms::config();
240 |
241 | //获取指定代理器的配置:
242 | $config['SmsBao'] = Sms::config('SmsBao');
243 | ```
244 |
245 | ### Sms::beforeSend($handler[, $override]);
246 |
247 | 发送前钩子,示例:
248 | ```php
249 | Sms::beforeSend(function($task, $index, $handlers, $prevReturn){
250 | //获取短信数据
251 | $smsData = $task->data;
252 | ...
253 | //如果返回false会终止发送任务
254 | return true;
255 | });
256 | ```
257 | > 更多细节请查看 [task-balancer](https://github.com/toplan/task-balancer#2-task-lifecycle) 的 `beforeRun` 钩子
258 |
259 | ### Sms::beforeAgentSend($handler[, $override]);
260 |
261 | 代理器发送前钩子,示例:
262 | ```php
263 | Sms::beforeAgentSend(function($task, $driver, $index, $handlers, $prevReturn){
264 | //短信数据:
265 | $smsData = $task->data;
266 | //当前使用的代理器名称:
267 | $agentName = $driver->name;
268 | //如果返回false会停止使用当前代理器
269 | return true;
270 | });
271 | ```
272 | > 更多细节请查看 [task-balancer](https://github.com/toplan/task-balancer#2-task-lifecycle) 的 `beforeDriverRun` 钩子
273 |
274 | ### Sms::afterAgentSend($handler[, $override]);
275 |
276 | 代理器发送后钩子,示例:
277 | ```php
278 | Sms::afterAgentSend(function($task, $agentResult, $index, $handlers, $prevReturn){
279 | //$result为代理器的发送结果数据
280 | $agentName = $agentResult['driver'];
281 | ...
282 | });
283 | ```
284 | > 更多细节请查看 [task-balancer](https://github.com/toplan/task-balancer#2-task-lifecycle) 的 `afterDriverRun` 钩子
285 |
286 | ### Sms::afterSend($handler[, $override]);
287 |
288 | 发送后钩子,示例:
289 | ```php
290 | Sms::afterSend(function($task, $taskResult, $index, $handlers, $prevReturn){
291 | //$result为发送后获得的结果数组
292 | $success = $taskResult['success'];
293 | ...
294 | });
295 | ```
296 | > 更多细节请查看 [task-balancer](https://github.com/toplan/task-balancer#2-task-lifecycle) 的 `afterRun` 钩子
297 |
298 | ### Sms::queue([$enable[, $handler]])
299 |
300 | 该方法可以设置是否启用队列以及定义如何推送到队列。
301 |
302 | `$handler`匿名函数可使用的参数:
303 | + `$sms` : Sms实例
304 | + `$data` : Sms实例中的短信数据,等同于`$sms->all()`
305 |
306 | 定义如何推送到队列:
307 | ```php
308 | //自动启用队列
309 | Sms::queue(function($sms, $data){
310 | //define how to push to queue.
311 | ...
312 | });
313 |
314 | //第一个参数为true,启用队列
315 | Sms::queue(true, function($sms, $data){
316 | //define how to push to queue.
317 | ...
318 | });
319 |
320 | //第一个参数为false,暂时关闭队列
321 | Sms::queue(false, function($sms, $data){
322 | //define how to push to queue.
323 | ...
324 | });
325 | ```
326 |
327 | 如果已经定义过如何推送到队列,还可以继续设置关闭/开启队列:
328 | ```php
329 | Sms::queue(true);//开启队列
330 | Sms::queue(false);//关闭队列
331 | ```
332 |
333 | 获取队列启用情况:
334 | ```php
335 | $enable = Sms::queue();
336 | //为true,表示当前启用了队列。
337 | //为false,表示当前关闭了队列。
338 | ```
339 |
340 | ## API - 发送相关
341 |
342 | ### Sms::make()
343 |
344 | 生成发送短信的sms实例,并返回实例。
345 | ```php
346 | $sms = Sms::make();
347 |
348 | //创建实例的同时设置短信内容:
349 | $sms = Sms::make('【签名】这是短信内容...');
350 |
351 | //创建实例的同时设置短信模版:
352 | $sms = Sms::make('YunTongXun', 'your_temp_id');
353 | //或
354 | $sms = Sms::make([
355 | 'YunTongXun' => 'your_temp_id',
356 | 'SubMail' => 'your_temp_id',
357 | ...
358 | ]);
359 | ```
360 |
361 | ### Sms::voice()
362 |
363 | 生成发送语音验证码的sms实例,并返回实例。
364 | ```php
365 | $sms = Sms::voice();
366 |
367 | //创建实例的同时设置验证码
368 | $sms = Sms::voice($code);
369 | ```
370 |
371 | > - 如果你使用`Luosimao`语音验证码,还需用在配置文件中`Luosimao`选项中设置`voiceApikey`。
372 | > - **语音文件ID**即是在服务商配置的语音文件的唯一编号,比如阿里大鱼[语音通知](http://open.taobao.com/doc2/apiDetail.htm?spm=a219a.7395905.0.0.oORhh9&apiId=25445)的`voice_code`。
373 | > - **模版语音**是另一种语音请求方式,它是通过模版ID和模版数据进行的语音请求,比如阿里大鱼的[文本转语音通知](http://open.taobao.com/doc2/apiDetail.htm?spm=a219a.7395905.0.0.f04PJ3&apiId=25444)。
374 |
375 | ### type($type)
376 |
377 | 设置实例类型,可选值有`Sms::TYPE_SMS`和`Sms::TYPE_VOICE`,返回实例对象。
378 |
379 | ### to($mobile)
380 |
381 | 设置发送给谁,并返回实例。
382 | ```php
383 | $sms->to('1828*******');
384 |
385 | //兼容腾讯云
386 | $sms->to([86, '1828*******'])
387 | ```
388 |
389 | ### template($agentName, $id)
390 |
391 | 指定代理器设置模版或批量设置,并返回实例。
392 | ```php
393 | //设置指定服务商的模板id
394 | $sms->template('YunTongXun', 'your_temp_id')
395 | ->template('SubMail', 'your_temp_id');
396 |
397 | //一次性设置多个服务商的模板id
398 | $sms->template([
399 | 'YunTongXun' => 'your_temp_id',
400 | 'SubMail' => 'your_temp_id',
401 | ...
402 | ]);
403 | ```
404 |
405 | ### data($key, $value)
406 |
407 | 设置模板短信的模板数据,并返回实例对象。
408 | ```php
409 | //单个数据
410 | $sms->data('code', $code);
411 |
412 | //同时设置多个数据
413 | $sms->data([
414 | 'code' => $code,
415 | 'minutes' => $minutes
416 | ]);
417 | ```
418 |
419 | > 通过`template`和`data`方法的组合除了可以实现模版短信的数据填充,还可以实现模版语音的数据填充。
420 |
421 | ### content($text)
422 |
423 | 设置内容短信的内容,并返回实例对象。
424 |
425 | > 一些内置的代理器(如SmsBao、YunPian、Luosimao)使用的是内容短信(即直接发送短信内容),那么就需要为它们设置短信内容。
426 |
427 | ```php
428 | $sms->content('【签名】这是短信内容...');
429 | ```
430 |
431 | ### code($code)
432 |
433 | 设置语音验证码,并返回实例对象。
434 |
435 | ### file($agentName, $id)
436 |
437 | 设置语音文件,并返回实例对象。
438 | ```php
439 | $sms->file('Agent1', 'agent1_file_id')
440 | ->file('Agent2', 'agent2_file_id');
441 |
442 | //或
443 | $sms->file([
444 | 'Agent1' => 'agent1_file_id',
445 | 'Agent2' => 'agent2_fiile_id',
446 | ]);
447 | ```
448 |
449 | ### params($agentName, $params)
450 |
451 | 直接设置参数到服务商提供的原生接口上,并返回实例对象。
452 | ```php
453 | $sms->params('Agent1', [
454 | 'callbackUrl' => ...,
455 | 'userData' => ...,
456 | ]);
457 |
458 | //或
459 | $sms->params([
460 | 'Agent1' => [
461 | 'callbackUrl' => ...,
462 | 'userData' => ...,
463 | ],
464 | 'Agent2' => [
465 | ...
466 | ],
467 | ]);
468 | ```
469 |
470 | ### all([$key])
471 |
472 | 获取Sms实例中的短信数据,不带参数时返回所有数据,其结构如下:
473 | ```php
474 | [
475 | 'type' => ...,
476 | 'to' => ...,
477 | 'templates' => [...],
478 | 'data' => [...], // template data
479 | 'content' => ...,
480 | 'code' => ..., // voice code
481 | 'files' => [...], // voice files
482 | 'params' => [...],
483 | ]
484 | ```
485 |
486 | ### agent($name)
487 |
488 | 临时设置发送时使用的代理器(不会影响备用代理器的正常使用),并返回实例,`$name`为代理器名称。
489 | ```php
490 | $sms->agent('SmsBao');
491 | ```
492 | > 通过该方法设置的代理器将获得绝对优先权,但只对当前短信实例有效。
493 |
494 | ### send()
495 |
496 | 请求发送短信/语音验证码。
497 | ```php
498 | //会遵循是否使用队列
499 | $result = $sms->send();
500 |
501 | //忽略是否使用队列
502 | $result = $sms->send(true);
503 | ```
504 |
505 | > `$result`数据结构请参看[task-balancer](https://github.com/toplan/task-balancer)
506 |
507 | # 自定义代理器
508 |
509 | - step 1
510 |
511 | 可将配置项(如果有用到)加入到`config/phpsms.php`中键为`agents`的数组里。
512 |
513 | ```php
514 | //example:
515 | 'Foo' => [
516 | 'key' => 'your api key',
517 | ...
518 | ]
519 | ```
520 |
521 | - step 2
522 |
523 | 新建一个继承`Toplan\PhpSms\Agent`抽象类的代理器类,建议代理器类名为`FooAgent`,建议命名空间为`Toplan\PhpSms`。
524 |
525 | > 如果类名不为`FooAgent`或者命名空间不为`Toplan\PhpSms`,在使用该代理器时则需要指定代理器类,详见[高级调度配置](#高级调度配置)。
526 |
527 | - step 3
528 |
529 | 实现相应的接口,可选的接口有:
530 |
531 | | 接口 | 说明 |
532 | | ------------- | :----------: |
533 | | ContentSms | 发送内容短信 |
534 | | TemplateSms | 发送模版短信 |
535 | | VoiceCode | 发送语音验证码 |
536 | | ContentVoice | 发送内容语音 |
537 | | TemplateVoice | 发送模版语音 |
538 | | FileVoice | 发送文件语音 |
539 |
540 | # 高级调度配置
541 |
542 | 代理器的高级调度配置可以通过配置文件(`config/phpsms.php`)中的`scheme`项目配置,也可以通过`scheme`静态方法设置。
543 | 值得注意的是,高级调度配置的值的数据结构是数组。
544 |
545 | ### 指定代理器类
546 |
547 | 如果你自定义了一个代理器,类名不为`FooAgent`或者命名空间不为`Toplan\PhpSms`,
548 | 那么你还可以在调度配置时指定你的代理器使用的类。
549 |
550 | * 配置方式:
551 |
552 | 通过配置值中`agentClass`键来指定类名。
553 |
554 | * 示例:
555 | ```php
556 | Sms::scheme('agentName', [
557 | '10 backup',
558 | 'agentClass' => 'My\Namespace\MyAgentClass'
559 | ]);
560 | ```
561 |
562 | ### 寄生代理器
563 |
564 | 如果你既不想使用内置的代理器,也不想创建文件写自定义代理器,那么寄生代理器或许是个好的选择,
565 | 无需定义代理器类,只需在调度配置时定义好发送短信和语音验证码的方式即可。
566 |
567 | * 配置方式:
568 |
569 | 可以配置的发送过程有:
570 |
571 | | 发送过程 | 参数列表 | 说明 |
572 | | ----------------- | :---------------------------: | :----------: |
573 | | sendContentSms | $agent, $to, $content | 发送内容短信 |
574 | | sendTemplateSms | $agent, $to, $tmpId, $tmpData | 发送模版短信 |
575 | | sendVoiceCode | $agent, $to, $code | 发送语音验证码 |
576 | | sendContentVoice | $agent, $to, $content | 发送内容语音 |
577 | | sendTemplateVoice | $agent, $to, $tmpId, $tmpData | 发送模版语音 |
578 | | sendFileVoice | $agent, $to, $fileId | 发送文件语音 |
579 |
580 | * 示例:
581 | ```php
582 | Sms::scheme([
583 | 'agentName' => [
584 | '20 backup',
585 | 'sendContentSms' => function($agent, $to, $content){
586 | // 获取配置(如果设置了的话):
587 | $key = $agent->key;
588 | ...
589 | // 可使用的内置方法:
590 | $agent->curlGet($url, $params); //get
591 | $agent->curlPost($url, $params); //post
592 | ...
593 | // 更新发送结果:
594 | $agent->result(Agent::SUCCESS, true);
595 | $agent->result(Agent::INFO, 'some info');
596 | $agent->result(Agent::CODE, 'your code');
597 | },
598 | 'sendVoiceCode' => function($agent, $to, $code){
599 | // 发送语音验证码,同上
600 | }
601 | ]
602 | ]);
603 | ```
604 |
605 | # Todo
606 |
607 | - [ ] 重新实现云通讯代理器,去掉`lib/CCPRestSmsSDK.php`
608 | - [ ] 重新实现云之讯代理器,去掉`lib/Ucpaas.php`
609 | - [ ] 升级云片接口到v2版本
610 |
611 | # License
612 |
613 | MIT
614 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "toplan/phpsms",
3 | "description": "Probably the most intelligent, elegant sms send library in php",
4 | "license": "MIT",
5 | "keywords": ["sms", "php", "phpsms", "sms library"],
6 | "authors": [
7 | {
8 | "name": "toplan",
9 | "email": "toplan710@gmail.com"
10 | }
11 | ],
12 | "require": {
13 | "php": ">=5.4.0",
14 | "toplan/task-balancer": "~0.5",
15 | "jeremeamia/SuperClosure": "~2.2"
16 | },
17 | "require-dev": {
18 | "phpunit/phpunit": "~4.0"
19 | },
20 | "autoload": {
21 | "psr-4": {
22 | "Toplan\\PhpSms\\": [
23 | "src/phpsms/",
24 | "src/phpsms/agents/",
25 | "src/phpsms/interfaces/"
26 | ],
27 | "Toplan\\PhpSms\\Facades\\": [
28 | "src/phpsms/facades/"
29 | ]
30 | },
31 | "classmap": [
32 | "src/phpsms/lib/"
33 | ]
34 | },
35 | "autoload-dev": {
36 | "classmap": [
37 | "tests/"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/AgentTest.php
16 |
17 |
18 | ./tests/ProtectedTest.php
19 |
20 |
21 | ./tests/ConfigTest.php
22 |
23 |
24 | ./tests/SmsTest.php
25 |
26 |
27 | ./tests/SerializeTest.php
28 |
29 |
30 |
31 |
32 | ./src/phpsms/agents/Agent.php
33 | ./src/phpsms/agents/LogAgent.php
34 | ./src/phpsms/Sms.php
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/config/phpsms.php:
--------------------------------------------------------------------------------
1 | {value}
9 | *
10 | * Examples:
11 | * 'Log' => '10 backup'
12 | * 'SmsBao' => '100'
13 | * 'CustomAgent' => [
14 | * '5 backup',
15 | * 'agentClass' => '/Namespace/ClassName'
16 | * ]
17 | *
18 | * Supported agents:
19 | * 'Log', 'YunPian', 'YunTongXun', 'SubMail', 'Luosimao',
20 | * 'Ucpaas', 'JuHe', 'Alidayu', 'SendCloud', 'SmsBao',
21 | * 'Qcloud', 'Aliyun'
22 | *
23 | */
24 | 'scheme' => [
25 | 'Log',
26 | ],
27 |
28 | /*
29 | * The configuration
30 | * -------------------------------------------------------------------
31 | *
32 | * Expected the name of agent to be a string.
33 | *
34 | */
35 | 'agents' => [
36 | /*
37 | * -----------------------------------
38 | * YunPian
39 | * 云片代理器
40 | * -----------------------------------
41 | * website:http://www.yunpian.com
42 | * support content sms.
43 | */
44 | 'YunPian' => [
45 | //用户唯一标识,必须
46 | 'apikey' => 'your_api_key',
47 | ],
48 |
49 | /*
50 | * -----------------------------------
51 | * YunTongXun
52 | * 云通讯代理器
53 | * -----------------------------------
54 | * website:http://www.yuntongxun.com/
55 | * support template sms.
56 | */
57 | 'YunTongXun' => [
58 | //主帐号
59 | 'accountSid' => 'your_account_sid',
60 | //主帐号令牌
61 | 'accountToken' => 'your_account_token',
62 | //应用Id
63 | 'appId' => 'your_app_id',
64 | //请求地址(不加协议前缀)
65 | 'serverIP' => 'app.cloopen.com',
66 | //请求端口
67 | 'serverPort' => '8883',
68 | //被叫号显
69 | 'displayNum' => null,
70 | //语音验证码播放次数
71 | 'playTimes' => 3,
72 | ],
73 |
74 | /*
75 | * -----------------------------------
76 | * SubMail
77 | * -----------------------------------
78 | * website:http://submail.cn/
79 | * support template sms.
80 | */
81 | 'SubMail' => [
82 | 'appid' => 'your_app_id',
83 | 'signature' => 'your app key',
84 | ],
85 |
86 | /*
87 | * -----------------------------------
88 | * luosimao
89 | * -----------------------------------
90 | * website:http://luosimao.com
91 | * support content sms.
92 | */
93 | 'Luosimao' => [
94 | 'apikey' => 'your_api_key',
95 | 'voiceApikey' => 'your_voice_api_key',
96 | ],
97 |
98 | /*
99 | * -----------------------------------
100 | * ucpaas
101 | * -----------------------------------
102 | * website:http://ucpaas.com
103 | * support template sms.
104 | */
105 | 'Ucpaas' => [
106 | //主帐号,对应开官网发者主账号下的 ACCOUNT SID
107 | 'accountSid' => 'your_account_sid',
108 | //主帐号令牌,对应官网开发者主账号下的 AUTH TOKEN
109 | 'accountToken' => 'your_account_token',
110 | //应用Id,在官网应用列表中点击应用,对应应用详情中的APP ID
111 | //在开发调试的时候,可以使用官网自动为您分配的测试Demo的APP ID
112 | 'appId' => 'your_app_id',
113 | ],
114 |
115 | /*
116 | * -----------------------------------
117 | * JuHe
118 | * 聚合数据
119 | * -----------------------------------
120 | * website:https://www.juhe.cn
121 | * support template sms.
122 | */
123 | 'JuHe' => [
124 | //应用App Key
125 | 'key' => 'your_key',
126 | //语音验证码播放次数
127 | 'times' => 3,
128 | ],
129 |
130 | /*
131 | * -----------------------------------
132 | * Alidayu
133 | * 阿里大鱼代理器
134 | * -----------------------------------
135 | * website:http://www.alidayu.com
136 | * support template sms.
137 | */
138 | 'Alidayu' => [
139 | //请求地址
140 | 'sendUrl' => 'http://gw.api.taobao.com/router/rest',
141 | //淘宝开放平台中,对应阿里大鱼短信应用的App Key
142 | 'appKey' => 'your_app_key',
143 | //淘宝开放平台中,对应阿里大鱼短信应用的App Secret
144 | 'secretKey' => 'your_secret_key',
145 | //短信签名,传入的短信签名必须是在阿里大鱼“管理中心-短信签名管理”中的可用签名
146 | 'smsFreeSignName' => 'your_sms_free_sign_name',
147 | //被叫号显(用于语音通知),传入的显示号码必须是阿里大鱼“管理中心-号码管理”中申请或购买的号码
148 | 'calledShowNum' => null,
149 | ],
150 |
151 | /*
152 | * -----------------------------------
153 | * SendCloud
154 | * -----------------------------------
155 | * website: http://sendcloud.sohu.com/sms/
156 | * support template sms.
157 | */
158 | 'SendCloud' => [
159 | 'smsUser' => 'your_SMS_USER',
160 | 'smsKey' => 'your_SMS_KEY',
161 | ],
162 |
163 | /*
164 | * -----------------------------------
165 | * SmsBao
166 | * -----------------------------------
167 | * website: http://www.smsbao.com
168 | * support content sms.
169 | */
170 | 'SmsBao' => [
171 | //注册账号
172 | 'username' => 'your_username',
173 | //账号密码(明文)
174 | 'password' => 'your_password',
175 | ],
176 |
177 | /*
178 | * -----------------------------------
179 | * Qcloud
180 | * 腾讯云
181 | * -----------------------------------
182 | * website:http://www.qcloud.com
183 | * support template sms.
184 | */
185 | 'Qcloud' => [
186 | 'appId' => 'your_app_id',
187 | 'appKey' => 'your_app_key',
188 | ],
189 |
190 | /*
191 | * -----------------------------------
192 | * Aliyun
193 | * 阿里云
194 | * -----------------------------------
195 | * website:https://www.aliyun.com/product/sms
196 | * support template sms.
197 | */
198 | 'Aliyun' => [
199 | 'accessKeyId' => 'your_access_key_id',
200 | 'accessKeySecret' => 'your_access_key_secret',
201 | 'signName' => 'your_sms_sign_name',
202 | 'regionId' => 'cn-shenzhen',
203 | ],
204 | ],
205 | ];
206 |
--------------------------------------------------------------------------------
/src/phpsms/PhpSmsException.php:
--------------------------------------------------------------------------------
1 | publishes([
28 | __DIR__ . '/../config/phpsms.php' => $publishPath,
29 | ], 'config');
30 | }
31 |
32 | /**
33 | * Register the service provider.
34 | */
35 | public function register()
36 | {
37 | if ($this->app instanceof LumenApplication) {
38 | $this->app->configure('phpsms');
39 | }
40 | $this->mergeConfigFrom(__DIR__ . '/../config/phpsms.php', 'phpsms');
41 |
42 | $this->app->singleton('Toplan\\PhpSms\\Sms', function () {
43 | Sms::scheme(config('phpsms.scheme', []));
44 | Sms::config(config('phpsms.agents', []));
45 |
46 | return new Sms(false);
47 | });
48 | }
49 |
50 | /**
51 | * Get the services provided by the provider.
52 | *
53 | * @return array
54 | */
55 | public function provides()
56 | {
57 | return ['Toplan\\PhpSms\\Sms'];
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/phpsms/Sms.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class Sms
14 | {
15 | const TYPE_SMS = 1;
16 | const TYPE_VOICE = 2;
17 |
18 | /**
19 | * Task instance.
20 | *
21 | * @var Task
22 | */
23 | protected static $task = null;
24 |
25 | /**
26 | * Agent instances.
27 | *
28 | * @var Agent[]
29 | */
30 | protected static $agents = [];
31 |
32 | /**
33 | * Dispatch scheme of agents.
34 | *
35 | * @var array
36 | */
37 | protected static $scheme = [];
38 |
39 | /**
40 | * Configuration information of agents.
41 | *
42 | * @var array
43 | */
44 | protected static $agentsConfig = [];
45 |
46 | /**
47 | * Whether to use the queue system.
48 | *
49 | * @var bool
50 | */
51 | protected static $enableQueue = false;
52 |
53 | /**
54 | * How to use the queue system.
55 | *
56 | * @var \Closure
57 | */
58 | protected static $howToUseQueue = null;
59 |
60 | /**
61 | * Available hooks.
62 | *
63 | * @var string[]
64 | */
65 | protected static $availableHooks = [
66 | 'beforeRun',
67 | 'beforeDriverRun',
68 | 'afterDriverRun',
69 | 'afterRun',
70 | ];
71 |
72 | /**
73 | * Data container.
74 | *
75 | * @var array
76 | */
77 | protected $smsData = [
78 | 'type' => self::TYPE_SMS,
79 | 'to' => null,
80 | 'templates' => [],
81 | 'data' => [],
82 | 'content' => null,
83 | 'code' => null,
84 | 'files' => [],
85 | 'params' => [],
86 | ];
87 |
88 | /**
89 | * The name of first agent.
90 | *
91 | * @var string
92 | */
93 | protected $firstAgent = null;
94 |
95 | /**
96 | * Whether pushed to the queue system.
97 | *
98 | * @var bool
99 | */
100 | protected $pushedToQueue = false;
101 |
102 | /**
103 | * State container.
104 | *
105 | * @var array
106 | */
107 | protected $state = [];
108 |
109 | /**
110 | * Constructor
111 | *
112 | * @param bool $autoBoot
113 | */
114 | public function __construct($autoBoot = true)
115 | {
116 | if ($autoBoot) {
117 | self::bootTask();
118 | }
119 | }
120 |
121 | /**
122 | * Bootstrap the task.
123 | */
124 | public static function bootTask()
125 | {
126 | if (!self::isTaskBooted()) {
127 | self::configure();
128 | foreach (self::scheme() as $name => $scheme) {
129 | self::registerDriver($name, $scheme);
130 | }
131 | }
132 | }
133 |
134 | /**
135 | * Is task has been booted.
136 | *
137 | * @return bool
138 | */
139 | protected static function isTaskBooted()
140 | {
141 | return !empty(self::getTask()->drivers);
142 | }
143 |
144 | /**
145 | * Get the task instance.
146 | *
147 | * @return Task
148 | */
149 | public static function getTask()
150 | {
151 | if (empty(self::$task)) {
152 | self::$task = new Task();
153 | }
154 |
155 | return self::$task;
156 | }
157 |
158 | /**
159 | * Configure.
160 | *
161 | * @throws PhpSmsException
162 | */
163 | protected static function configure()
164 | {
165 | $config = [];
166 | if (!count(self::scheme())) {
167 | self::initScheme($config);
168 | }
169 | $diff = array_diff_key(self::scheme(), self::$agentsConfig);
170 | self::initAgentsConfig(array_keys($diff), $config);
171 | if (!count(self::scheme())) {
172 | throw new PhpSmsException('Expected at least one agent in scheme.');
173 | }
174 | }
175 |
176 | /**
177 | * Initialize the dispatch scheme.
178 | *
179 | * @param array $config
180 | */
181 | protected static function initScheme(array &$config)
182 | {
183 | $config = empty($config) ? include __DIR__ . '/../config/phpsms.php' : $config;
184 | $scheme = isset($config['scheme']) ? $config['scheme'] : [];
185 | self::scheme($scheme);
186 | }
187 |
188 | /**
189 | * Initialize the configuration information.
190 | *
191 | * @param array $agents
192 | * @param array $config
193 | */
194 | protected static function initAgentsConfig(array $agents, array &$config)
195 | {
196 | if (empty($agents)) {
197 | return;
198 | }
199 | $config = empty($config) ? include __DIR__ . '/../config/phpsms.php' : $config;
200 | $agentsConfig = isset($config['agents']) ? $config['agents'] : [];
201 | foreach ($agents as $name) {
202 | $agentConfig = isset($agentsConfig[$name]) ? $agentsConfig[$name] : [];
203 | self::config($name, $agentConfig);
204 | }
205 | }
206 |
207 | /**
208 | * register driver.
209 | *
210 | * @param string $name
211 | * @param string|array $scheme
212 | */
213 | protected static function registerDriver($name, $scheme)
214 | {
215 | // parse higher-order scheme
216 | $settings = [];
217 | if (is_array($scheme)) {
218 | $settings = self::parseScheme($scheme);
219 | $scheme = $settings['scheme'];
220 | }
221 | // register
222 | self::getTask()->driver("$name $scheme")->work(function (Driver $driver) use ($settings) {
223 | $agent = self::getAgent($driver->name, $settings);
224 | extract($driver->getTaskData());
225 | $template = isset($templates[$driver->name]) ? $templates[$driver->name] : null;
226 | $file = isset($files[$driver->name]) ? $files[$driver->name] : null;
227 | $params = isset($params[$driver->name]) ? $params[$driver->name] : [];
228 | if ($type === self::TYPE_VOICE) {
229 | $agent->sendVoice($to, $content, $template, $data, $code, $file, $params);
230 | } elseif ($type === self::TYPE_SMS) {
231 | $agent->sendSms($to, $content, $template, $data, $params);
232 | }
233 | $result = $agent->result();
234 | if ($result['success']) {
235 | $driver->success();
236 | }
237 | unset($result['success']);
238 |
239 | return $result;
240 | });
241 | }
242 |
243 | /**
244 | * Parse the higher-order dispatch scheme.
245 | *
246 | * @param array $options
247 | *
248 | * @return array
249 | */
250 | protected static function parseScheme(array $options)
251 | {
252 | $weight = Util::pullFromArray($options, 'weight');
253 | $backup = Util::pullFromArray($options, 'backup') ? 'backup' : '';
254 | $props = array_filter(array_values($options), function ($prop) {
255 | return is_numeric($prop) || is_string($prop);
256 | });
257 |
258 | $options['scheme'] = implode(' ', $props) . " $weight $backup";
259 |
260 | return $options;
261 | }
262 |
263 | /**
264 | * Get the agent instance by name.
265 | *
266 | * @param string $name
267 | * @param array $options
268 | *
269 | * @throws PhpSmsException
270 | *
271 | * @return Agent
272 | */
273 | public static function getAgent($name, array $options = [])
274 | {
275 | if (!self::hasAgent($name)) {
276 | $scheme = self::scheme($name);
277 | $config = self::config($name);
278 | if (is_array($scheme) && empty($options)) {
279 | $options = self::parseScheme($scheme);
280 | }
281 | if (isset($options['scheme'])) {
282 | unset($options['scheme']);
283 | }
284 | $className = "Toplan\\PhpSms\\{$name}Agent";
285 | if (isset($options['agentClass'])) {
286 | $className = $options['agentClass'];
287 | unset($options['agentClass']);
288 | }
289 | if (!empty($options)) {
290 | self::$agents[$name] = new ParasiticAgent($config, $options);
291 | } elseif (class_exists($className)) {
292 | self::$agents[$name] = new $className($config);
293 | } else {
294 | throw new PhpSmsException("Not support agent `$name`.");
295 | }
296 | }
297 |
298 | return self::$agents[$name];
299 | }
300 |
301 | /**
302 | * Whether has the specified agent.
303 | *
304 | * @param string $name
305 | *
306 | * @return bool
307 | */
308 | public static function hasAgent($name)
309 | {
310 | return isset(self::$agents[$name]);
311 | }
312 |
313 | /**
314 | * Set or get the dispatch scheme.
315 | *
316 | * @param string|array|null $name
317 | * @param string|array|bool|null $scheme
318 | * @param bool $override
319 | *
320 | * @return mixed
321 | */
322 | public static function scheme($name = null, $scheme = null, $override = false)
323 | {
324 | if (is_array($name) && is_bool($scheme)) {
325 | $override = $scheme;
326 | }
327 |
328 | return Util::operateArray(self::$scheme, $name, $scheme, null, function ($key, $value) {
329 | if (is_string($key)) {
330 | self::modifyScheme($key, is_array($value) ? $value : "$value");
331 | } elseif (is_int($key)) {
332 | self::modifyScheme($value, '');
333 | }
334 | }, $override, function (array $origin) {
335 | if (self::isTaskBooted()) {
336 | foreach (array_keys($origin) as $name) {
337 | self::getTask()->removeDriver($name);
338 | }
339 | }
340 | });
341 | }
342 |
343 | /**
344 | * Modify the dispatch scheme of agent.
345 | *
346 | * @param string $name
347 | * @param string|array $scheme
348 | *
349 | * @throws PhpSmsException
350 | */
351 | protected static function modifyScheme($name, $scheme)
352 | {
353 | self::validateAgentName($name);
354 | self::$scheme[$name] = $scheme;
355 | if (self::isTaskBooted()) {
356 | $driver = self::getTask()->getDriver($name);
357 | if ($driver) {
358 | if (is_array($scheme)) {
359 | $higherOrderScheme = self::parseScheme($scheme);
360 | $scheme = $higherOrderScheme['scheme'];
361 | }
362 | $driver->reset($scheme);
363 | } else {
364 | self::registerDriver($name, $scheme);
365 | }
366 | }
367 | }
368 |
369 | /**
370 | * Set or get the configuration information.
371 | *
372 | * @param string|array|null $name
373 | * @param array|bool|null $config
374 | * @param bool $override
375 | *
376 | * @throws PhpSmsException
377 | *
378 | * @return array
379 | */
380 | public static function config($name = null, $config = null, $override = false)
381 | {
382 | $overrideAll = (is_array($name) && is_bool($config)) ? $config : false;
383 |
384 | return Util::operateArray(self::$agentsConfig, $name, $config, [], function ($name, array $config) use ($override) {
385 | self::modifyConfig($name, $config, $override);
386 | }, $overrideAll, function (array $origin) {
387 | foreach (array_keys($origin) as $name) {
388 | if (self::hasAgent("$name")) {
389 | self::getAgent("$name")->config([], true);
390 | }
391 | }
392 | });
393 | }
394 |
395 | /**
396 | * Modify the configuration information.
397 | *
398 | * @param string $name
399 | * @param array $config
400 | * @param bool $override
401 | *
402 | * @throws PhpSmsException
403 | */
404 | protected static function modifyConfig($name, array $config, $override = false)
405 | {
406 | self::validateAgentName($name);
407 | if (!isset(self::$agentsConfig[$name])) {
408 | self::$agentsConfig[$name] = [];
409 | }
410 | $target = &self::$agentsConfig[$name];
411 | if ($override) {
412 | $target = $config;
413 | } else {
414 | $target = array_merge($target, $config);
415 | }
416 | if (self::hasAgent($name)) {
417 | self::getAgent($name)->config($target);
418 | }
419 | }
420 |
421 | /**
422 | * Validate the name of agent.
423 | *
424 | * @param string $name
425 | *
426 | * @throws PhpSmsException
427 | */
428 | protected static function validateAgentName($name)
429 | {
430 | if (empty($name) || !is_string($name) || preg_match('/^[0-9]+$/', $name)) {
431 | throw new PhpSmsException('Expected the name of agent to be a string which except the digital string.');
432 | }
433 | }
434 |
435 | /**
436 | * Tear down scheme.
437 | */
438 | public static function cleanScheme()
439 | {
440 | self::scheme([], true);
441 | }
442 |
443 | /**
444 | * Tear down config information.
445 | */
446 | public static function cleanConfig()
447 | {
448 | self::config([], true);
449 | }
450 |
451 | /**
452 | * Create a instance for send sms.
453 | *
454 | * @param mixed $agentName
455 | * @param mixed $tempId
456 | *
457 | * @return Sms
458 | */
459 | public static function make($agentName = null, $tempId = null)
460 | {
461 | $sms = new self();
462 | $sms->type(self::TYPE_SMS);
463 | if (is_array($agentName)) {
464 | $sms->template($agentName);
465 | } elseif ($agentName && is_string($agentName)) {
466 | if ($tempId === null) {
467 | $sms->content($agentName);
468 | } elseif (is_string($tempId) || is_int($tempId)) {
469 | $sms->template($agentName, "$tempId");
470 | }
471 | }
472 |
473 | return $sms;
474 | }
475 |
476 | /**
477 | * Create a instance for send voice.
478 | *
479 | * @param int|string|null $code
480 | *
481 | * @return Sms
482 | */
483 | public static function voice($code = null)
484 | {
485 | $sms = new self();
486 | $sms->type(self::TYPE_VOICE);
487 | $sms->code($code);
488 |
489 | return $sms;
490 | }
491 |
492 | /**
493 | * Set whether to use the queue system,
494 | * and define how to use it.
495 | *
496 | * @param bool|\Closure|null $enable
497 | * @param \Closure|null $handler
498 | *
499 | * @return bool
500 | */
501 | public static function queue($enable = null, $handler = null)
502 | {
503 | if ($enable === null && $handler === null) {
504 | return self::$enableQueue;
505 | }
506 | if (is_callable($enable)) {
507 | $handler = $enable;
508 | $enable = true;
509 | }
510 | self::$enableQueue = (bool) $enable;
511 | if (is_callable($handler)) {
512 | self::$howToUseQueue = $handler;
513 | }
514 |
515 | return self::$enableQueue;
516 | }
517 |
518 | /**
519 | * Set the type of Sms instance.
520 | *
521 | * @param $type
522 | *
523 | * @throws PhpSmsException
524 | *
525 | * @return $this
526 | */
527 | public function type($type)
528 | {
529 | if ($type !== self::TYPE_SMS && $type !== self::TYPE_VOICE) {
530 | throw new PhpSmsException('Expected the parameter equals to `Sms::TYPE_SMS` or `Sms::TYPE_VOICE`.');
531 | }
532 | $this->smsData['type'] = $type;
533 |
534 | return $this;
535 | }
536 |
537 | /**
538 | * Set the recipient`s mobile number.
539 | *
540 | * @param string|array $mobile
541 | *
542 | * @return $this
543 | */
544 | public function to($mobile)
545 | {
546 | if (is_string($mobile)) {
547 | $mobile = trim($mobile);
548 | }
549 | $this->smsData['to'] = $mobile;
550 |
551 | return $this;
552 | }
553 |
554 | /**
555 | * Set the sms content.
556 | *
557 | * @param string $content
558 | *
559 | * @return $this
560 | */
561 | public function content($content)
562 | {
563 | $this->smsData['content'] = trim((string) $content);
564 |
565 | return $this;
566 | }
567 |
568 | /**
569 | * Set the template ids.
570 | *
571 | * @param mixed $name
572 | * @param mixed $tempId
573 | *
574 | * @return $this
575 | */
576 | public function template($name, $tempId = null)
577 | {
578 | Util::operateArray($this->smsData['templates'], $name, $tempId);
579 |
580 | return $this;
581 | }
582 |
583 | /**
584 | * Set the template data.
585 | *
586 | * @param mixed $key
587 | * @param mixed $value
588 | *
589 | * @return $this
590 | */
591 | public function data($key, $value = null)
592 | {
593 | Util::operateArray($this->smsData['data'], $key, $value);
594 |
595 | return $this;
596 | }
597 |
598 | /**
599 | * Set the voice code.
600 | *
601 | * @param string|int $code
602 | *
603 | * @return $this
604 | */
605 | public function code($code)
606 | {
607 | $this->smsData['code'] = $code;
608 |
609 | return $this;
610 | }
611 |
612 | /**
613 | * Set voice file.
614 | *
615 | * @param string|array $name
616 | * @param string|int $id
617 | *
618 | * @return $this
619 | */
620 | public function file($name, $id = null)
621 | {
622 | Util::operateArray($this->smsData['files'], $name, $id);
623 |
624 | return $this;
625 | }
626 |
627 | /**
628 | * Set params of agent.
629 | *
630 | * @param string|array $name
631 | * @param array|bool|null $params
632 | * @param bool $override
633 | *
634 | * @return $this
635 | */
636 | public function params($name, $params = null, $override = false)
637 | {
638 | $overrideAll = (is_array($name) && is_bool($params)) ? $params : false;
639 | Util::operateArray($this->smsData['params'], $name, $params, [], function ($name, array $params) use ($override) {
640 | if (!isset($this->smsData['params'][$name])) {
641 | $this->smsData['params'][$name] = [];
642 | }
643 | $target = &$this->smsData['params'][$name];
644 | if ($override) {
645 | $target = $params;
646 | } else {
647 | $target = array_merge($target, $params);
648 | }
649 | }, $overrideAll);
650 |
651 | return $this;
652 | }
653 |
654 | /**
655 | * Set the first agent.
656 | *
657 | * @param string $name
658 | *
659 | * @throws PhpSmsException
660 | *
661 | * @return $this
662 | */
663 | public function agent($name)
664 | {
665 | if (!is_string($name) || empty($name)) {
666 | throw new PhpSmsException('Expected the parameter to be non-empty string.');
667 | }
668 | $this->firstAgent = $name;
669 |
670 | return $this;
671 | }
672 |
673 | /**
674 | * Start send.
675 | *
676 | * If call with a `true` parameter, this system will immediately start request to send sms whatever whether to use the queue.
677 | * if the current instance has pushed to the queue, you can recall this method in queue system without any parameter,
678 | * so this mechanism in order to make you convenient to use this method in queue system.
679 | *
680 | * @param bool $immediately
681 | *
682 | * @return mixed
683 | */
684 | public function send($immediately = false)
685 | {
686 | if (!self::$enableQueue || $this->pushedToQueue) {
687 | $immediately = true;
688 | }
689 | if ($immediately) {
690 | return self::$task->data($this->all())->run($this->firstAgent);
691 | }
692 |
693 | return $this->push();
694 | }
695 |
696 | /**
697 | * Push to the queue system.
698 | *
699 | * @throws \Exception | PhpSmsException
700 | *
701 | * @return mixed
702 | */
703 | public function push()
704 | {
705 | if (!is_callable(self::$howToUseQueue)) {
706 | throw new PhpSmsException('Expected define how to use the queue system by methods `queue`.');
707 | }
708 | try {
709 | $this->pushedToQueue = true;
710 |
711 | return call_user_func_array(self::$howToUseQueue, [$this, $this->all()]);
712 | } catch (\Exception $e) {
713 | $this->pushedToQueue = false;
714 | throw $e;
715 | }
716 | }
717 |
718 | /**
719 | * Get all of the data.
720 | *
721 | * @param null|string $key
722 | *
723 | * @return mixed
724 | */
725 | public function all($key = null)
726 | {
727 | if ($key !== null) {
728 | return isset($this->smsData[$key]) ? $this->smsData[$key] : null;
729 | }
730 |
731 | return $this->smsData;
732 | }
733 |
734 | /**
735 | * Define the static hook methods by overload static method.
736 | *
737 | * @param string $name
738 | * @param array $args
739 | *
740 | * @throws PhpSmsException
741 | */
742 | public static function __callStatic($name, $args)
743 | {
744 | $name = $name === 'beforeSend' ? 'beforeRun' : $name;
745 | $name = $name === 'afterSend' ? 'afterRun' : $name;
746 | $name = $name === 'beforeAgentSend' ? 'beforeDriverRun' : $name;
747 | $name = $name === 'afterAgentSend' ? 'afterDriverRun' : $name;
748 | if (!in_array($name, self::$availableHooks)) {
749 | throw new PhpSmsException("Not found methods `$name`.");
750 | }
751 | $handler = $args[0];
752 | $override = isset($args[1]) ? (bool) $args[1] : false;
753 | self::getTask()->hook($name, $handler, $override);
754 | }
755 |
756 | /**
757 | * Define the hook methods by overload method.
758 | *
759 | * @param string $name
760 | * @param array $args
761 | *
762 | * @throws PhpSmsException
763 | * @throws \Exception
764 | */
765 | public function __call($name, $args)
766 | {
767 | try {
768 | $this->__callStatic($name, $args);
769 | } catch (\Exception $e) {
770 | throw $e;
771 | }
772 | }
773 |
774 | /**
775 | * Serialize magic method.
776 | *
777 | * @return array
778 | */
779 | public function __sleep()
780 | {
781 | try {
782 | $this->state['scheme'] = self::toggleSerializeScheme(self::scheme());
783 | $this->state['agentsConfig'] = self::config();
784 | $this->state['handlers'] = self::serializeHandlers();
785 | } catch (\Exception $e) {
786 | //swallow exception
787 | }
788 |
789 | return ['smsData', 'firstAgent', 'pushedToQueue', 'state'];
790 | }
791 |
792 | /**
793 | * Deserialize magic method.
794 | */
795 | public function __wakeup()
796 | {
797 | if (empty($this->state)) {
798 | return;
799 | }
800 | self::$scheme = self::toggleSerializeScheme($this->state['scheme']);
801 | self::$agentsConfig = $this->state['agentsConfig'];
802 | self::reinstallHandlers($this->state['handlers']);
803 | }
804 |
805 | /**
806 | * Serialize or deserialize the scheme.
807 | *
808 | * @param array $scheme
809 | *
810 | * @return array
811 | */
812 | protected static function toggleSerializeScheme(array $scheme)
813 | {
814 | foreach ($scheme as $name => &$options) {
815 | if (is_array($options)) {
816 | foreach (ParasiticAgent::methods() as $method) {
817 | self::toggleSerializeClosure($options, $method);
818 | }
819 | }
820 | }
821 |
822 | return $scheme;
823 | }
824 |
825 | /**
826 | * Serialize the hooks' handlers.
827 | *
828 | * @return array
829 | */
830 | protected static function serializeHandlers()
831 | {
832 | $hooks = (array) self::getTask()->handlers;
833 | foreach ($hooks as &$handlers) {
834 | foreach (array_keys($handlers) as $key) {
835 | self::toggleSerializeClosure($handlers, $key);
836 | }
837 | }
838 |
839 | return $hooks;
840 | }
841 |
842 | /**
843 | * Reinstall hooks' handlers.
844 | *
845 | * @param array $handlers
846 | */
847 | protected static function reinstallHandlers(array $handlers)
848 | {
849 | $serializer = Util::getClosureSerializer();
850 | foreach ($handlers as $hookName => $serializedHandlers) {
851 | foreach ($serializedHandlers as $index => $handler) {
852 | if (is_string($handler)) {
853 | $handler = $serializer->unserialize($handler);
854 | }
855 | self::$hookName($handler, $index === 0);
856 | }
857 | }
858 | }
859 |
860 | /**
861 | * Serialize or deserialize the specified closure and then replace the original value.
862 | *
863 | * @param array $options
864 | * @param int|string $key
865 | */
866 | protected static function toggleSerializeClosure(array &$options, $key)
867 | {
868 | if (!isset($options[$key])) {
869 | return;
870 | }
871 | $serializer = Util::getClosureSerializer();
872 | if (is_callable($options[$key])) {
873 | $options[$key] = (string) $serializer->serialize($options[$key]);
874 | } elseif (is_string($options[$key])) {
875 | $options[$key] = $serializer->unserialize($options[$key]);
876 | }
877 | }
878 | }
879 |
--------------------------------------------------------------------------------
/src/phpsms/Util.php:
--------------------------------------------------------------------------------
1 | $v) {
25 | self::operateArray($array, $k, $v, $default, $setter, false, null, true);
26 | }
27 | } elseif (is_callable($setter)) {
28 | call_user_func_array($setter, [$key, $value]);
29 | } else {
30 | $array[$key] = $value;
31 | }
32 |
33 | return $array;
34 | }
35 |
36 | public static function pullFromArray(array &$options, $key)
37 | {
38 | $value = null;
39 | if (!isset($options[$key])) {
40 | return $value;
41 | }
42 | $value = $options[$key];
43 | unset($options[$key]);
44 |
45 | return $value;
46 | }
47 |
48 | public static function getClosureSerializer()
49 | {
50 | if (empty(self::$closureSerializer)) {
51 | self::$closureSerializer = new Serializer();
52 | }
53 |
54 | return self::$closureSerializer;
55 | }
56 |
57 | public static function formatMobiles($target)
58 | {
59 | if (!is_array($target)) {
60 | return [$target];
61 | }
62 | $list = [];
63 | $nation = $number = null;
64 | $count = count($target);
65 | if ($count === 2) {
66 | $firstItem = $target[0];
67 | if (is_int($firstItem) && $firstItem > 0 && $firstItem <= 9999) {
68 | $nation = $firstItem;
69 | $number = $target[1];
70 | }
71 | if (is_string($firstItem) && strlen($firstItem = trim($firstItem)) <= 4) {
72 | $nation = $firstItem;
73 | $number = $target[1];
74 | }
75 | }
76 | if (!is_null($nation)) {
77 | return [compact('nation', 'number')];
78 | }
79 | foreach ($target as $childTarget) {
80 | $childList = self::formatMobiles($childTarget);
81 | foreach ($childList as $childListItem) {
82 | array_push($list, $childListItem);
83 | }
84 | }
85 |
86 | return $list;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/phpsms/agents/Agent.php:
--------------------------------------------------------------------------------
1 | reset();
40 | $this->config($config);
41 | }
42 |
43 | /**
44 | * Reset states.
45 | */
46 | public function reset()
47 | {
48 | $this->result = [
49 | self::SUCCESS => false,
50 | self::INFO => null,
51 | self::CODE => 0,
52 | ];
53 | }
54 |
55 | /**
56 | * Get or set the configuration information.
57 | *
58 | * @param string|array $key
59 | * @param mixed $value
60 | * @param bool $override
61 | *
62 | * @return mixed
63 | */
64 | public function config($key = null, $value = null, $override = false)
65 | {
66 | if (is_array($key) && is_bool($value)) {
67 | $override = $value;
68 | }
69 |
70 | return Util::operateArray($this->config, $key, $value, null, null, $override);
71 | }
72 |
73 | /**
74 | * Get or set the custom params.
75 | *
76 | * @param string|array $key
77 | * @param mixed $value
78 | * @param bool $override
79 | *
80 | * @return mixed
81 | */
82 | public function params($key = null, $value = null, $override = false)
83 | {
84 | if (is_array($key) && is_bool($value)) {
85 | $override = $value;
86 | }
87 |
88 | return Util::operateArray($this->params, $key, $value, null, null, $override);
89 | }
90 |
91 | /**
92 | * SMS send process.
93 | *
94 | * @param $to
95 | * @param $content
96 | * @param $tempId
97 | * @param array $data
98 | * @param array $params
99 | */
100 | public function sendSms($to, $content = null, $tempId = null, array $data = [], array $params = [])
101 | {
102 | $this->reset();
103 | $this->params($params, true);
104 | $to = $this->formatMobile(Util::formatMobiles($to));
105 |
106 | if ($tempId && $this instanceof TemplateSms) {
107 | $this->sendTemplateSms($to, $tempId, $data);
108 | } elseif ($content && $this instanceof ContentSms) {
109 | $this->sendContentSms($to, $content);
110 | }
111 | }
112 |
113 | /**
114 | * Voice send process.
115 | *
116 | * @param $to
117 | * @param $content
118 | * @param $tempId
119 | * @param array $data
120 | * @param $code
121 | * @param $fileId
122 | * @param array $params
123 | */
124 | public function sendVoice($to, $content = null, $tempId = null, array $data = [], $code = null, $fileId = null, array $params = [])
125 | {
126 | $this->reset();
127 | $this->params($params, true);
128 | $to = $this->formatMobile(Util::formatMobiles($to));
129 |
130 | if ($tempId && $this instanceof TemplateVoice) {
131 | $this->sendTemplateVoice($to, $tempId, $data);
132 | } elseif ($fileId && $this instanceof FileVoice) {
133 | $this->sendFileVoice($to, $fileId);
134 | } elseif ($code && $this instanceof VoiceCode) {
135 | $this->sendVoiceCode($to, $code);
136 | } elseif ($content && $this instanceof ContentVoice) {
137 | $this->sendContentVoice($to, $content);
138 | }
139 | }
140 |
141 | /**
142 | * Formatting a mobile number from the list of mobile numbers.
143 | *
144 | * @param array $list
145 | *
146 | * @return string
147 | */
148 | public function formatMobile(array $list)
149 | {
150 | return implode(',', array_unique(array_map(function ($value) {
151 | return is_array($value) ? "{$value['number']}" : $value;
152 | }, $list)));
153 | }
154 |
155 | /**
156 | * @codeCoverageIgnore
157 | *
158 | * @param $url
159 | * @param array $params
160 | * @param array $opts
161 | *
162 | * @return array
163 | */
164 | public function curlPost($url, array $params = [], array $opts = [])
165 | {
166 | $options = [
167 | CURLOPT_POST => true,
168 | CURLOPT_URL => $url,
169 | ];
170 | foreach ($opts as $key => $value) {
171 | if ($key !== CURLOPT_POST && $key !== CURLOPT_URL) {
172 | $options[$key] = $value;
173 | }
174 | }
175 | if (!array_key_exists(CURLOPT_POSTFIELDS, $options)) {
176 | $options[CURLOPT_POSTFIELDS] = $this->params($params);
177 | }
178 |
179 | return self::curl($options);
180 | }
181 |
182 | /**
183 | * @codeCoverageIgnore
184 | *
185 | * @param $url
186 | * @param array $params
187 | * @param array $opts
188 | *
189 | * @return array
190 | */
191 | public function curlGet($url, array $params = [], array $opts = [])
192 | {
193 | $params = $this->params($params);
194 | $queryStr = http_build_query($params);
195 | $opts[CURLOPT_POST] = false;
196 | $opts[CURLOPT_URL] = $queryStr ? "$url?$queryStr" : $url;
197 |
198 | return self::curl($opts);
199 | }
200 |
201 | /**
202 | * cURl
203 | *
204 | * @codeCoverageIgnore
205 | *
206 | * @param array $opts curl options
207 | *
208 | * @return array ['request', 'response']
209 | * request: Whether request success.
210 | * response: Response data.
211 | */
212 | public static function curl(array $opts = [])
213 | {
214 | $ch = curl_init();
215 | curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
216 | curl_setopt($ch, CURLOPT_HEADER, false);
217 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
218 | curl_setopt($ch, CURLOPT_TIMEOUT, 30);
219 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
220 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
221 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
222 | foreach ($opts as $key => $value) {
223 | curl_setopt($ch, $key, $value);
224 | }
225 |
226 | $response = curl_exec($ch);
227 | $request = $response !== false;
228 | if (!$request) {
229 | $response = curl_getinfo($ch);
230 | }
231 | curl_close($ch);
232 |
233 | return compact('request', 'response');
234 | }
235 |
236 | /**
237 | * Get or set the result data.
238 | *
239 | * @param $name
240 | * @param $value
241 | *
242 | * @return mixed
243 | */
244 | public function result($name = null, $value = null)
245 | {
246 | if ($name === null) {
247 | return $this->result;
248 | }
249 | if (array_key_exists($name, $this->result)) {
250 | if ($value === null) {
251 | return $this->result[$name];
252 | }
253 | $this->result[$name] = $value;
254 | }
255 | }
256 |
257 | /**
258 | * Overload object properties.
259 | *
260 | * @param $name
261 | *
262 | * @return mixed
263 | */
264 | public function __get($name)
265 | {
266 | return $this->config($name);
267 | }
268 |
269 | /**
270 | * When using isset() or empty() on inaccessible object properties,
271 | * the __isset() overloading method will be called.
272 | *
273 | * @param $name
274 | *
275 | * @return bool
276 | */
277 | public function __isset($name)
278 | {
279 | return isset($this->config[$name]);
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/src/phpsms/agents/AlidayuAgent.php:
--------------------------------------------------------------------------------
1 | 'alibaba.aliqin.fc.sms.num.send',
27 | 'sms_type' => 'normal',
28 | 'sms_free_sign_name' => $this->smsFreeSignName,
29 | 'sms_param' => $this->getTempDataString($tempData),
30 | 'rec_num' => $to,
31 | 'sms_template_code' => $tempId,
32 | ];
33 | $this->request($params);
34 | }
35 |
36 | /**
37 | * Template voice send process.
38 | *
39 | * @param string|array $to
40 | * @param int|string $tempId
41 | * @param array $tempData
42 | */
43 | public function sendTemplateVoice($to, $tempId, array $tempData)
44 | {
45 | $params = [
46 | 'called_num' => $to,
47 | 'called_show_num' => $this->calledShowNum,
48 | 'method' => 'alibaba.aliqin.fc.tts.num.singlecall',
49 | 'tts_code' => $tempId,
50 | 'tts_param' => $this->getTempDataString($tempData),
51 | ];
52 | $this->request($params);
53 | }
54 |
55 | /**
56 | * Voice code send process.
57 | *
58 | * @param string|array $to
59 | * @param int|string $code
60 | */
61 | public function sendVoiceCode($to, $code)
62 | {
63 | $params = [
64 | 'called_num' => $to,
65 | 'called_show_num' => $this->calledShowNum,
66 | 'method' => 'alibaba.aliqin.fc.voice.num.singlecall',
67 | 'voice_code' => $code,
68 | ];
69 | $this->request($params);
70 | }
71 |
72 | protected function request(array $params)
73 | {
74 | $params = $this->createParams($params);
75 | $result = $this->curlPost($this->sendUrl, [], [
76 | CURLOPT_POSTFIELDS => http_build_query($params),
77 | ]);
78 | $this->setResult($result, $this->genResponseName($params['method']));
79 | }
80 |
81 | protected function createParams(array $params)
82 | {
83 | $params = array_merge([
84 | 'app_key' => $this->appKey,
85 | 'v' => '2.0',
86 | 'format' => 'json',
87 | 'sign_method' => 'md5',
88 | 'timestamp' => date('Y-m-d H:i:s'),
89 | ], $params);
90 | $params['sign'] = $this->genSign($params);
91 |
92 | return $this->params($params);
93 | }
94 |
95 | protected function genSign($params)
96 | {
97 | ksort($params);
98 | $stringToBeSigned = $this->secretKey;
99 | foreach ($params as $k => $v) {
100 | if (is_string($v) && '@' !== substr($v, 0, 1)) {
101 | $stringToBeSigned .= "$k$v";
102 | }
103 | }
104 | unset($k, $v);
105 | $stringToBeSigned .= $this->secretKey;
106 |
107 | return strtoupper(md5($stringToBeSigned));
108 | }
109 |
110 | protected function setResult($result, $callbackName)
111 | {
112 | if ($result['request']) {
113 | $result = json_decode($result['response'], true);
114 | if (isset($result[$callbackName]['result'])) {
115 | $result = $result[$callbackName]['result'];
116 | $this->result(Agent::SUCCESS, (bool) $result['success']);
117 | $this->result(Agent::INFO, json_encode($result));
118 | $this->result(Agent::CODE, $result['err_code']);
119 | } elseif (isset($result['error_response'])) {
120 | $error = $result['error_response'];
121 | $this->result(Agent::INFO, json_encode($error));
122 | $this->result(Agent::CODE, $error['code']);
123 | }
124 | } else {
125 | $this->result(Agent::INFO, 'request failed');
126 | }
127 | }
128 |
129 | protected function genResponseName($method)
130 | {
131 | return str_replace('.', '_', $method) . '_response';
132 | }
133 |
134 | protected function getTempDataString(array $data)
135 | {
136 | $data = array_map(function ($value) {
137 | return (string) $value;
138 | }, $data);
139 |
140 | return json_encode($data);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/phpsms/agents/AliyunAgent.php:
--------------------------------------------------------------------------------
1 | 'SendSms',
21 | 'SignName' => $this->signName,
22 | 'TemplateParam' => $this->getTempDataString($data),
23 | 'PhoneNumbers' => $to,
24 | 'TemplateCode' => $tempId,
25 | ];
26 | $this->request($params);
27 | }
28 |
29 | protected function request(array $params)
30 | {
31 | $params = $this->createParams($params);
32 | $result = $this->curlPost(self::$sendUrl, [], [
33 | CURLOPT_POSTFIELDS => http_build_query($params),
34 | ]);
35 | $this->setResult($result);
36 | }
37 |
38 | protected function createParams(array $params)
39 | {
40 | $params = array_merge([
41 | 'RegionId' => $this->regionId ?: 'cn-shenzhen',
42 | 'Format' => 'JSON',
43 | 'Version' => '2017-05-25',
44 | 'AccessKeyId' => $this->accessKeyId,
45 | 'SignatureMethod' => 'HMAC-SHA1',
46 | 'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
47 | 'SignatureVersion' => '1.0',
48 | 'SignatureNonce' => uniqid(),
49 | ], $params);
50 | $params['Signature'] = $this->computeSignature($params);
51 |
52 | return $this->params($params);
53 | }
54 |
55 | private function computeSignature($parameters)
56 | {
57 | ksort($parameters);
58 | $canonicalizedQueryString = '';
59 | foreach ($parameters as $key => $value) {
60 | $canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value);
61 | }
62 | $stringToSign = 'POST&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1));
63 |
64 | return base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret . '&', true));
65 | }
66 |
67 | protected function percentEncode($str)
68 | {
69 | $res = urlencode($str);
70 | $res = preg_replace('/\+/', '%20', $res);
71 | $res = preg_replace('/\*/', '%2A', $res);
72 | $res = preg_replace('/%7E/', '~', $res);
73 |
74 | return $res;
75 | }
76 |
77 | protected function setResult($result)
78 | {
79 | if ($result['request']) {
80 | $this->result(Agent::INFO, $result['response']);
81 | $result = json_decode($result['response'], true);
82 | $this->result(Agent::CODE, $result['Code']);
83 | if ($result['Code'] === 'OK') {
84 | $this->result(Agent::SUCCESS, true);
85 | }
86 | } else {
87 | $this->result(Agent::INFO, 'request failed');
88 | }
89 | }
90 |
91 | protected function getTempDataString(array $data)
92 | {
93 | $data = array_map(function ($value) {
94 | return (string) $value;
95 | }, $data);
96 |
97 | return json_encode($data);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/phpsms/agents/JuHeAgent.php:
--------------------------------------------------------------------------------
1 | $value) {
18 | if (preg_match('/[#&=]/', $value)) {
19 | $value = urlencode($value);
20 | }
21 | $split = !$tplValue ? '' : '&';
22 | $tplValue .= "$split#$key#=$value";
23 | }
24 | $params = [
25 | 'key' => $this->key,
26 | 'mobile' => $to,
27 | 'tpl_id' => $tempId,
28 | 'tpl_value' => urlencode($tplValue),
29 | 'dtype' => 'json',
30 | ];
31 | $result = $this->curlGet($sendUrl, $params);
32 | $this->setResult($result);
33 | }
34 |
35 | public function sendVoiceCode($to, $code)
36 | {
37 | $url = 'http://op.juhe.cn/yuntongxun/voice';
38 | $params = [
39 | 'valicode' => $code,
40 | 'to' => $to,
41 | 'playtimes' => $this->times ?: 3,
42 | 'key' => $this->key,
43 | 'dtype' => 'json',
44 | ];
45 | $result = $this->curlGet($url, $params);
46 | $this->setResult($result);
47 | }
48 |
49 | protected function setResult($result)
50 | {
51 | if ($result['request']) {
52 | $this->result(Agent::INFO, $result['response']);
53 | $result = json_decode($result['response'], true);
54 | $this->result(Agent::SUCCESS, $result['error_code'] === 0);
55 | $this->result(Agent::CODE, $result['error_code']);
56 | } else {
57 | $this->result(Agent::INFO, 'request failed');
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/phpsms/agents/LogAgent.php:
--------------------------------------------------------------------------------
1 | result(Agent::SUCCESS, true);
13 | $this->result(Agent::INFO, 'send content sms success');
14 | }
15 |
16 | public function sendTemplateSms($to, $tempId, array $data)
17 | {
18 | $this->result(Agent::SUCCESS, true);
19 | $this->result(Agent::INFO, 'send template sms success');
20 | }
21 |
22 | public function sendVoiceCode($to, $code)
23 | {
24 | $this->result(Agent::SUCCESS, true);
25 | $this->result(Agent::INFO, 'send voice verify success');
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/phpsms/agents/LuosimaoAgent.php:
--------------------------------------------------------------------------------
1 | curlPost(self::$smsUrl, [
25 | 'mobile' => $to,
26 | 'message' => $content,
27 | ], [
28 | CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
29 | CURLOPT_USERPWD => "api:key-{$this->apikey}",
30 | ]);
31 | $this->setResult($result);
32 | }
33 |
34 | public function sendVoiceCode($to, $code)
35 | {
36 | $result = $this->curlPost(self::$voiceCodeUrl, [
37 | 'mobile' => $to,
38 | 'code' => $code,
39 | ], [
40 | CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
41 | CURLOPT_USERPWD => "api:key-{$this->voiceApikey}",
42 | ]);
43 | $this->setResult($result);
44 | }
45 |
46 | protected function setResult($result)
47 | {
48 | if ($result['request']) {
49 | $this->result(Agent::INFO, $result['response']);
50 | $result = json_decode($result['response'], true);
51 | $this->result(Agent::SUCCESS, $result['error'] === 0);
52 | $this->result(Agent::CODE, $result['error']);
53 | } else {
54 | $this->result(Agent::INFO, 'request failed');
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/phpsms/agents/ParasiticAgent.php:
--------------------------------------------------------------------------------
1 | handlers = $handlers;
20 | }
21 |
22 | /**
23 | * Content SMS send process.
24 | *
25 | * @param string|array $to
26 | * @param string $content
27 | */
28 | public function sendContentSms($to, $content)
29 | {
30 | $this->handle(__FUNCTION__, func_get_args());
31 | }
32 |
33 | /**
34 | * Content voice send process.
35 | *
36 | * @param string|array $to
37 | * @param string $content
38 | */
39 | public function sendContentVoice($to, $content)
40 | {
41 | $this->handle(__FUNCTION__, func_get_args());
42 | }
43 |
44 | /**
45 | * File voice send process.
46 | *
47 | * @param string|array $to
48 | * @param int|string $fileId
49 | */
50 | public function sendFileVoice($to, $fileId)
51 | {
52 | $this->handle(__FUNCTION__, func_get_args());
53 | }
54 |
55 | /**
56 | * Template SMS send process.
57 | *
58 | * @param string|array $to
59 | * @param int|string $tempId
60 | * @param array $tempData
61 | */
62 | public function sendTemplateSms($to, $tempId, array $tempData)
63 | {
64 | $this->handle(__FUNCTION__, func_get_args());
65 | }
66 |
67 | /**
68 | * Template voice send process.
69 | *
70 | * @param string|array $to
71 | * @param int|string $tempId
72 | * @param array $tempData
73 | */
74 | public function sendTemplateVoice($to, $tempId, array $tempData)
75 | {
76 | $this->handle(__FUNCTION__, func_get_args());
77 | }
78 |
79 | /**
80 | * Voice code send process.
81 | *
82 | * @param string|array $to
83 | * @param int|string $code
84 | */
85 | public function sendVoiceCode($to, $code)
86 | {
87 | $this->handle(__FUNCTION__, func_get_args());
88 | }
89 |
90 | /**
91 | * Handle send process by closure.
92 | *
93 | * @param $name
94 | * @param array $args
95 | */
96 | protected function handle($name, array $args = [])
97 | {
98 | if (isset($this->handlers[$name]) && is_callable($this->handlers[$name])) {
99 | array_unshift($args, $this);
100 | call_user_func_array($this->handlers[$name], $args);
101 | }
102 | }
103 |
104 | /**
105 | * Get methods name which inherit from interfaces.
106 | *
107 | * @return array
108 | */
109 | public static function methods()
110 | {
111 | if (!is_array(self::$methods)) {
112 | self::$methods = [];
113 | $interfaces = class_implements('Toplan\\PhpSms\\ParasiticAgent');
114 | foreach ($interfaces as $interface) {
115 | self::$methods = array_merge(self::$methods, get_class_methods($interface));
116 | }
117 | }
118 |
119 | return self::$methods;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/phpsms/agents/QcloudAgent.php:
--------------------------------------------------------------------------------
1 | $value['nation'],
23 | 'mobile' => $value['number'],
24 | ];
25 | }, array_filter($list, function ($value) {
26 | return is_array($value);
27 | }));
28 |
29 | return count($list) === 1 ? array_pop($list) : array_values($list);
30 | }
31 |
32 | public function sendContentSms($to, $content)
33 | {
34 | $params = [
35 | 'type' => 0, // 0:普通短信 1:营销短信
36 | 'msg' => $content,
37 | 'tel' => $to,
38 | 'time' => time(),
39 | ];
40 | $this->random = $this->getRandom();
41 | $sendUrl = "{$this->sendSms}?sdkappid={$this->appId}&random={$this->random}";
42 | $this->request($sendUrl, $params);
43 | }
44 |
45 | public function sendTemplateSms($to, $tempId, array $data)
46 | {
47 | $params = [
48 | 'tel' => $to,
49 | 'tpl_id' => $tempId,
50 | 'params' => array_values($data),
51 | 'time' => time(),
52 | ];
53 | $this->random = $this->getRandom();
54 | $sendUrl = "{$this->sendSms}?sdkappid={$this->appId}&random={$this->random}";
55 | $this->request($sendUrl, $params);
56 | }
57 |
58 | public function sendVoiceCode($to, $code)
59 | {
60 | $params = [
61 | 'tel' => $to,
62 | 'msg' => $code,
63 | ];
64 | $sendUrl = "{$this->sendVoiceCode}?sdkappid={$this->appId}&random={$this->random}";
65 | $this->request($sendUrl, $params);
66 | }
67 |
68 | public function sendContentVoice($to, $content)
69 | {
70 | $params = [
71 | 'tel' => $to,
72 | 'prompttype' => 2,
73 | 'promptfile' => $content,
74 | ];
75 | $sendUrl = "{$this->sendVoicePrompt}?sdkappid={$this->appId}&random={$this->random}";
76 | $this->request($sendUrl, $params);
77 | }
78 |
79 | protected function request($sendUrl, array $params)
80 | {
81 | $params['sig'] = $this->genSign($params);
82 | $params = $this->params($params);
83 | $result = $this->curlPost($sendUrl, [], [
84 | CURLOPT_POSTFIELDS => json_encode($params),
85 | ]);
86 | $this->setResult($result);
87 | }
88 |
89 | protected function genSign($params)
90 | {
91 | $mobileStr = null;
92 | if (array_key_exists('mobile', $params['tel'])) {
93 | $mobileStr = $params['tel']['mobile'];
94 | } else {
95 | $mobileStr = implode(',', array_map(function ($value) {
96 | return $value['mobile'];
97 | }, $params['tel']));
98 | }
99 | $signature = "appkey={$this->appKey}&random={$this->random}&time={$params['time']}&mobile={$mobileStr}";
100 |
101 | return hash('sha256', $signature, false);
102 | }
103 |
104 | protected function setResult($result)
105 | {
106 | if ($result['request']) {
107 | $this->result(Agent::INFO, $result['response']);
108 | $result = json_decode($result['response'], true);
109 | if (isset($result['result'])) {
110 | $this->result(Agent::SUCCESS, $result['result'] === 0);
111 | $this->result(Agent::CODE, $result['result']);
112 | } elseif (isset($result['ErrorCode'])) {
113 | $this->result(Agent::CODE, $result['ErrorCode']);
114 | $this->result(Agent::INFO, $result['ErrorInfo']);
115 | }
116 | } else {
117 | $this->result(Agent::INFO, 'request failed');
118 | }
119 | }
120 |
121 | protected function getRandom()
122 | {
123 | return rand(100000, 999999);
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/phpsms/agents/SendCloudAgent.php:
--------------------------------------------------------------------------------
1 | 0,
17 | 'vars' => $this->getTempDataString($data),
18 | 'phone' => $to,
19 | 'templateId' => $tempId,
20 | ];
21 | $this->request('http://sendcloud.sohu.com/smsapi/send', $params);
22 | }
23 |
24 | public function sendVoiceCode($to, $code)
25 | {
26 | $params = [
27 | 'phone' => $to,
28 | 'code' => $code,
29 | ];
30 | $this->request('http://sendcloud.sohu.com/smsapi/sendVoice', $params);
31 | }
32 |
33 | protected function request($sendUrl, array $params)
34 | {
35 | $params['smsUser'] = $this->smsUser;
36 | $params['signature'] = $this->genSign($params);
37 | $result = $this->curlPost($sendUrl, $params);
38 | $this->setResult($result);
39 | }
40 |
41 | protected function genSign($params)
42 | {
43 | ksort($params);
44 | $stringToBeSigned = '';
45 | foreach ($params as $k => $v) {
46 | $stringToBeSigned .= $k . '=' . $v . '&';
47 | }
48 | $stringToBeSigned = trim($stringToBeSigned, '&');
49 | $stringToBeSigned = $this->smsKey . '&' . $stringToBeSigned . '&' . $this->smsKey;
50 |
51 | return md5($stringToBeSigned);
52 | }
53 |
54 | protected function setResult($result)
55 | {
56 | if ($result['request']) {
57 | $this->result(Agent::INFO, $result['response']);
58 | $result = json_decode($result['response'], true);
59 | if (isset($result['result'])) {
60 | $this->result(Agent::SUCCESS, (bool) $result['result']);
61 | $this->result(Agent::CODE, $result['statusCode']);
62 | }
63 | } else {
64 | $this->result(Agent::INFO, 'request failed');
65 | }
66 | }
67 |
68 | protected function getTempDataString(array $data)
69 | {
70 | return json_encode(array_map('strval', $data));
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/phpsms/agents/SmsBaoAgent.php:
--------------------------------------------------------------------------------
1 | '发送成功',
15 | '-1' => '参数不全',
16 | '30' => '密码错误',
17 | '40' => '账号不存在',
18 | '41' => '余额不足',
19 | '42' => '帐户已过期',
20 | '43' => 'IP地址限制',
21 | '50' => '内容含有敏感词',
22 | '51' => '手机号码不正确',
23 | ];
24 |
25 | public function sendContentSms($to, $content)
26 | {
27 | $url = 'http://api.smsbao.com/sms';
28 | $params = [
29 | 'u' => $this->username,
30 | 'p' => md5($this->password),
31 | 'm' => $to,
32 | 'c' => $content,
33 | ];
34 | $result = $this->curlGet($url, $params);
35 | $this->setResult($result);
36 | }
37 |
38 | public function sendVoiceCode($to, $code)
39 | {
40 | $url = 'http://api.smsbao.com/voice';
41 | $params = [
42 | 'u' => $this->username,
43 | 'p' => md5($this->password),
44 | 'm' => $to,
45 | 'c' => $code,
46 | ];
47 | $result = $this->curlGet($url, $params);
48 | $this->setResult($result);
49 | }
50 |
51 | protected function setResult($result)
52 | {
53 | if ($result['request']) {
54 | $result = $result['response'];
55 | $msg = array_key_exists($result, $this->resultArr) ? $this->resultArr[$result] : 'unknown error';
56 | $this->result(Agent::INFO, json_encode(['code' => $result, 'msg' => $msg]));
57 | $this->result(Agent::SUCCESS, $result === '0');
58 | $this->result(Agent::CODE, $result);
59 | } else {
60 | $this->result(Agent::INFO, 'request failed');
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/phpsms/agents/SubMailAgent.php:
--------------------------------------------------------------------------------
1 | $this->appid,
18 | 'project' => $tempId,
19 | 'to' => $to,
20 | 'signature' => $this->signature,
21 | 'vars' => json_encode($data),
22 | ];
23 | $result = $this->curlPost($url, $params);
24 | $this->setResult($result);
25 | }
26 |
27 | public function sendVoiceCode($to, $code)
28 | {
29 | $url = 'https://api.mysubmail.com/voice/verify.json';
30 | $params = [
31 | 'appid' => $this->appid,
32 | 'to' => $to,
33 | 'code' => $code,
34 | 'signature' => $this->signature,
35 | ];
36 | $result = $this->curlPost($url, $params);
37 | $this->setResult($result);
38 | }
39 |
40 | protected function setResult($result)
41 | {
42 | if ($result['request']) {
43 | $this->result(Agent::INFO, $result['response']);
44 | $result = json_decode($result['response'], true);
45 | if ($result['status'] === 'success') {
46 | $this->result(Agent::SUCCESS, true);
47 | } else {
48 | $this->result(Agent::INFO, $result['msg']);
49 | $this->result(Agent::CODE, $result['code']);
50 | }
51 | } else {
52 | $this->result(Agent::INFO, 'request failed');
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/phpsms/agents/UcpaasAgent.php:
--------------------------------------------------------------------------------
1 | ucpass()->templateSMS($this->appId, $to, $tempId, implode(',', $data));
17 | $this->setResult($response);
18 | }
19 |
20 | public function sendVoiceCode($to, $code)
21 | {
22 | $response = $this->ucpass()->voiceCode($this->appId, $code, $to);
23 | $this->result($response);
24 | }
25 |
26 | protected function ucpass()
27 | {
28 | return new \Ucpaas([
29 | 'accountsid' => $this->accountSid,
30 | 'token' => $this->accountToken,
31 | ]);
32 | }
33 |
34 | protected function setResult($result)
35 | {
36 | $result = json_decode($result);
37 | if (!$result) {
38 | return $this->result(Agent::INFO, 'request failed');
39 | }
40 | $this->result(Agent::SUCCESS, $result->resp->respCode === '000000');
41 | $this->result(Agent::CODE, $result->resp->respCode);
42 | $this->result(Agent::INFO, json_encode($result->resp));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/phpsms/agents/YunPianAgent.php:
--------------------------------------------------------------------------------
1 | params([
21 | 'apikey' => $this->apikey,
22 | 'mobile' => $to,
23 | 'text' => $content,
24 | ]);
25 | $result = $this->curlPost($url, [], [
26 | CURLOPT_HTTPHEADER => $this->headers,
27 | CURLOPT_POSTFIELDS => http_build_query($params),
28 | ]);
29 | $this->setResult($result);
30 | }
31 |
32 | public function sendVoiceCode($to, $code)
33 | {
34 | $url = 'https://voice.yunpian.com/v1/voice/send.json';
35 | $params = $this->params([
36 | 'apikey' => $this->apikey,
37 | 'mobile' => $to,
38 | 'code' => $code,
39 | ]);
40 | $result = $this->curlPost($url, [], [
41 | CURLOPT_HTTPHEADER => $this->headers,
42 | CURLOPT_POSTFIELDS => http_build_query($params),
43 | ]);
44 | $this->setResult($result);
45 | }
46 |
47 | protected function setResult($result)
48 | {
49 | if ($result['request']) {
50 | $this->result(Agent::INFO, $result['response']);
51 | $result = json_decode($result['response'], true);
52 | $this->result(Agent::SUCCESS, $result['code'] === 0);
53 | $this->result(Agent::CODE, $result['code']);
54 | } else {
55 | $this->result(Agent::INFO, 'request failed');
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/phpsms/agents/YunTongXunAgent.php:
--------------------------------------------------------------------------------
1 | rest()->sendTemplateSMS($to, $data, $tempId);
24 | $this->setResult($result);
25 | }
26 |
27 | public function sendVoiceCode($to, $code)
28 | {
29 | $playTimes = intval($this->playTimes ?: 3);
30 | $displayNum = $this->displayNum ?: null;
31 | $lang = $this->params('lang') ?: 'zh';
32 | $respUrl = $this->params('respUrl');
33 | $userData = $this->params('userData');
34 | $welcomePrompt = $this->params('welcomePrompt');
35 | $result = $this->rest()->voiceVerify($code, $playTimes, $to, $displayNum, $respUrl, $lang, $userData, $welcomePrompt);
36 | $this->setResult($result);
37 | }
38 |
39 | protected function rest()
40 | {
41 | $rest = new REST($this->serverIP, $this->serverPort, '2013-12-26', 'json');
42 | $rest->setAccount($this->accountSid, $this->accountToken);
43 | $rest->setAppId($this->appId);
44 |
45 | return $rest;
46 | }
47 |
48 | protected function setResult($result)
49 | {
50 | if (!$result) {
51 | return;
52 | }
53 | $code = $info = (string) $result->statusCode;
54 | $success = $code === '000000';
55 | if (isset($result->statusMsg)) {
56 | $info = (string) $result->statusMsg;
57 | } elseif (isset($result->TemplateSMS)) {
58 | $info = 'smsSid:' . $result->TemplateSMS->smsMessageSid;
59 | } elseif (isset($result->VoiceVerify)) {
60 | $info = 'callSid:' . $result->VoiceVerify->callSid;
61 | }
62 | $this->result(Agent::SUCCESS, $success);
63 | $this->result(Agent::CODE, $code);
64 | $this->result(Agent::INFO, $info);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/phpsms/facades/Sms.php:
--------------------------------------------------------------------------------
1 | Batch = date('YmdHis');
30 | $this->ServerIP = $ServerIP;
31 | $this->ServerPort = $ServerPort;
32 | $this->SoftVersion = $SoftVersion;
33 | if (in_array($BodyType, ['xml', 'json'])) {
34 | $this->BodyType = $BodyType;
35 | }
36 | }
37 |
38 | /**
39 | * 设置主帐号
40 | *
41 | * @param string $AccountSid 主帐号
42 | * @param string $AccountToken 主帐号Token
43 | */
44 | public function setAccount($AccountSid, $AccountToken)
45 | {
46 | $this->AccountSid = $AccountSid;
47 | $this->AccountToken = $AccountToken;
48 | }
49 |
50 | /**
51 | * 设置应用ID
52 | *
53 | * @param string $AppId 应用ID
54 | */
55 | public function setAppId($AppId)
56 | {
57 | $this->AppId = $AppId;
58 | }
59 |
60 | /**
61 | * 发起HTTPS请求
62 | *
63 | * @param string $url
64 | * @param mixed $data
65 | * @param mixed $header
66 | * @param mixed $post
67 | *
68 | * @return mixed
69 | */
70 | public function curl_post($url, $data, $header, $post = 1)
71 | {
72 | //初始化curl
73 | $ch = curl_init();
74 | //参数设置
75 | $res = curl_setopt($ch, CURLOPT_URL, $url);
76 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
77 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
78 | curl_setopt($ch, CURLOPT_HEADER, 0);
79 | curl_setopt($ch, CURLOPT_POST, $post);
80 | if ($post) {
81 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
82 | }
83 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
84 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
85 | $result = curl_exec($ch);
86 | //连接失败
87 | if ($result === false) {
88 | if ($this->BodyType === 'json') {
89 | $result = '{"statusCode":"172001","statusMsg":"网络错误"}';
90 | } else {
91 | $result = '172001网络错误';
92 | }
93 | }
94 | curl_close($ch);
95 |
96 | return $result;
97 | }
98 |
99 | /**
100 | * 发送模板短信
101 | *
102 | * @param string $to
103 | * 短信接收彿手机号码集合,用英文逗号分开
104 | * @param array $datas
105 | * 内容数据
106 | * @param mixed $tempId
107 | * 模板Id
108 | *
109 | * @return mixed
110 | */
111 | public function sendTemplateSMS($to, $datas, $tempId)
112 | {
113 | //主帐号鉴权信息验证,对必选参数进行判空。
114 | $auth = $this->accAuth();
115 | if ($auth !== true) {
116 | return $auth;
117 | }
118 | // 拼接请求包体
119 | if ($this->BodyType === 'json') {
120 | $data = '';
121 | for ($i = 0; $i < count($datas); $i++) {
122 | $data = $data . "'" . $datas[$i] . "',";
123 | }
124 | $body = "{'to':'$to','templateId':'$tempId','appId':'$this->AppId','datas':[" . $data . ']}';
125 | } else {
126 | $data = '';
127 | for ($i = 0; $i < count($datas); $i++) {
128 | $data = $data . '' . $datas[$i] . '';
129 | }
130 | $body = "
131 | $to
132 | $this->AppId
133 | $tempId
134 | " . $data . '
135 | ';
136 | }
137 | // 大写的sig参数
138 | $sig = strtoupper(md5($this->AccountSid . $this->AccountToken . $this->Batch));
139 | // 生成请求URL
140 | $url = "https://$this->ServerIP:$this->ServerPort/$this->SoftVersion/Accounts/$this->AccountSid/SMS/TemplateSMS?sig=$sig";
141 | // 生成授权:主帐户Id + 英文冒号 + 时间戳。
142 | $authen = base64_encode($this->AccountSid . ':' . $this->Batch);
143 | // 生成包头
144 | $header = array("Accept:application/$this->BodyType", "Content-Type:application/$this->BodyType;charset=utf-8", "Authorization:$authen");
145 | // 发送请求
146 | $result = $this->curl_post($url, $body, $header);
147 | if ($this->BodyType === 'json') {//JSON格式
148 | $datas = json_decode($result);
149 | } else { //xml格式
150 | $datas = simplexml_load_string(trim($result, " \t\n\r"));
151 | }
152 | // 重新装填数据
153 | if (isset($datas->templateSMS)) {
154 | $datas->TemplateSMS = $datas->templateSMS;
155 | }
156 |
157 | return $datas;
158 | }
159 |
160 | /**
161 | * 语音验证码
162 | *
163 | * @param mixed $verifyCode 验证码内容,为数字和英文字母,不区分大小写,长度4-8位
164 | * @param mixed $playTimes 播放次数,1-3次
165 | * @param mixed $to 接收号码
166 | * @param mixed $displayNum 显示的主叫号码
167 | * @param mixed $respUrl 语音验证码状态通知回调地址,云通讯平台将向该Url地址发送呼叫结果通知
168 | * @param mixed $lang 语言类型
169 | * @param mixed $userData 第三方私有数据
170 | * @param mixed $welcomePrompt 欢迎提示音,在播放验证码语音前播放此内容(语音文件格式为wav)
171 | * @param mixed $playVerifyCode 语音验证码的内容全部播放此节点下的全部语音文件
172 | *
173 | * @return mixed
174 | */
175 | public function voiceVerify($verifyCode, $playTimes, $to, $displayNum = null, $respUrl = null, $lang = 'zh', $userData = null, $welcomePrompt = null, $playVerifyCode = null)
176 | {
177 | //主帐号鉴权信息验证,对必选参数进行判空。
178 | $auth = $this->accAuth();
179 | if ($auth !== true) {
180 | return $auth;
181 | }
182 | // 拼接请求包体
183 | if ($this->BodyType === 'json') {
184 | $body = "{'appId':'$this->AppId','verifyCode':'$verifyCode','playTimes':'$playTimes','to':'$to','respUrl':'$respUrl','displayNum':'$displayNum',
185 | 'lang':'$lang','userData':'$userData','welcomePrompt':'$welcomePrompt','playVerifyCode':'$playVerifyCode'}";
186 | } else {
187 | $body = "
188 | $this->AppId
189 | $verifyCode
190 | $playTimes
191 | $to
192 | $respUrl
193 | $displayNum
194 | $lang
195 | $userData
196 | $welcomePrompt
197 | $playVerifyCode
198 | ";
199 | }
200 | // 大写的sig参数
201 | $sig = strtoupper(md5($this->AccountSid . $this->AccountToken . $this->Batch));
202 | // 生成请求URL
203 | $url = "https://$this->ServerIP:$this->ServerPort/$this->SoftVersion/Accounts/$this->AccountSid/Calls/VoiceVerify?sig=$sig";
204 | // 生成授权:主帐户Id + 英文冒号 + 时间戳。
205 | $authen = base64_encode($this->AccountSid . ':' . $this->Batch);
206 | // 生成包头
207 | $header = array("Accept:application/$this->BodyType", "Content-Type:application/$this->BodyType;charset=utf-8", "Authorization:$authen");
208 | // 发送请求
209 | $result = $this->curl_post($url, $body, $header);
210 | if ($this->BodyType === 'json') {//JSON格式
211 | $datas = json_decode($result);
212 | } else { //xml格式
213 | $datas = simplexml_load_string(trim($result, " \t\n\r"));
214 | }
215 |
216 | return $datas;
217 | }
218 |
219 | /**
220 | * 主帐号鉴权
221 | *
222 | * @return mixed
223 | */
224 | public function accAuth()
225 | {
226 | if ($this->ServerIP === '') {
227 | $data = new stdClass();
228 | $data->statusCode = '172004';
229 | $data->statusMsg = 'IP为空';
230 |
231 | return $data;
232 | }
233 | if ($this->ServerPort <= 0) {
234 | $data = new stdClass();
235 | $data->statusCode = '172005';
236 | $data->statusMsg = '端口错误(小于等于0)';
237 |
238 | return $data;
239 | }
240 | if ($this->SoftVersion === '') {
241 | $data = new stdClass();
242 | $data->statusCode = '172013';
243 | $data->statusMsg = '版本号为空';
244 |
245 | return $data;
246 | }
247 | if ($this->AccountSid === '') {
248 | $data = new stdClass();
249 | $data->statusCode = '172006';
250 | $data->statusMsg = '主帐号为空';
251 |
252 | return $data;
253 | }
254 | if ($this->AccountToken === '') {
255 | $data = new stdClass();
256 | $data->statusCode = '172007';
257 | $data->statusMsg = '主帐号令牌为空';
258 |
259 | return $data;
260 | }
261 | if ($this->AppId === '') {
262 | $data = new stdClass();
263 | $data->statusCode = '172012';
264 | $data->statusMsg = '应用ID为空';
265 |
266 | return $data;
267 | }
268 |
269 | return true;
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/src/phpsms/lib/Ucpaas.php:
--------------------------------------------------------------------------------
1 | accountSid = isset($options['accountsid']) ? $options['accountsid'] : '';
49 | $this->token = isset($options['token']) ? $options['token'] : '';
50 | $this->timestamp = date('YmdHis');
51 | } else {
52 | throw new Exception('非法参数');
53 | }
54 | }
55 |
56 | /**
57 | * @return string
58 | * 包头验证信息,使用Base64编码(账户Id:时间戳)
59 | */
60 | private function getAuthorization()
61 | {
62 | $data = $this->accountSid . ':' . $this->timestamp;
63 |
64 | return trim(base64_encode($data));
65 | }
66 |
67 | /**
68 | * @return string
69 | * 验证参数,URL后必须带有sig参数,sig= MD5(账户Id + 账户授权令牌 + 时间戳,共32位)(注:转成大写)
70 | */
71 | private function getSigParameter()
72 | {
73 | $sig = $this->accountSid . $this->token . $this->timestamp;
74 |
75 | return strtoupper(md5($sig));
76 | }
77 |
78 | /**
79 | * @param string $url
80 | * @param string|null $body
81 | * @param string $type
82 | * @param string $method
83 | *
84 | * @return mixed|string
85 | */
86 | private function getResult($url, $body, $type, $method)
87 | {
88 | $data = $this->connection($url, $body, $type, $method);
89 | if (isset($data) && !empty($data)) {
90 | $result = $data;
91 | } else {
92 | $result = '没有返回数据';
93 | }
94 |
95 | return $result;
96 | }
97 |
98 | /**
99 | * @param string $url
100 | * @param string $type
101 | * @param string|null $body
102 | * @param string $method
103 | *
104 | * @return mixed|string
105 | */
106 | private function connection($url, $body, $type, $method)
107 | {
108 | if ($type === 'json') {
109 | $mine = 'application/json';
110 | } else {
111 | $mine = 'application/xml';
112 | }
113 | if (function_exists('curl_init')) {
114 | $header = array(
115 | 'Accept:' . $mine,
116 | 'Content-Type:' . $mine . ';charset=utf-8',
117 | 'Authorization:' . $this->getAuthorization(),
118 | );
119 | $ch = curl_init($url);
120 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
121 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
122 | if ($method === 'post') {
123 | curl_setopt($ch, CURLOPT_POST, 1);
124 | curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
125 | }
126 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
127 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
128 | $result = curl_exec($ch);
129 | curl_close($ch);
130 | } else {
131 | $opts = array();
132 | $opts['http'] = array();
133 | $headers = array(
134 | 'method' => strtoupper($method),
135 | );
136 | $headers[] = 'Accept:' . $mine;
137 | $headers['header'] = array();
138 | $headers['header'][] = 'Authorization: ' . $this->getAuthorization();
139 | $headers['header'][] = 'Content-Type:' . $mine . ';charset=utf-8';
140 |
141 | if (!empty($body)) {
142 | $headers['header'][] = 'Content-Length:' . strlen($body);
143 | $headers['content'] = $body;
144 | }
145 |
146 | $opts['http'] = $headers;
147 | $result = file_get_contents($url, false, stream_context_create($opts));
148 | }
149 |
150 | return $result;
151 | }
152 |
153 | /**
154 | * @param $appId
155 | * @param $fromClient
156 | * @param string $to
157 | * @param null $fromSerNum
158 | * @param null $toSerNum
159 | * @param string $type
160 | *
161 | * @throws Exception
162 | *
163 | * @return mixed|string
164 | * @links http://www.ucpaas.com/page/doc/doc_rest3-1.jsp
165 | */
166 | public function callBack($appId, $fromClient, $to, $fromSerNum = null, $toSerNum = null, $type = 'json')
167 | {
168 | $url = self::BaseUrl . self::SoftVersion . '/Accounts/' . $this->accountSid . '/Calls/callBack?sig=' . $this->getSigParameter();
169 | if ($type === 'json') {
170 | $body_json = array('callback' => array(
171 | 'appId' => $appId,
172 | 'fromClient' => $fromClient,
173 | 'fromSerNum' => $fromSerNum,
174 | 'to' => $to,
175 | 'toSerNum' => $toSerNum,
176 | ));
177 | $body = json_encode($body_json);
178 | } elseif ($type === 'xml') {
179 | $body_xml = '
180 |
181 | ' . $fromClient . '
182 | ' . $fromSerNum . '
183 | ' . $to . '
184 | ' . $toSerNum . '
185 | ' . $appId . '
186 | ';
187 | $body = trim($body_xml);
188 | } else {
189 | throw new Exception('只能json或xml,默认为json');
190 | }
191 | $data = $this->getResult($url, $body, $type, 'post');
192 |
193 | return $data;
194 | }
195 |
196 | /**
197 | * @param $appId
198 | * @param $verifyCode
199 | * @param $to
200 | * @param string $type
201 | *
202 | * @throws Exception
203 | *
204 | * @return mixed|string
205 | * @links http://www.ucpaas.com/page/doc/doc_rest3-2.jsp
206 | */
207 | public function voiceCode($appId, $verifyCode, $to, $type = 'json')
208 | {
209 | $url = self::BaseUrl . self::SoftVersion . '/Accounts/' . $this->accountSid . '/Calls/voiceCode?sig=' . $this->getSigParameter();
210 | if ($type === 'json') {
211 | $body_json = array('voiceCode' => array(
212 | 'appId' => $appId,
213 | 'verifyCode' => $verifyCode,
214 | 'to' => $to,
215 | ));
216 | $body = json_encode($body_json);
217 | } elseif ($type === 'xml') {
218 | $body_xml = '
219 |
220 | ' . $verifyCode . '
221 | ' . $to . '
222 | ' . $appId . '
223 | ';
224 | $body = trim($body_xml);
225 | } else {
226 | throw new Exception('只能json或xml,默认为json');
227 | }
228 | $data = $this->getResult($url, $body, $type, 'post');
229 |
230 | return $data;
231 | }
232 |
233 | /**
234 | * @param $appId
235 | * @param $to
236 | * @param $templateId
237 | * @param null $param
238 | * @param string $type
239 | *
240 | * @throws Exception
241 | *
242 | * @return mixed|string
243 | * @links http://www.ucpaas.com/page/doc/doc_rest4-1.jsp
244 | */
245 | public function templateSMS($appId, $to, $templateId, $param = null, $type = 'json')
246 | {
247 | $url = self::BaseUrl . self::SoftVersion . '/Accounts/' . $this->accountSid . '/Messages/templateSMS?sig=' . $this->getSigParameter();
248 | if ($type === 'json') {
249 | $body_json = array('templateSMS' => array(
250 | 'appId' => $appId,
251 | 'templateId' => $templateId,
252 | 'to' => $to,
253 | 'param' => $param,
254 | ));
255 | $body = json_encode($body_json);
256 | } elseif ($type === 'xml') {
257 | $body_xml = '
258 |
259 | ' . $templateId . '
260 | ' . $to . '
261 | ' . $param . '
262 | ' . $appId . '
263 | ';
264 | $body = trim($body_xml);
265 | } else {
266 | throw new Exception('只能json或xml,默认为json');
267 | }
268 | $data = $this->getResult($url, $body, $type, 'post');
269 |
270 | return $data;
271 | }
272 | }
273 |
--------------------------------------------------------------------------------
/tests/AgentTest.php:
--------------------------------------------------------------------------------
1 | 'value',
15 | 'key2' => 'value2',
16 | ];
17 | $this->agent = new LogAgent($config);
18 | }
19 |
20 | public function testResultOriginValue()
21 | {
22 | $r = $this->agent->result();
23 | $this->assertArrayHasKey('success', $r);
24 | $this->assertArrayHasKey('info', $r);
25 | $this->assertArrayHasKey('code', $r);
26 | }
27 |
28 | public function testSetAndGetResult()
29 | {
30 | $this->agent->result('success', true);
31 | $this->agent->result('info', 'info');
32 | $this->agent->result('code', 'code');
33 |
34 | $r = $this->agent->result();
35 | $code = $this->agent->result('code');
36 | $null = $this->agent->result('undefined');
37 |
38 | $this->assertTrue($r['success']);
39 | $this->assertEquals('info', $r['info']);
40 | $this->assertEquals('code', $r['code']);
41 | $this->assertEquals('code', $code);
42 | $this->assertNull($null);
43 | }
44 |
45 | public function testGetConfig()
46 | {
47 | //get config value
48 | $value = $this->agent->__get('key');
49 | $value2 = $this->agent->key2;
50 | $this->assertEquals('value', $value);
51 | $this->assertEquals('value2', $value2);
52 |
53 | //get not define
54 | $value = $this->agent->notdefine;
55 | $this->assertNull($value);
56 | $this->assertFalse(isset($this->agent->notdefile));
57 | $this->assertTrue(empty($this->agent->notdefile));
58 | }
59 |
60 | public function testSendTemplateSms()
61 | {
62 | $this->agent->sendSms('18280111111', null, 'template_id', []);
63 | $r = $this->agent->result();
64 | $this->assertTrue($r['success']);
65 | $this->assertEquals('send template sms success', $r['info']);
66 | }
67 |
68 | public function testSendContentSms()
69 | {
70 | $this->agent->sendSms('18280111111', 'content', 0, []);
71 | $r = $this->agent->result();
72 | $this->assertTrue($r['success']);
73 | $this->assertEquals('send content sms success', $r['info']);
74 | }
75 |
76 | public function testSendVoice()
77 | {
78 | $this->agent->sendVoice('18280111111', null, 0, [], '1111');
79 | $r = $this->agent->result();
80 | $this->assertTrue($r['success']);
81 | }
82 |
83 | public function testParasitic()
84 | {
85 | $parasiticAgent = new ParasiticAgent([], [
86 | 'sendContentSms' => function ($agent, $to, $content) {
87 | $agent->result(Agent::INFO, $content);
88 | $agent->result(Agent::CODE, $to);
89 | $agent->result(Agent::SUCCESS, true);
90 | },
91 | 'sendVoiceCode' => function ($agent, $to, $code) {
92 | $agent->result(Agent::INFO, 'parasitic_voice_verify');
93 | $agent->result(Agent::CODE, $code);
94 | },
95 | ]);
96 | $parasiticAgent->sendSms('18280111111', 'parasitic_sms_content');
97 | $this->assertEquals('parasitic_sms_content', $parasiticAgent->result('info'));
98 | $this->assertEquals('18280111111', $parasiticAgent->result('code'));
99 |
100 | $parasiticAgent->sendVoice('18280111111', null, null, [], '2222');
101 | $this->assertEquals('parasitic_voice_verify', $parasiticAgent->result('info'));
102 | $this->assertEquals('2222', $parasiticAgent->result('code'));
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/tests/ConfigTest.php:
--------------------------------------------------------------------------------
1 | assertCount(0, Sms::scheme());
11 | Sms::cleanConfig();
12 | $this->assertCount(0, Sms::config());
13 | }
14 |
15 | public function testAddEnableAgent()
16 | {
17 | Sms::scheme(['Log']);
18 | $this->assertCount(1, Sms::scheme());
19 |
20 | Sms::scheme('Log', '80 backup');
21 | $this->assertCount(1, Sms::scheme());
22 | $this->assertEquals('80 backup', Sms::scheme('Log'));
23 |
24 | Sms::scheme('Luosimao', 'backup');
25 | $this->assertCount(2, Sms::scheme());
26 |
27 | Sms::scheme([
28 | 'Luosimao' => '0 backup',
29 | 'YunPian' => '0',
30 | ]);
31 | $this->assertCount(3, Sms::scheme());
32 | $this->assertEquals('0', Sms::scheme('YunPian'));
33 | }
34 |
35 | public function testAddAgentConfig()
36 | {
37 | Sms::config('Log', []);
38 | $this->assertCount(1, Sms::config());
39 | $this->assertCount(0, Sms::config('Log'));
40 |
41 | Sms::config('Luosimao', [
42 | 'apikey' => '123',
43 | ]);
44 | $this->assertCount(2, Sms::config());
45 | $this->assertArrayHasKey('apikey', Sms::config('Luosimao'));
46 |
47 | Sms::config([
48 | 'Luosimao' => [
49 | 'apikey' => '123',
50 | ],
51 | 'YunPian' => [
52 | 'apikey' => '123',
53 | ],
54 | ]);
55 | $this->assertCount(3, Sms::config());
56 | }
57 |
58 | public function testUpdateAgentConfig()
59 | {
60 | $agent = Sms::getAgent('Luosimao');
61 | $this->assertEquals('123', $agent->apikey);
62 |
63 | Sms::config('Luosimao', [
64 | 'apikey' => '12345',
65 | 'data' => 'hello world',
66 | ]);
67 |
68 | $this->assertEquals('12345', $agent->apikey);
69 | $this->assertEquals('hello world', $agent->data);
70 |
71 | Sms::cleanConfig();
72 | $this->assertEquals(null, $agent->apikey);
73 | $this->assertEquals(null, $agent->data);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tests/ProtectedTest.php:
--------------------------------------------------------------------------------
1 | getMethod($name);
12 | $method->setAccessible(true);
13 |
14 | return $method;
15 | }
16 |
17 | public function testConfiguration()
18 | {
19 | $method = self::getPrivateMethod('configure');
20 | $obj = new Sms(false);
21 | $method->invokeArgs($obj, []);
22 | $config = include __DIR__ . '/../src/config/phpsms.php';
23 | $this->assertCount(count($config['scheme']), Sms::scheme());
24 | $this->assertCount(count($config['scheme']), Sms::config());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/SerializeTest.php:
--------------------------------------------------------------------------------
1 | function ($agent) {
17 | $agent->result(Agent::SUCCESS, true);
18 | $agent->result(Agent::INFO, 'some_info');
19 | },
20 | ]);
21 | Sms::beforeSend(function () {
22 | print_r('[_before_send_]');
23 | }, true);
24 | Sms::afterSend(function () {
25 | print_r('[_after_send_]');
26 | }, true);
27 | self::$sms = Sms::make()->to('18280354...')->content('content...');
28 | }
29 |
30 | public function testSerialize()
31 | {
32 | Sms::afterAgentSend(function ($task, $data) {
33 | $this->assertEquals('TestAgent', $data['driver']);
34 | $this->assertEquals('some_info', $data['result']['info']);
35 | });
36 |
37 | $serialized = serialize(self::$sms);
38 |
39 | Sms::cleanScheme();
40 | $this->assertEmpty(Sms::scheme());
41 |
42 | $sms = unserialize($serialized);
43 |
44 | $this->assertArrayHasKey('TestAgent', Sms::scheme());
45 | $this->expectOutputString('[_before_send_][_after_send_]');
46 | $sms->send();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/SmsTest.php:
--------------------------------------------------------------------------------
1 | '10',
14 | 'Luosimao' => '0',
15 | ]);
16 | self::$sms = Sms::make();
17 | }
18 |
19 | public function testMakeSms()
20 | {
21 | $this->assertInstanceOf('Toplan\PhpSms\Sms', self::$sms);
22 | }
23 |
24 | public function testHasAgent()
25 | {
26 | $this->assertFalse(Sms::hasAgent('Log'));
27 | $this->assertFalse(Sms::hasAgent('SomeAgent'));
28 |
29 | Sms::getAgent('Log');
30 | $this->assertTrue(Sms::hasAgent('Log'));
31 | }
32 |
33 | public function testGetAgent()
34 | {
35 | $agent = Sms::getAgent('Log');
36 | $this->assertInstanceOf('Toplan\PhpSms\LogAgent', $agent);
37 | $luosimao = Sms::getAgent('Luosimao');
38 | $this->assertInstanceOf('Toplan\PhpSms\LuosimaoAgent', $luosimao);
39 | }
40 |
41 | public function testGetTask()
42 | {
43 | $task = Sms::getTask();
44 | $this->assertInstanceOf('Toplan\TaskBalance\Task', $task);
45 | }
46 |
47 | public function testGetSmsData()
48 | {
49 | $data = self::$sms->all();
50 | $this->assertArrayHasKey('to', $data);
51 | $this->assertArrayHasKey('templates', $data);
52 | $this->assertArrayHasKey('data', $data);
53 | $this->assertArrayHasKey('content', $data);
54 | $this->assertArrayHasKey('code', $data);
55 | $this->assertArrayHasKey('files', $data);
56 | $this->assertArrayHasKey('params', $data);
57 | self::$sms->to('...');
58 | $this->assertEquals('...', self::$sms->all('to'));
59 | }
60 |
61 | public function testSetTo()
62 | {
63 | self::$sms->to('18280345...');
64 | $this->assertEquals('18280345...', self::$sms->all('to'));
65 | }
66 |
67 | public function testSetTemplate()
68 | {
69 | self::$sms->template('Luosimao', '123');
70 | $smsData = self::$sms->all();
71 | $this->assertEquals([
72 | 'Luosimao' => '123',
73 | ], $smsData['templates']);
74 | self::$sms->template([
75 | 'Luosimao' => '1234',
76 | 'YunTongXun' => '6789',
77 | ]);
78 | $smsData = self::$sms->all();
79 | $this->assertEquals([
80 | 'Luosimao' => '1234',
81 | 'YunTongXun' => '6789',
82 | ], $smsData['templates']);
83 | }
84 |
85 | public function testSetData()
86 | {
87 | self::$sms->data([
88 | 'code' => '1',
89 | 'msg' => 'msg',
90 | ]);
91 | $smsData = self::$sms->all();
92 | $this->assertEquals([
93 | 'code' => '1',
94 | 'msg' => 'msg',
95 | ], $smsData['data']);
96 | }
97 |
98 | public function testSetContent()
99 | {
100 | self::$sms->content('this is content');
101 | $smsData = self::$sms->all();
102 | $this->assertEquals('this is content', $smsData['content']);
103 | }
104 |
105 | public function testSendSms()
106 | {
107 | $result = self::$sms->send();
108 | $this->assertArrayHasKey('success', $result);
109 | $this->assertArrayHasKey('time', $result);
110 | $this->assertArrayHasKey('logs', $result);
111 | }
112 |
113 | public function testBeforeSend()
114 | {
115 | Sms::beforeSend(function () {
116 | print_r('before_');
117 | });
118 | $this->expectOutputString('before_');
119 | self::$sms->send();
120 | }
121 |
122 | public function testAfterSend()
123 | {
124 | self::$sms->afterSend(function () {
125 | print_r('after');
126 | });
127 | $this->expectOutputString('before_after');
128 | self::$sms->send();
129 | }
130 |
131 | public function testSetAgent()
132 | {
133 | $result = self::$sms->agent('Log')->send();
134 | $this->assertTrue($result['success']);
135 | $this->assertCount(1, $result['logs']);
136 | $this->assertEquals('Log', $result['logs'][0]['driver']);
137 | }
138 |
139 | public function testVoice()
140 | {
141 | $sms = Sms::voice('code');
142 | $data = $sms->all();
143 | $this->assertEquals('code', $data['code']);
144 | }
145 |
146 | public function testUseQueue()
147 | {
148 | $status = Sms::queue();
149 | $this->assertFalse($status);
150 |
151 | //define how to use queue
152 | //way 1
153 | Sms::queue(function ($sms, $data) {
154 | return 'in_queue_1';
155 | });
156 | $this->assertTrue(Sms::queue());
157 |
158 | //define how to use queue
159 | //way 2
160 | Sms::queue(false, function ($sms, $data) {
161 | return 'in_queue_2';
162 | });
163 | $this->assertFalse(Sms::queue());
164 |
165 | //open queue
166 | Sms::queue(true);
167 | $this->assertTrue(Sms::queue());
168 |
169 | //push sms to queue
170 | $result = self::$sms->send();
171 | $this->assertEquals('in_queue_2', $result);
172 |
173 | //force send
174 | $result = self::$sms->send(true);
175 | $this->assertTrue($result['success']);
176 | $this->assertCount(1, $result['logs']);
177 | $this->assertEquals('Log', $result['logs'][0]['driver']);
178 | }
179 | }
180 |
--------------------------------------------------------------------------------