Passed
Push — v6 ( 32f4f3...27c58a )
by 光春
03:39
created

Ks3Client   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 322
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 198
c 1
b 0
f 0
dl 0
loc 322
rs 5.04
wmc 57

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __call() 0 19 3
F invoke() 0 210 49
A __construct() 0 12 2
A postObject() 0 28 3

How to fix   Complexity   

Complex Class

Complex classes like Ks3Client often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Ks3Client, and based on these observations, apply Extract Interface, too.

1
<?php
2
//使用时请在项目中引用该php文件
3
//设置默认时区
4
date_default_timezone_set('Asia/Shanghai');
5
6
//检测API路径
7
if (!defined("KS3_API_PATH")) {
8
	define("KS3_API_PATH", __DIR__);
9
}
10
//是否使用VHOST
11
if (!defined("KS3_API_VHOST")) {
12
	define("KS3_API_VHOST", FALSE);
13
}
14
//是否开启日志(写入日志文件)
15
if (!defined("KS3_API_LOG")) {
16
	define("KS3_API_LOG", FALSE);
17
}
18
//是否显示日志(直接输出日志)
19
if (!defined("KS3_API_DISPLAY_LOG")) {
20
	define("KS3_API_DISPLAY_LOG", FALSE);
21
}
22
//定义日志目录(默认是该项目log下)
23
if (!defined("KS3_API_LOG_PATH")) {
24
	define("KS3_API_LOG_PATH", "");
25
}
26
//是否使用HTTPS
27
if (!defined("KS3_API_USE_HTTPS")) {
28
	define("KS3_API_USE_HTTPS", FALSE);
29
}
30
//是否开启curl debug模式
31
if (!defined("KS3_API_DEBUG_MODE")) {
32
	define("KS3_API_DEBUG_MODE", FALSE);
33
}
34
define("KS3_API_Author", "[email protected]");
35
define("KS3_API_Version", "1.2");
36
37
require_once KS3_API_PATH . DIRECTORY_SEPARATOR . "config" . DIRECTORY_SEPARATOR . "Consts.php";
38
require_once KS3_API_PATH . DIRECTORY_SEPARATOR . "core" . DIRECTORY_SEPARATOR . "API.php";
39
require_once KS3_API_PATH . DIRECTORY_SEPARATOR . "core" . DIRECTORY_SEPARATOR . "Signers.php";
40
require_once KS3_API_PATH . DIRECTORY_SEPARATOR . "core" . DIRECTORY_SEPARATOR . "Ks3Request.class.php";
41
require_once KS3_API_PATH . DIRECTORY_SEPARATOR . "core" . DIRECTORY_SEPARATOR . "Handlers.php";
42
require_once KS3_API_PATH . DIRECTORY_SEPARATOR . "core" . DIRECTORY_SEPARATOR . "Builders.php";
43
require_once KS3_API_PATH . DIRECTORY_SEPARATOR . "core" . DIRECTORY_SEPARATOR . "Logger.php";
44
require_once KS3_API_PATH . DIRECTORY_SEPARATOR . "core" . DIRECTORY_SEPARATOR . "MessageHolder.php";
45
require_once KS3_API_PATH . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "RequestCore.class.php";
46
require_once KS3_API_PATH . DIRECTORY_SEPARATOR . "exceptions" . DIRECTORY_SEPARATOR . "Exceptions.php";
47
48
if (function_exists('get_loaded_extensions')) {
49
    //检测curl扩展
50
    $extensions = get_loaded_extensions();
51
    if ($extensions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $extensions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
52
        if (!in_array('curl', $extensions, true)) {
53
            throw new Ks3ClientException("please install curl extension");
54
        }
55
        if (!in_array('mbstring', $extensions, true)) {
56
            throw new Ks3ClientException("please install mbstring extension");
57
58
        }
59
    } else {
60
        throw new Ks3ClientException("please install extensions");
61
    }
62
} else {
63
    throw new Ks3ClientException();
64
}
65
66
class Ks3Client
67
{
68
    private $accessKey;
69
    private $secretKey;
70
    private $endpoint;
71
    private $log;
72
73
    public function __construct($accessKey = NULL, $secretKey = NULL, $endpoint = NULL)
74
    {
75
        $this->accessKey = $accessKey;
76
        $this->secretKey = $secretKey;
77
78
        if (empty($endpoint)) {
79
            throw new Ks3ClientException("must set endpoint, please see http://ks3.ksyun.com/doc/api/index.html Region part");
80
        }
81
        $this->endpoint = $endpoint;
82
83
        $this->signers = array();
0 ignored issues
show
Bug Best Practice introduced by
The property signers does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
84
        $this->log = new Logger();
85
    }
86
87
	/**
88
	 * 方法列表:(具体使用请参考readme.md)
89
	 * listBuckets,罗列bucket
90
	 * deleteBucket,删除bucket
91
	 * deleteBucketCORS,删除bucket跨域配置
92
	 * createBucket,新建bucket
93
	 * setBucketAcl,设置bucket访问权限
94
	 * setBucketCORS,设置bucket跨域配置
95
	 * setBucketLogging,设置bucket日志配置
96
	 * listObjects,罗列object
97
	 * getBucketAcl,获取bucket访问权限
98
	 * getBucketCORS,获取bucket跨域配置
99
	 * getBucketLocation,获取bucket地点配置
100
	 * getBucketLogging,获取bucket日志配置
101
	 * bucketExists,判断bucket是否存在
102
	 * listMutipartUploads,罗列当前bucket下尚未结束的分块上传
103
	 * putObjectByContent,上传文件
104
	 * putObjectByFile,上传文件
105
	 * setObjectAcl,设置object访问权限
106
	 * copyObject,复制object
107
	 * getObjectMeta,获取object元数据
108
	 * objectExists,判断object是否存在
109
	 * deleteObject,删除object
110
	 * deleteObjects,删除多个object
111
	 * getObject,下载object
112
	 * getObjectAcl,获取object访问权限
113
	 * initMultipartUpload,初始化分块上传
114
	 * uploadPart,上传块
115
	 * abortMultipartUpload,终止分块上传
116
	 * listParts,罗列已经上传的块
117
	 * completeMultipartUpload,完成分块上传
118
	 * generatePresignedUrl,生成文件外链
119
	 * putAdp,添加异步数据处理任务
120
	 * getAdp,查询异步数据处理任务
121
	 * @param $method
122
	 * @param array $args
123
	 * @return ResponseCore|string|string[]|null
124
	 * @throws Exception
125
	 */
126
    public function __call($method, $args = array())
127
    {
128
        $holder = new MessageHolder();
129
130
        $holder->msg = "------------------Logging Start-------------------------\r\n";
131
        $holder->msg .= "method->" . $method . " args->" . serialize($args) . "\r\n";
132
        $ex = NULL;
133
        try {
134
            $result = $this->invoke($method, $args, $holder);
135
        } catch (Exception $e) {
136
            $holder->msg .= $e . "\r\n";
137
            $ex = $e;
138
        }
139
        $holder->msg .= "------------------Logging End-------------------------\r\n";
140
        $this->log->info($holder->msg);
141
        if ($ex != NULL) {
142
			throw $ex;
143
		}
144
        return $result;
145
    }
146
147
    private function invoke($method, $args = array(), $holder, $location = NULL)
148
    {
149
        $api = API::$API[$method];
150
        if (!$api) {
151
            throw new Ks3ClientException($method . " Not Found API");
152
        }
153
        if (count($args) !== 0) {
154
            if (count($args) > 1 || !is_array($args[0])) {
155
                throw new Ks3ClientException("this method only needs one array argument");
156
            }
157
            $args = $args[0];
158
        }
159
        if (isset($api["redirect"])) {
160
            $api = API::$API[$api["redirect"]];
161
        }
162
        $request = new Ks3Request();
163
        if (empty($args["Bucket"])) {
164
            if ($api["needBucket"]) {
165
                throw new Ks3ClientException($method . " this api need bucket");
166
            }
167
        } else {
168
            $request->bucket = $args["Bucket"];
0 ignored issues
show
Bug Best Practice introduced by
The property $bucket is declared private in Ks3Request. Since you implement __set, consider adding a @property or @property-write.
Loading history...
169
        }
170
		$position = $api["objectPostion"] ?? "Key";
171
		if (empty($args[$position])) {
172
            if ($api["needObject"]) {
173
                throw new Ks3ClientException($method . " this api need " . $position);
174
            }
175
        } else {
176
            $key = $args[$position];
177
            $preEncoding = mb_detect_encoding($key, array("ASCII", "UTF-8", "GB2312", "GBK", "BIG5"));
178
            $holder->msg .= "key encoding " . $preEncoding . "\r\n";
179
            if (strtolower($preEncoding) !== "utf-8") {
180
                $key = iconv($preEncoding, "UTF-8", $key);
181
            }
182
            $request->key = $key;
0 ignored issues
show
Bug Best Practice introduced by
The property $key is declared private in Ks3Request. Since you implement __set, consider adding a @property or @property-write.
Loading history...
183
        }
184
        $method = $api["method"];
185
        if ($method === "Method") {
186
            if (empty($args["Method"])) {
187
                $request->method = "GET";
0 ignored issues
show
Bug Best Practice introduced by
The property $method is declared private in Ks3Request. Since you implement __set, consider adding a @property or @property-write.
Loading history...
188
            } else {
189
                $request->method = $args["Method"];
190
            }
191
        } else {
192
            $request->method = $api["method"];
193
        }
194
        if (KS3_API_USE_HTTPS) {
195
			$request->scheme = "https://";
0 ignored issues
show
Bug Best Practice introduced by
The property $scheme is declared private in Ks3Request. Since you implement __set, consider adding a @property or @property-write.
Loading history...
196
		}
197
        else {
198
			$request->scheme = "http://";
199
		}
200
        $request->endpoint = $this->endpoint;
0 ignored issues
show
Bug Best Practice introduced by
The property $endpoint is declared private in Ks3Request. Since you implement __set, consider adding a @property or @property-write.
Loading history...
201
        //add subresource
202
        if (!empty($api["subResource"])) {
203
            $request->subResource = $api["subResource"];
0 ignored issues
show
Bug Best Practice introduced by
The property $subResource is declared private in Ks3Request. Since you implement __set, consider adding a @property or @property-write.
Loading history...
204
        }
205
        //add query params
206
        if (isset($api["queryParams"])) {
207
            foreach ($api["queryParams"] as $key => $value) {
208
                $required = FALSE;
209
                if (strpos($value, "!") === 0) {
210
                    $required = TRUE;
211
                    $value = substr($value, 1);
212
                }
213
                $index = explode("->", $value);
214
                $curIndexArg = $args;
215
                $add = TRUE;
216
                $curkey = "";
217
                foreach ($index as $key1 => $value1) {
218
                    if (!isset($curIndexArg[$value1]) && $value1 !== "*") {
219
                        $add = FALSE;
220
                    } else {
221
                        $curkey = $value1;
222
                        //星号表示所有,按照暂时的业务,默认星号后面就没了
223
                        if ($curkey === "*") {
224
                            foreach ($curIndexArg as $queryK => $queryV) {
225
                                if (!is_array($queryV)) {
226
                                    $request->addQueryParams($queryK, $queryV);
227
                                }
228
                            }
229
                            $add = FALSE;
230
                            $required = FALSE;
231
                            break;
232
                        }
233
234
						$curIndexArg = $curIndexArg[$value1];
235
					}
236
                }
237
                if (!empty($curIndexArg) && $add) {
238
                    $request->addQueryParams($curkey, $curIndexArg);
239
                    continue;
240
                }
241
                if ($required) {
242
					throw new Ks3ClientException($method . " param " . $value . " is required");
243
				}
244
            }
245
        }
246
        if (isset($api["body"])) {
247
            if (isset($api["body"]["builder"])) {
248
                $builderName = $api["body"]["builder"];
249
                $builder = new $builderName();
250
                $request->body = $builder->build($args);
0 ignored issues
show
Bug Best Practice introduced by
The property $body is declared private in Ks3Request. Since you implement __set, consider adding a @property or @property-write.
Loading history...
251
            } else if (isset($api["body"]["position"])) {
252
                $position = $api["body"]["position"];
253
                $index = explode("->", $position);
254
                $curIndexArg = $args;
255
                $add = TRUE;
256
                $curkey = "";
0 ignored issues
show
Unused Code introduced by
The assignment to $curkey is dead and can be removed.
Loading history...
257
                foreach ($index as $key1 => $value1) {
258
                    if (!isset($curIndexArg[$value1])) {
259
                        $add = FALSE;
260
                    } else {
261
                        $curIndexArg = $curIndexArg[$value1];
262
                        $curkey = $value1;
263
                    }
264
                }
265
                if (!empty($curIndexArg) && $add) {
266
                    $request->body = $curIndexArg;
267
                }
268
            }
269
        }
270
271
        //add ext headers
272
        //TODO
273
        //sign request
274
        $signer = NULL;
275
        if (isset($api["signer"])) {
276
            $signers = explode("->", $api["signer"]);
277
            foreach ($signers as $key => $value) {
278
                $signer = new $value();
279
                $log = $signer->sign($request, array("accessKey" => $this->accessKey, "secretKey" => $this->secretKey, "args" => $args));
280
                if (!empty($log)) {
281
                    $holder->msg .= $log . "\r\n";
282
                }
283
            }
284
        }
285
286
        if ($signer === NULL || !($signer instanceof QueryAuthSigner)) {
287
            $url = $request->toUrl($this->endpoint);
288
            if ($location != NULL) {
289
				$url = $location;
290
			}
291
            $httpRequest = new RequestCore($url);
292
            if (KS3_API_DEBUG_MODE === TRUE) {
0 ignored issues
show
introduced by
The condition KS3_API_DEBUG_MODE === TRUE is always false.
Loading history...
293
				$httpRequest->debug_mode = TRUE;
294
			}
295
            $httpRequest->set_method($request->method);
296
            foreach ($request->headers as $key => $value) {
0 ignored issues
show
Bug Best Practice introduced by
The property $headers is declared private in Ks3Request. Since you implement __get, consider adding a @property or @property-read.
Loading history...
297
                $httpRequest->add_header($key, $value);
298
            }
299
            $httpRequest->request_body = $request->body;
300
301
            if (isset($args["writeCallBack"])) {
302
                $httpRequest->register_streaming_write_callback($args["writeCallBack"]);
303
            }
304
            if (isset($args["readCallBack"])) {
305
                $httpRequest->register_streaming_read_callback($args["readCallBack"]);
306
            }
307
308
            $read_stream = $request->read_stream;
0 ignored issues
show
Bug Best Practice introduced by
The property $read_stream is declared private in Ks3Request. Since you implement __get, consider adding a @property or @property-read.
Loading history...
309
            $read_length = $request->getHeader(Headers::$ContentLength);
310
            $seek_position = $request->seek_position;
0 ignored issues
show
Bug Best Practice introduced by
The property $seek_position is declared private in Ks3Request. Since you implement __get, consider adding a @property or @property-read.
Loading history...
311
            if (isset($read_stream)) {
312
                $httpRequest->set_read_stream($read_stream, $read_length);
313
                $httpRequest->set_seek_position($seek_position);
314
                $httpRequest->remove_header(Headers::$ContentLength);
315
            }
316
            $write_stream = $request->write_stream;
0 ignored issues
show
Bug Best Practice introduced by
The property $write_stream is declared private in Ks3Request. Since you implement __get, consider adding a @property or @property-read.
Loading history...
317
            if (isset($write_stream)) {
318
                $httpRequest->set_write_stream($write_stream);
319
            }
320
321
            $holder->msg .= "request url->" . serialize($httpRequest->request_url) . "\r\n";
322
            $holder->msg .= "request headers->" . serialize($httpRequest->request_headers) . "\r\n";
323
            $holder->msg .= "request body->" . $httpRequest->request_body . "\r\n";
324
            $holder->msg .= "request read stream length->" . $read_length . "\r\n";
325
            $holder->msg .= "request read stream seek position->" . $seek_position . "\r\n";
326
            $httpRequest->send_request();
327
            //print_r($httpRequest);
328
            $body = $httpRequest->get_response_body();
329
            $data = new ResponseCore ($httpRequest->get_response_header(), Utils::replaceNS2($body), $httpRequest->get_response_code());
0 ignored issues
show
Bug introduced by
It seems like $httpRequest->get_response_header() can also be of type string; however, parameter $header of ResponseCore::__construct() does only seem to accept array, 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

329
            $data = new ResponseCore (/** @scrutinizer ignore-type */ $httpRequest->get_response_header(), Utils::replaceNS2($body), $httpRequest->get_response_code());
Loading history...
Bug introduced by
$httpRequest->get_response_code() of type string is incompatible with the type integer expected by parameter $status of ResponseCore::__construct(). ( Ignorable by Annotation )

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

329
            $data = new ResponseCore ($httpRequest->get_response_header(), Utils::replaceNS2($body), /** @scrutinizer ignore-type */ $httpRequest->get_response_code());
Loading history...
330
331
            if ($data->status == 307) {
332
                $respHeaders = $httpRequest->get_response_header();
333
                $location = $respHeaders["location"];
334
                if (strpos($location, "http") === 0) {
335
                    $holder->msg .= "response code->" . $httpRequest->get_response_code() . "\r\n";
336
                    $holder->msg .= "response headers->" . serialize($httpRequest->get_response_header()) . "\r\n";
337
                    $holder->msg .= "response body->" . $body . "\r\n";
338
                    $holder->msg .= "retry request to " . $location . "\r\n";
339
                    //array($args)详见invoke开头
340
                    return $this->invoke($method, array($args), $holder, $location);
341
                }
342
            }
343
            $holder->msg .= "response code->" . $httpRequest->get_response_code() . "\r\n";
344
            $holder->msg .= "response headers->" . serialize($httpRequest->get_response_header()) . "\r\n";
345
            $holder->msg .= "response body->" . $body . "\r\n";
346
            $handlers = explode("->", $api["handler"]);
347
            foreach ($handlers as $key => $value) {
348
                $handler = new $value();
349
                $data = $handler->handle($data);
350
            }
351
            return $data;
352
        }
353
354
		$url = $request->toUrl($this->endpoint);
355
		$holder->msg .= $url . "\r\n";
356
		return $url;
357
	}
358
359
    //用于生产表单上传时的签名信息
360
    public function postObject($bucket, $postFormData = array(), $unknowValueFormFiled = array(), $filename = NULL, $expire = 18000): array
361
	{
362
        $policy = array();
363
364
        $expireTime = Utils::iso8601(time() + $expire);
365
        $policy["expiration"] = $expireTime;
366
        $postFormData["bucket"] = $bucket;
367
        $conditions = array();
368
        foreach ($postFormData as $key => $value) {
369
            $condition = array();
370
            $condition[$key] = str_replace("\${filename}", $filename, $value);
371
            $conditions[] = $condition;
372
        }
373
        foreach ($unknowValueFormFiled as $key => $value) {
374
            $condition = array();
375
            $condition[] = "starts-with";
376
            $condition[] = "\$" . $value;
377
            $condition[] = "";
378
            $conditions[] = $condition;
379
        }
380
        $policy["conditions"] = $conditions;
381
        $json = json_encode($policy);
382
        $signature = base64_encode(hash_hmac('sha1', base64_encode($json), $this->secretKey, true));
383
        $result = array();
384
        $result["Policy"] = base64_encode($json);
385
        $result["Signature"] = $signature;
386
        $result["KSSAccessKeyId"] = $this->accessKey;
387
        return $result;
388
    }
389
}
390
391
392