Passed
Push — master ( ef93dc...4f7931 )
by
unknown
13:58
created

Sms::generateCode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
ccs 9
cts 9
cp 1
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
/*
4
 * This file is part of ibrand/laravel-sms.
5
 *
6
 * (c) iBrand <https://www.ibrand.cc>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace iBrand\Sms;
13
14
use Carbon\Carbon;
15
use iBrand\Sms\Jobs\DbLogger;
16
use iBrand\Sms\Messages\CodeMessage;
17
use iBrand\Sms\Storage\CacheStorage;
18
use iBrand\Sms\Storage\StorageInterface;
19
use Overtrue\EasySms\Contracts\MessageInterface;
20
use Overtrue\EasySms\EasySms;
21
use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
22
23
/**
24
 * Class Sms.
25
 */
26
class Sms
27
{
28
	/**
29
	 * @var EasySms
30
	 */
31
	protected $easySms;
32
	/**
33
	 * @var
34
	 */
35
	protected $storage;
36
37
	/**
38
	 * @var
39
	 */
40
	protected $key;
41
42
	/**
43
	 * @param mixed $key
44
	 */
45 30
	public function setKey($key)
46
	{
47 30
		$key       = 'ibrand.sms.' . $key;
48 30
		$this->key = md5($key);
49 30
	}
50
51
	/**
52
	 * @return mixed
53
	 */
54 4
	public function getKey()
55
	{
56 4
		return $this->key;
57
	}
58
59
	/**
60
	 * Sms constructor.
61
	 *
62
	 * @param EasySms $easySms
63
	 */
64 39
	public function __construct(EasySms $easySms, StorageInterface $storage)
65
	{
66 39
		$this->easySms = $easySms;
67 39
		$this->storage = $storage;
68 39
	}
69
70
	/**
71
	 * @param StorageInterface $storage
72
	 */
73 4
	public function setStorage(StorageInterface $storage)
74
	{
75 4
		$this->storage = $storage;
76 4
	}
77
78
	/**
79
	 * @param       $to
80
	 * @param null  $data
81
	 * @param array $gateways
82
	 *
83
	 * @return bool
84
	 */
85 26
	public function send($to, $data = [], array $gateways = [])
86
	{
87
		try {
88 26
			$flag = false;
89
90 26
			$this->setKey($to);
91
92
			//1. get code from storage.
93 26
			$code = $this->getCodeFromStorage();
94
95 26
			if ($this->needNewCode($code)) {
96 26
				$code = $this->getNewCode($to);
97
			}
98
99 26
			$validMinutes = (int) config('ibrand.sms.code.validMinutes', 5);
100
101 26
			if (!($data instanceof MessageInterface)) {
102 26
				$message = new CodeMessage($code->code, $validMinutes, $data);
0 ignored issues
show
Bug introduced by
It seems like $data defined by parameter $data on line 85 can also be of type null; however, iBrand\Sms\Messages\CodeMessage::__construct() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
103
			} else {
104
				$message = $data;
105
			}
106
107 26
			$results = $this->easySms->send($to, $message, $gateways);
108
109 22
			foreach ($results as $key => $value) {
110 22
				if ('success' == $value['status']) {
111 22
					$code->put('sent', true);
112 22
					$code->put('sentAt', Carbon::now());
113 22
					$this->storage->set($this->key, $code);
114 22
					$flag = true;
115
				}
116
			}
117 8
		} catch (NoGatewayAvailableException $noGatewayAvailableException) {
118 8
			$results = $noGatewayAvailableException->results;
119 8
			$flag    = false;
120
		} catch (\Exception $exception) {
121
			$results = $exception->getMessage();
122
			$flag    = false;
123
		}
124
125 26
		DbLogger::dispatch($code, json_encode($results), $flag);
0 ignored issues
show
Bug introduced by
The variable $code does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
126
127 26
		return $flag;
128
	}
129
130
	/**
131
	 * check china mobile.
132
	 *
133
	 * @param $to
134
	 *
135
	 * @return false|int
136
	 */
137 2
	public function verifyMobile($to)
138
	{
139 2
		return preg_match('/^(?:\+?86)?1(?:3\d{3}|5[^4\D]\d{2}|8\d{3}|7(?:[0-35-9]\d{2}|4(?:0\d|1[0-2]|9\d))|9[0-35-9]\d{2}|6[2567]\d{2}|4(?:(?:10|4[01])\d{3}|[68]\d{4}|[579]\d{2}))\d{6}$/', $to);
140
	}
141
142
	/**
143
	 * @param $to
144
	 *
145
	 * @return mixed
146
	 */
147 26
	public function getCodeFromStorage()
148
	{
149 26
		return $this->storage->get($this->key, '');
150
	}
151
152
	/**
153
	 * @param $code
154
	 *
155
	 * @return bool
156
	 */
157 26
	protected function needNewCode($code)
158
	{
159 26
		if (empty($code)) {
160 26
			return true;
161
		}
162
163 8
		return $this->checkAttempts($code);
164
	}
165
166
	/**
167
	 * Check attempt times.
168
	 *
169
	 * @param $code
170
	 *
171
	 * @return bool
172
	 */
173 8
	private function checkAttempts($code)
174
	{
175 8
		$maxAttempts = config('ibrand.sms.code.maxAttempts');
176
177 8
		if ($code->expireAt > Carbon::now() && $code->attempts < $maxAttempts) {
178 4
			return false;
179
		}
180
181 8
		return true;
182
	}
183
184
	/**
185
	 * @param $to
186
	 *
187
	 * @return Code
188
	 */
189 30
	public function getNewCode($to)
190
	{
191 30
		$code = $this->generateCode($to);
192
193 30
		$this->storage->set($this->key, $code);
194
195 30
		return $code;
196
	}
197
198
	/**
199
	 * @param $to
200
	 *
201
	 * @return bool
202
	 */
203 6
	public function canSend($to)
204
	{
205 6
		$this->setKey($to);
206
207 6
		$code = $this->storage->get($this->key, '');
208
209 6
		if (empty($code) || $code->sentAt < Carbon::now()->addMinutes(-1)) {
210 6
			return true;
211
		}
212
213 6
		return false;
214
	}
215
216
	/**
217
	 * @param $to
218
	 *
219
	 * @return Code
220
	 */
221 30
	public function generateCode($to)
222
	{
223 30
		$length       = (int) config('ibrand.sms.code.length', 5);
224 30
		$characters   = '0123456789';
225 30
		$charLength   = strlen($characters);
226 30
		$randomString = '';
227 30
		for ($i = 0; $i < $length; ++$i) {
228 30
			$randomString .= $characters[mt_rand(0, $charLength - 1)];
229
		}
230
231 30
		$validMinutes = (int) config('ibrand.sms.code.validMinutes', 5);
232
233 30
		return new Code($to, $randomString, false, 0, Carbon::now()->addMinutes($validMinutes));
234
	}
235
236
	/**
237
	 * @return CacheStorage|StorageInterface
238
	 */
239 5
	public function getStorage()
240
	{
241 5
		return $this->storage ? $this->storage : new CacheStorage();
242
	}
243
244
	/**
245
	 * @param $to
246
	 * @param $inputCode
247
	 *
248
	 * @return bool
249
	 */
250 4
	public function checkCode($to, $inputCode)
251
	{
252 4
		if (config('app.debug')) {
253
			return true;
254
		}
255
256 4
		$this->setKey($to);
257
258 4
		$code = $this->storage->get($this->key, '');
259
260 4
		if (empty($code)) {
261
			return false;
262
		}
263
264 4
		if ($code && $code->code == $inputCode) {
265 4
			$this->storage->forget($this->key);
266
267 4
			return true;
268
		}
269
270 4
		$code->put('attempts', $code->attempts + 1);
271
272 4
		$this->storage->set($this->key, $code);
273
274 4
		return false;
275
	}
276
}
277