SmsService::ucwordsHyphen()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
declare (strict_types=1);
4
5
namespace dtapps\qiniu\sms;
6
7
use think\exception\HttpException;
8
9
/**
10
 * Class SmsService
11
 * @package dtapps\qiniu\sms
12
 */
13
class SmsService extends Service
14
{
15
    private $accessKey, $secretKey, $body;
16
    private $method = "POST";
17
    private $headers = [
18
        "content-type" => "application/json"
19
    ];
20
    private $baseURL = Config::SMS_HOST . "/" . Config::SMS_VERSION . "/message";
21
22
    /**
23
     * @param string $value
24
     * @return $this
25
     */
26
    public function accessKey(string $value): self
27
    {
28
        $this->accessKey = $value;
29
        return $this;
30
    }
31
32
    /**
33
     * @param string $value
34
     * @return $this
35
     */
36
    public function secretKey(string $value): self
37
    {
38
        $this->secretKey = $value;
39
        return $this;
40
    }
41
42
    /**
43
     * 请求参数
44
     * @param array $params
45
     * @return $this
46
     */
47
    public function setParam(array $params): self
48
    {
49
        $this->body = json_encode($params);
50
        return $this;
51
    }
52
53
    /**
54
     * 请求接口
55
     * @param string $type
56
     * @return $this
57
     */
58
    private function setUrl(string $type): self
0 ignored issues
show
Unused Code introduced by
The method setUrl() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
59
    {
60
        $this->baseURL .= $type;
61
        return $this;
62
    }
63
64
    /**
65
     * 请求类型
66
     * @param string $type
67
     * @return $this
68
     */
69
    private function setMethod(string $type): self
0 ignored issues
show
Unused Code introduced by
The method setMethod() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
70
    {
71
        $this->method = $type;
72
        return $this;
73
    }
74
75
    /**
76
     * @param $name
77
     * @param $value
78
     * @return $this
79
     */
80
    private function withHeader($name, $value): self
0 ignored issues
show
Unused Code introduced by
The method withHeader() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
81
    {
82
        $this->headers = array_merge($this->headers, [$name => $value]);
83
        return $this;
84
    }
85
86
    /**
87
     * 返回数组数据
88
     * @return array
89
     */
90
    public function toArray(): array
91
    {
92
        //首先检测是否支持curl
93
        if (!extension_loaded("curl")) {
94
            throw new HttpException(404, '请开启curl模块!');
95
        }
96
        // 签名
97
        $this->headers["Authorization"] = $this->authorizationV2($this->baseURL, $this->method, $this->body, $this->headers["content-type"]);
98
        $this->headers['Content-Type'] = $this->headers["content-type"];
99
        // 准备请求
100
        $request = new Request($this->method, $this->baseURL, $this->headers, $this->body);
101
        // 发送请求
102
        $ret = $this->sendRequest($request);
103
        if (!$ret->ok()) {
104
            return array(null, new Error($this->baseURL, $ret));
105
        }
106
        $r = ($ret->body === null) ? array() : $ret->json();
107
        return array($r, null);
108
    }
109
110
    /**
111
     * 网络请求
112
     * @param $request
113
     * @return Response
114
     */
115
    public function sendRequest($request): Response
116
    {
117
        $t1 = microtime(true);
118
        $ch = curl_init();
119
        $options = array(
120
            CURLOPT_USERAGENT => $this->userAgent(),
121
            CURLOPT_RETURNTRANSFER => true,
122
            CURLOPT_SSL_VERIFYPEER => false,
123
            CURLOPT_SSL_VERIFYHOST => false,
124
            CURLOPT_HEADER => true,
125
            CURLOPT_NOBODY => false,
126
            CURLOPT_CUSTOMREQUEST => $request->method,
127
            CURLOPT_URL => $request->url,
128
        );
129
        // Handle open_basedir & safe mode
130
        if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
131
            $options[CURLOPT_FOLLOWLOCATION] = true;
132
        }
133
        if (!empty($request->headers)) {
134
            $headers = array();
135
            foreach ($request->headers as $key => $val) {
136
                array_push($headers, "$key: $val");
137
            }
138
            $options[CURLOPT_HTTPHEADER] = $headers;
139
        }
140
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
141
        if (!empty($request->body)) {
142
            $options[CURLOPT_POSTFIELDS] = $request->body;
143
        }
144
        curl_setopt_array($ch, $options);
145
        $result = curl_exec($ch);
146
        $t2 = microtime(true);
147
        $duration = round($t2 - $t1, 3);
148
        $ret = curl_errno($ch);
149
        if ($ret !== 0) {
150
            $r = new Response(-1, $duration, array(), null, curl_error($ch));
151
            curl_close($ch);
152
            return $r;
153
        }
154
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
155
        $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
156
        $headers = $this->parseHeaders(substr($result, 0, $header_size));
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type true; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

156
        $headers = $this->parseHeaders(substr(/** @scrutinizer ignore-type */ $result, 0, $header_size));
Loading history...
157
        $body = substr($result, $header_size);
158
        curl_close($ch);
159
        return new Response($code, $duration, $headers, $body, null);
160
    }
