Issues (6)

src/SmsService.php (2 issues)

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
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
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
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));
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
}