161
162
    private function userAgent(): string
163
    {
164
        $sdkInfo = "QiniuPHP/" . Config::SDK_VER;
165
166
        $systemInfo = php_uname("s");
167
        $machineInfo = php_uname("m");
168
169
        $envInfo = "($systemInfo/$machineInfo)";
170
171
        $phpVer = phpversion();
172
173
        return "$sdkInfo $envInfo PHP/$phpVer";
174
    }
175
176
    /**
177
     * @param $raw
178
     * @return array
179
     */
180
    private function parseHeaders($raw): array
181
    {
182
        $headers = array();
183
        $headerLines = explode("\r\n", $raw);
184
        foreach ($headerLines as $line) {
185
            $headerLine = trim($line);
186
            $kv = explode(':', $headerLine);
187
            if (count($kv) > 1) {
188
                $kv[0] = $this->ucwordsHyphen($kv[0]);
189
                $headers[$kv[0]] = trim($kv[1]);
190
            }
191
        }
192
        return $headers;
193
    }
194
195
    /**
196
     * @param $str
197
     * @return array|string|string[]
198
     */
199
    private function ucwordsHyphen($str)
200
    {
201
        return str_replace('- ', '-', ucwords(str_replace('-', '- ', $str)));
202
    }
203
204
    /**
205
     * @param $url
206
     * @param $method
207
     * @param null $body
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $body is correct as it would always require null to be passed?
Loading history...
208
     * @param null $contentType
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $contentType is correct as it would always require null to be passed?
Loading history...
209
     * @return string
210
     */
211
    public function authorizationV2($url, $method, $body = null, $contentType = null): string
212
    {
213
        $urlItems = parse_url($url);
214
        $host = $urlItems['host'];
215
216
        if (isset($urlItems['port'])) {
217
            $port = $urlItems['port'];
218
        } else {
219
            $port = '';
220
        }
221
222
        $path = $urlItems['path'];
223
        if (isset($urlItems['query'])) {
224
            $query = $urlItems['query'];
225
        } else {
226
            $query = '';
227
        }
228
229
        //write request uri
230
        $toSignStr = $method . ' ' . $path;
231
        if (!empty($query)) {
232
            $toSignStr .= '?' . $query;
233
        }
234
235
        //write host and port
236
        $toSignStr .= "\nHost: " . $host;
237
        if (!empty($port)) {
238
            $toSignStr .= ":" . $port;
239
        }
240
241
        //write content type
242
        if (!empty($contentType)) {
243
            $toSignStr .= "\nContent-Type: " . $contentType;
244
        }
245
246
        $toSignStr .= "\n\n";
247
248
        //write body
249
        if (!empty($body)) {
250
            $toSignStr .= $body;
251
        }
252
253
        $sign = $this->sign($toSignStr);
254
        return 'Qiniu ' . $sign;
255
    }
256
257
    private function sign($data): string
258
    {
259
        $hmac = hash_hmac('sha1', $data, $this->secretKey, true);
260
        return $this->accessKey . ':' . $this->base64_urlSafeEncode($hmac);
261
    }
262
263
    private function base64_urlSafeEncode(string $data): string
264
    {
265
        $find = array('+', '/');
266
        $replace = array('-', '_');
267
        return str_replace($find, $replace, base64_encode($data));
268
    }
269
}