Passed
Push — master ( 09d4ea...720e36 )
by frey
38s
created

Cosapi::normalizerPath()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 6
nop 2
dl 0
loc 14
ccs 10
cts 10
cp 1
crap 4
rs 9.2
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 6 and the first side effect is on line 5.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
namespace Freyo\Flysystem\QcloudCOSv3\Client;
4
5
date_default_timezone_set('PRC');
6
class Cosapi
7
{
8
    //计算sign签名的时间参数
9
    const EXPIRED_SECONDS = 180;
10
    //512K
11
    const SLICE_SIZE_512K = 524288;
12
    //1M
13
    const SLICE_SIZE_1M = 1048576;
14
    //2M
15
    const SLICE_SIZE_2M = 2097152;
16
    //3M
17
    const SLICE_SIZE_3M = 3145728;
18
    //20M 大于20M的文件需要进行分片传输
19
    const MAX_UNSLICE_FILE_SIZE = 20971520;
20
    //失败尝试次数
21
    const MAX_RETRY_TIMES = 3;
22
    //返回的错误码
23
    const COSAPI_PARAMS_ERROR = -1;
24
    const COSAPI_NETWORK_ERROR = -2;
25
    //HTTP请求超时时间
26
    private static $timeout = 60;
27
28
    /*
29
     * 设置HTTP请求超时时间
30
     * @param  int  $timeout  超时时长
31
     */
32
    public static function setTimeout($timeout = 60)
33
    {
34
        if (!is_int($timeout) || $timeout < 0) {
35
            return false;
36
        }
37
38
        self::$timeout = $timeout;
39
40
        return true;
41
    }
42
43
    /**
44
     * 上传文件,自动判断文件大小,如果小于20M则使用普通文件上传,大于20M则使用分片上传.
45
     *
46
     * @param string $bucketName bucket名称
47
     * @param string $srcPath    本地文件路径
48
     * @param string $dstPath    上传的文件路径
49
     * @param string $bizAttr    文件属性
50
     * @param string $sliceSize  分片大小(512k,1m,2m,3m),默认:1m
51
     * @param string $insertOnly 同名文件是否覆盖
52
     *
53
     * @return array
54
     */
55 2
    public static function upload($bucketName, $srcPath, $dstPath,
56
                $bizAttr = null, $sliceSize = null, $insertOnly = null)
57
    {
58 2 View Code Duplication
        if (!file_exists($srcPath)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
59
            return [
60
                    'code'    => self::COSAPI_PARAMS_ERROR,
61
                    'message' => 'file '.$srcPath.' not exists',
62
                    'data'    => [], ];
63
        }
64
65 2
        $dstPath = '/'.ltrim($dstPath, '/');
66
67
        //文件大于20M则使用分片传输
68 2
        if (filesize($srcPath) < self::MAX_UNSLICE_FILE_SIZE) {
69 2
            return self::uploadfile($bucketName, $srcPath, $dstPath, $bizAttr, $insertOnly);
70
        } else {
71
            $sliceSize = self::getSliceSize($sliceSize);
72
73
            return self::upload_slice($bucketName, $srcPath, $dstPath, $bizAttr, $sliceSize, $insertOnly);
74
        }
75
    }
76
77
    /**
78
     * 创建目录.
79
     *
80
     * @param string $bucketName bucket名称
81
     * @param string $path       目录路径
82
     * @param string $bizAttr    目录属性
83
     *
84
     * @return array
85
     */
86 1
    public static function createFolder($bucketName, $path, $bizAttr = null)
87
    {
88 1
        $path = self::normalizerPath($path, true);
89 1
        $path = self::cosUrlEncode($path);
90 1
        $expired = time() + self::EXPIRED_SECONDS;
91 1
        $url = self::generateResUrl($bucketName, $path);
92 1
        $sign = Auth::appSign($expired, $bucketName);
93
94
        $data = [
95 1
            'op'       => 'create',
96 1
            'biz_attr' => (isset($bizAttr) ? $bizAttr : ''),
97 1
        ];
98
99 1
        $data = json_encode($data);
100
101
        $req = [
102 1
            'url'     => $url,
103 1
            'method'  => 'post',
104 1
            'timeout' => self::$timeout,
105 1
            'data'    => $data,
106
            'header'  => [
107 1
                'Authorization:'.$sign,
108 1
                'Content-Type: application/json',
109 1
            ],
110 1
        ];
111
112 1
        return self::sendRequest($req);
113
    }
114
115
    /**
116
     * 目录列表.
117
     *
118
     * @param string $bucketName bucket名称
119
     * @param string $path       目录路径,sdk会补齐末尾的 '/'
120
     * @param int    $num        拉取的总数
121
     * @param string $pattern    eListBoth,ListDirOnly,eListFileOnly  默认both
122
     * @param int    $order      默认正序(=0), 填1为反序,
123
     * @param string $context    透传字段,用于翻页,前端不需理解,需要往前/往后翻页则透传回来
124
     *
125
     * @return array
126
     */
127 1 View Code Duplication
    public static function listFolder(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
128
                    $bucketName, $path, $num = 20,
129
                    $pattern = 'eListBoth', $order = 0,
130
                    $context = null)
131
    {
132 1
        $path = self::normalizerPath($path, true);
133
134 1
        return self::listBase($bucketName, $path, $num,
135 1
                $pattern, $order, $context);
136
    }
137
138
    /**
139
     * 目录列表(前缀搜索).
140
     *
141
     * @param string $bucketName bucket名称
142
     * @param string $prefix     列出含此前缀的所有文件
143
     * @param int    $num        拉取的总数
144
     * @param string $pattern    eListBoth(默认),ListDirOnly,eListFileOnly
145
     * @param int    $order      默认正序(=0), 填1为反序,
146
     * @param string $context    透传字段,用于翻页,前端不需理解,需要往前/往后翻页则透传回来
147
     *
148
     * @return array
149
     */
150 View Code Duplication
    public static function prefixSearch(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
                    $bucketName, $prefix, $num = 20,
152
                    $pattern = 'eListBoth', $order = 0,
153
                    $context = null)
154
    {
155
        $path = self::normalizerPath($prefix);
156
157
        return self::listBase($bucketName, $path, $num,
158
                $pattern, $order, $context);
159
    }
160
161
    /**
162
     * 目录更新.
163
     *
164
     * @param string $bucketName bucket名称
165
     * @param string $path       文件夹路径,SDK会补齐末尾的 '/'
166
     * @param string $bizAttr    目录属性
167
     *
168
     * @return array
169
     */
170
    public static function updateFolder($bucketName, $path, $bizAttr = null)
171
    {
172
        $path = self::normalizerPath($path, true);
173
174
        return self::updateBase($bucketName, $path, $bizAttr);
175
    }
176
177
    /**
178
     * 查询目录信息.
179
     *
180
     * @param string $bucketName bucket名称
181
     * @param string $path       目录路径
182
     *
183
     * @return array
184
     */
185
    public static function statFolder($bucketName, $path)
186
    {
187
        $path = self::normalizerPath($path, true);
188
189
        return self::statBase($bucketName, $path);
190
    }
191
192
    /**
193
     * 删除目录.
194
     *
195
     * @param string $bucketName bucket名称
196
     * @param string $path       目录路径
197
     *                           注意不能删除bucket下根目录/
198
     *
199
     * @return array
200
     */
201 1
    public static function delFolder($bucketName, $path)
202
    {
203 1
        $path = self::normalizerPath($path, true);
204
205 1
        return self::delBase($bucketName, $path);
206
    }
207
208
    /**
209
     * 更新文件.
210
     *
211
     * @param string $bucketName             bucket名称
212
     * @param string $path                   文件路径
213
     * @param null   $bizAttr
214
     * @param string $authority              :  eInvalid(继承Bucket的读写权限)/eWRPrivate(私有读写)/eWPrivateRPublic(公有读私有写)
215
     * @param array  $customer_headers_array 携带的用户自定义头域,包括
216
     *                                       'Cache-Control' => '*'
217
     *                                       'Content-Type' => '*'
218
     *                                       'Content-Disposition' => '*'
219
     *                                       'Content-Language' => '*'
220
     *                                       'x-cos-meta-自定义内容' => '*'
221
     *
222
     * @return array
223
     */
224 1
    public static function update($bucketName, $path,
225
                    $bizAttr = null, $authority = null, $customer_headers_array = null)
226
    {
227 1
        $path = self::normalizerPath($path);
228
229 1
        return self::updateBase($bucketName, $path, $bizAttr, $authority, $customer_headers_array);
230
    }
231
232
    /**
233
     * 移动(重命名)文件.
234
     *
235
     * @param string $bucketName  bucket名称
236
     * @param string $srcPath     源文件路径
237
     * @param string $dstPath     目的文件名(可以是单独文件名也可以是带目录的文件名)
238
     * @param int    $toOverWrite 是否覆盖(当目的文件名已经存在同名文件时是否覆盖)
239
     *
240
     * @return array
241
     */
242 1
    public static function move($bucketName, $srcPath, $dstPath, $toOverWrite = 0)
243
    {
244 1
        $srcPath = self::normalizerPath($srcPath);
245 1
        $dstPath = self::normalizerPath($dstPath);
246
247 1
        $srcPath = self::cosUrlEncode($srcPath);
248 1
        $url = self::generateResUrl($bucketName, $srcPath);
249 1
        $sign = Auth::appSign_once($srcPath, $bucketName);
250
251
        $data = [
252 1
            'op'            => 'move',
253 1
            'dest_fileid'   => $dstPath,
254 1
            'to_over_write' => $toOverWrite,
255 1
        ];
256
257 1
        $data = json_encode($data);
258
259
        $req = [
260 1
            'url'     => $url,
261 1
            'method'  => 'post',
262 1
            'timeout' => self::$timeout,
263 1
            'data'    => $data,
264
            'header'  => [
265 1
                    'Authorization: '.$sign,
266 1
                    'Content-Type: application/json',
267 1
            ],
268 1
        ];
269
270 1
        return self::sendRequest($req);
271
    }
272
273
    /**
274
     * 查询文件信息.
275
     *
276
     * @param string $bucketName bucket名称
277
     * @param string $path       文件路径
278
     *
279
     * @return array
280
     */
281 6
    public static function stat($bucketName, $path)
282
    {
283 6
        $path = self::normalizerPath($path);
284
285 6
        return self::statBase($bucketName, $path);
286
    }
287
288
    /**
289
     * 删除文件.
290
     *
291
     * @param string $bucketName
292
     * @param string $path       文件路径
293
     *
294
     * @return array
295
     */
296 1
    public static function delFile($bucketName, $path)
297
    {
298 1
        $path = self::normalizerPath($path);
299
300 1
        return self::delBase($bucketName, $path);
301
    }
302
303
    /**
304
     * 内部方法, 上传文件.
305
     *
306
     * @param string $bucketName bucket名称
307
     * @param string $srcPath    本地文件路径
308
     * @param string $dstPath    上传的文件路径
309
     * @param string $bizAttr    文件属性
310
     * @param int    $insertOnly 是否覆盖同名文件:0 覆盖,1:不覆盖
311
     *
312
     * @return array
313
     */
314 2
    private static function uploadfile($bucketName, $srcPath, $dstPath, $bizAttr = null, $insertOnly = null)
315
    {
316 2
        $srcPath = realpath($srcPath);
317 2
        $dstPath = self::cosUrlEncode($dstPath);
318
319 2 View Code Duplication
        if (filesize($srcPath) >= self::MAX_UNSLICE_FILE_SIZE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
320
            return [
321
                    'code'    => self::COSAPI_PARAMS_ERROR,
322
                    'message' => 'file '.$srcPath.' larger then 20M, please use upload_slice interface',
323
                    'data'    => [], ];
324
        }
325
326 2
        $expired = time() + self::EXPIRED_SECONDS;
327 2
        $url = self::generateResUrl($bucketName, $dstPath);
328 2
        $sign = Auth::appSign($expired, $bucketName);
329 2
        $sha1 = hash_file('sha1', $srcPath);
330
331
        $data = [
332 2
            'op'       => 'upload',
333 2
            'sha'      => $sha1,
334 2
            'biz_attr' => (isset($bizAttr) ? $bizAttr : ''),
335 2
        ];
336
337 2
        if (function_exists('curl_file_create')) {
338 2
            $data['filecontent'] = curl_file_create($srcPath);
339 2
        } else {
340
            $data['filecontent'] = '@'.$srcPath;
341
        }
342
343 2
        if (isset($insertOnly) && strlen($insertOnly) > 0) {
344 2
            $data['insertOnly'] = (($insertOnly == 0 || $insertOnly == '0') ? 0 : 1);
345 2
        }
346
347
        $req = [
348 2
            'url'     => $url,
349 2
            'method'  => 'post',
350 2
            'timeout' => self::$timeout,
351 2
            'data'    => $data,
352
            'header'  => [
353 2
                'Authorization:'.$sign,
354 2
            ],
355 2
        ];
356
357 2
        return self::sendRequest($req);
358
    }
359
360
    /**
361
     * 内部方法,上传文件.
362
     *
363
     * @param string $bucketName bucket名称
364
     * @param string $srcPath    本地文件路径
365
     * @param string $dstPath    上传的文件路径
366
     * @param string $bizAttr    文件属性
367
     * @param string $sliceSize  分片大小
368
     * @param int    $insertOnly 是否覆盖同名文件:0 覆盖,1:不覆盖
369
     *
370
     * @return array
371
     */
372
    private static function upload_slice(
373
        $bucketName, $srcPath, $dstPath,
374
        $bizAttr = null, $sliceSize = null, $insertOnly = null)
375
    {
376
        $srcPath = realpath($srcPath);
377
        $fileSize = filesize($srcPath);
378
        $dstPath = self::cosUrlEncode($dstPath);
379
380
        $expired = time() + self::EXPIRED_SECONDS;
381
        $url = self::generateResUrl($bucketName, $dstPath);
382
        $sign = Auth::appSign($expired, $bucketName);
383
        $sha1 = hash_file('sha1', $srcPath);
384
385
        $ret = self::upload_prepare(
386
                $fileSize, $sha1, $sliceSize,
387
                $sign, $url, $bizAttr, $insertOnly);
388
389
        if ($ret['code'] != 0) {
390
            return $ret;
391
        }
392
393
        if (isset($ret['data'])
394
                && isset($ret['data']['url'])) {
395
            //秒传命中,直接返回了url
396
            return $ret;
397
        }
398
399
        $sliceSize = $ret['data']['slice_size'];
400
        if ($sliceSize > self::SLICE_SIZE_3M ||
401
            $sliceSize <= 0) {
402
            $ret['code'] = self::COSAPI_PARAMS_ERROR;
403
            $ret['message'] = 'illegal slice size';
404
405
            return $ret;
406
        }
407
408
        $session = $ret['data']['session'];
409
        $offset = $ret['data']['offset'];
410
411
        $sliceCnt = ceil($fileSize / $sliceSize);
412
        // expired seconds for one slice mutiply by slice count
413
        // will be the expired seconds for whole file
414
        $expired = time() + (self::EXPIRED_SECONDS * $sliceCnt);
415
        $sign = Auth::appSign($expired, $bucketName);
416
417
        $ret = self::upload_data(
418
                $fileSize, $sha1, $sliceSize,
419
                $sign, $url, $srcPath,
420
                $offset, $session);
421
422
        return $ret;
423
    }
424
425
    /**
426
     * 第一个分片控制消息.
427
     *
428
     * @param string $fileSize   文件大小
429
     * @param string $sha1       文件sha值
430
     * @param string $sliceSize  分片大小
431
     * @param string $sign       签名
432
     * @param string $url        URL
433
     * @param string $bizAttr    文件属性
434
     * @param string $insertOnly 同名文件是否覆盖
435
     *
436
     * @return array
437
     */
438
    private static function upload_prepare(
439
        $fileSize, $sha1, $sliceSize,
440
        $sign, $url, $bizAttr = null, $insertOnly = null)
441
    {
442
        $data = [
443
            'op'       => 'upload_slice',
444
            'filesize' => $fileSize,
445
            'sha'      => $sha1,
446
        ];
447
448
        if (isset($bizAttr) && strlen($bizAttr)) {
449
            $data['biz_attr'] = $bizAttr;
450
        }
451
452
        if (isset($insertOnly)) {
453
            $data['insertOnly'] = (($insertOnly == 0) ? 0 : 1);
454
        }
455
456
        if ($sliceSize <= self::SLICE_SIZE_3M) {
457
            $data['slice_size'] = $sliceSize;
458
        } else {
459
            $data['slice_size'] = self::SLICE_SIZE_3M;
460
        }
461
462
        $req = [
463
            'url'     => $url,
464
            'method'  => 'post',
465
            'timeout' => self::$timeout,
466
            'data'    => $data,
467
            'header'  => [
468
                'Authorization:'.$sign,
469
            ],
470
        ];
471
472
        $ret = self::sendRequest($req);
473
474
        return $ret;
475
    }
476
477
    /**
478
     * 分片上传.
479
     *
480
     * @param int    $fileSize  文件大小
481
     * @param string $sha1      文件sha值
482
     * @param int    $sliceSize 文件分片大小
483
     * @param string $sign      签名
484
     * @param string $url       url
485
     * @param string $srcPath   源文件路径
486
     * @param int    $offset    文件偏移offset
487
     * @param string $session   session
488
     *
489
     * @return array
490
     */
491
    private static function upload_data(
492
            $fileSize, $sha1, $sliceSize,
493
            $sign, $url, $srcPath,
494
            $offset, $session)
495
    {
496
        while ($fileSize > $offset) {
497
            $filecontent = file_get_contents(
498
                    $srcPath, false, null,
499
                    $offset, $sliceSize);
500
501 View Code Duplication
            if ($filecontent === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
502
                return [
503
                    'code'    => self::COSAPI_PARAMS_ERROR,
504
                    'message' => 'read file '.$srcPath.' error',
505
                    'data'    => [],
506
                ];
507
            }
508
509
            $boundary = '---------------------------'.substr(md5(mt_rand()), 0, 10);
510
            $data = self::generateSliceBody(
511
                    $filecontent, $offset, $sha1,
512
                    $session, basename($srcPath), $boundary);
513
514
            $req = [
515
                'url'     => $url,
516
                'method'  => 'post',
517
                'timeout' => self::$timeout,
518
                'data'    => $data,
519
                'header'  => [
520
                    'Authorization:'.$sign,
521
                    'Content-Type: multipart/form-data; boundary='.$boundary,
522
                ],
523
            ];
524
525
            $retry_times = 0;
526
            do {
527
                $ret = self::sendRequest($req);
528
                if ($ret['code'] == 0) {
529
                    break;
530
                }
531
                $retry_times++;
532
            } while ($retry_times < self::MAX_RETRY_TIMES);
533
534
            if ($ret['code'] != 0) {
535
                return $ret;
536
            }
537
538
            if ($ret['data']['session']) {
539
                $session = $ret['data']['session'];
540
            }
541
542
            $offset += $sliceSize;
543
        }
544
545
        return $ret;
0 ignored issues
show
Bug introduced by
The variable $ret 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...
546
    }
547
548
    /**
549
     * 构造分片body体.
550
     *
551
     * @param string $fileContent 文件内容
552
     * @param string $offset      文件偏移
553
     * @param string $sha         文件sha值
554
     * @param string $session     session
555
     * @param string $fileName    文件名
556
     * @param string $boundary    分隔符
557
     *
558
     * @return string
559
     */
560
    private static function generateSliceBody(
561
            $fileContent, $offset, $sha,
0 ignored issues
show
Unused Code introduced by
The parameter $sha is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
562
            $session, $fileName, $boundary)
563
    {
564
        $formdata = '';
565
566
        $formdata .= '--'.$boundary."\r\n";
567
        $formdata .= "content-disposition: form-data; name=\"op\"\r\n\r\nupload_slice\r\n";
568
569
        $formdata .= '--'.$boundary."\r\n";
570
        $formdata .= "content-disposition: form-data; name=\"offset\"\r\n\r\n".$offset."\r\n";
571
572
        $formdata .= '--'.$boundary."\r\n";
573
        $formdata .= "content-disposition: form-data; name=\"session\"\r\n\r\n".$session."\r\n";
574
575
        $formdata .= '--'.$boundary."\r\n";
576
        $formdata .= 'content-disposition: form-data; name="fileContent"; filename="'.$fileName."\"\r\n";
577
        $formdata .= "content-type: application/octet-stream\r\n\r\n";
578
579
        $data = $formdata.$fileContent."\r\n--".$boundary."--\r\n";
580
581
        return $data;
582
    }
583
584
    /**
585
     * 内部公共函数.
586
     *
587
     * @param string $bucketName bucket名称
588
     * @param string $path       文件夹路径
589
     * @param int    $num        拉取的总数
590
     * @param string $pattern    eListBoth(默认),ListDirOnly,eListFileOnly
591
     * @param int    $order      默认正序(=0), 填1为反序,
592
     * @param string $context    在翻页查询时候用到
593
     *
594
     * @return array
595
     */
596 1
    private static function listBase(
597
                    $bucketName, $path, $num = 20,
598
                    $pattern = 'eListBoth', $order = 0, $context = null)
599
    {
600 1
        $path = self::cosUrlEncode($path);
601 1
        $expired = time() + self::EXPIRED_SECONDS;
602 1
        $url = self::generateResUrl($bucketName, $path);
603 1
        $sign = Auth::appSign($expired, $bucketName);
604
605
        $data = [
606 1
            'op' => 'list',
607 1
        ];
608
609 1
        if (self::isPatternValid($pattern) == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
610
            return [
611
                    'code'    => self::COSAPI_PARAMS_ERROR,
612
                    'message' => 'parameter pattern invalid',
613
                ];
614
        }
615 1
        $data['pattern'] = $pattern;
616
617 1
        if ($order != 0 && $order != 1) {
618
            return [
619
                    'code'    => self::COSAPI_PARAMS_ERROR,
620
                    'message' => 'parameter order invalid',
621
            ];
622
        }
623 1
        $data['order'] = $order;
624
625 1
        if ($num < 0 || $num > 199) {
626
            return [
627
                    'code'    => self::COSAPI_PARAMS_ERROR,
628
                    'message' => 'parameter num invalid, num need less then 200',
629
            ];
630
        }
631 1
        $data['num'] = $num;
632
633 1
        if (isset($context)) {
634
            $data['context'] = $context;
635
        }
636
637 1
        $url = $url.'?'.http_build_query($data);
638
639
        $req = [
640 1
            'url'     => $url,
641 1
            'method'  => 'get',
642 1
            'timeout' => self::$timeout,
643
            'header'  => [
644 1
                'Authorization:'.$sign,
645 1
            ],
646 1
        ];
647
648 1
        return self::sendRequest($req);
649
    }
650
651
    /**
652
     * 内部公共方法(更新文件和更新文件夹).
653
     *
654
     * @param string $bucketName           bucket名称
655
     * @param string $path                 路径
656
     * @param string $bizAttr              文件/目录属性
657
     * @param string $authority            :  eInvalid/eWRPrivate(私有)/eWPrivateRPublic(公有读写)
658
     * @param array  $custom_headers_array 携带的用户自定义头域,包括
659
     *                                     'Cache-Control' => '*'
660
     *                                     'Content-Type' => '*'
661
     *                                     'Content-Disposition' => '*'
662
     *                                     'Content-Language' => '*'
663
     *                                     'x-cos-meta-自定义内容' => '*'
664
     *
665
     * @return array|mixed
666
     */
667 1
    private static function updateBase($bucketName, $path,
668
                    $bizAttr = null, $authority = null, $custom_headers_array = null)
669
    {
670 1
        $path = self::cosUrlEncode($path);
671
672 1
        $url = self::generateResUrl($bucketName, $path);
673 1
        $sign = Auth::appSign_once(
674 1
                $path, $bucketName);
675
676
        $data = [
677 1
            'op' => 'update',
678 1
        ];
679
680 1
        $flag = 0;
681 1
        if (isset($bizAttr)) {
682
            $data['biz_attr'] = $bizAttr;
683
            $flag = $flag | 0x01;
684
        }
685
686 1
        if (isset($authority) && strlen($authority) > 0) {
687 1
            if (self::isAuthorityValid($authority) == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
688
                return [
689
                    'code'    => self::COSAPI_PARAMS_ERROR,
690
                    'message' => 'parameter authority invalid',
691
                    ];
692
            }
693
694 1
            $data['authority'] = $authority;
695 1
            $flag = $flag | 0x80;
696 1
        }
697
698 1
        if (isset($custom_headers_array)) {
699
            $data['custom_headers'] = [];
700
            self::add_customer_header($data['custom_headers'], $custom_headers_array);
701
            $flag = $flag | 0x40;
702
        }
703
704 1
        if ($flag != 0 && $flag != 1) {
705 1
            $data['flag'] = $flag;
706 1
        }
707
708 1
        $data = json_encode($data);
709
710
        $req = [
711 1
            'url'     => $url,
712 1
            'method'  => 'post',
713 1
            'timeout' => self::$timeout,
714 1
            'data'    => $data,
715
            'header'  => [
716 1
                'Authorization:'.$sign,
717 1
                'Content-Type: application/json',
718 1
            ],
719 1
        ];
720
721 1
        return self::sendRequest($req);
722
    }
723
724
    /**
725
     * 内部方法.
726
     *
727
     * @param string $bucketName bucket名称
728
     * @param string $path       文件/目录路径
729
     *
730
     * @return array
731
     */
732 6
    private static function statBase($bucketName, $path)
733
    {
734 6
        $path = self::cosUrlEncode($path);
735 6
        $expired = time() + self::EXPIRED_SECONDS;
736 6
        $url = self::generateResUrl($bucketName, $path);
737 6
        $sign = Auth::appSign($expired, $bucketName);
738
739
        $data = [
740 6
            'op' => 'stat',
741 6
        ];
742
743 6
        $url = $url.'?'.http_build_query($data);
744
745
        $req = [
746 6
            'url'     => $url,
747 6
            'method'  => 'get',
748 6
            'timeout' => self::$timeout,
749
            'header'  => [
750 6
                'Authorization:'.$sign,
751 6
            ],
752 6
        ];
753
754 6
        return self::sendRequest($req);
755
    }
756
757
    /**
758
     * 内部私有方法.
759
     *
760
     * @param string $bucketName bucket名称
761
     * @param string $path       文件/目录路径路径
762
     *
763
     * @return array
764
     */
765 2
    private static function delBase($bucketName, $path)
766
    {
767 2
        if ($path == '/') {
768
            return [
769
                    'code'    => self::COSAPI_PARAMS_ERROR,
770
                    'message' => 'can not delete bucket using api! go to http://console.qcloud.com/cos to operate bucket',
771
                    ];
772
        }
773
774 2
        $path = self::cosUrlEncode($path);
775
776 2
        $url = self::generateResUrl($bucketName, $path);
777 2
        $sign = Auth::appSign_once(
778 2
                $path, $bucketName);
779
780
        $data = [
781 2
            'op' => 'delete',
782 2
        ];
783
784 2
        $data = json_encode($data);
785
786
        $req = [
787 2
            'url'     => $url,
788 2
            'method'  => 'post',
789 2
            'timeout' => self::$timeout,
790 2
            'data'    => $data,
791
            'header'  => [
792 2
                'Authorization:'.$sign,
793 2
                'Content-Type: application/json',
794 2
            ],
795 2
        ];
796
797 2
        return self::sendRequest($req);
798
    }
799
800
    /**
801
     * 内部公共方法, 路径编码
802
     *
803
     * @param string $path 待编码路径
804
     *
805
     * @return mixed
806
     */
807 14
    private static function cosUrlEncode($path)
808
    {
809 14
        return str_replace('%2F', '/', rawurlencode($path));
810
    }
811
812
    /**
813
     * 内部公共方法, 构造URL.
814
     *
815
     * @param string $bucketName
816
     * @param string $dstPath
817
     *
818
     * @return string
819
     */
820 14
    private static function generateResUrl($bucketName, $dstPath)
821
    {
822 14
        return Conf::API_COSAPI_END_POINT.Conf::getAppId().'/'.$bucketName.$dstPath;
823
    }
824
825
    /**
826
     * 内部公共方法, 发送消息.
827
     *
828
     * @param array $req
829
     *
830
     * @return array
831
     */
832 14
    private static function sendRequest($req)
833
    {
834 14
        $rsp = Http::send($req);
835
836 14
        $ret = json_decode($rsp, true);
837
838 14
        if ($ret) {
839 14
            if (0 === $ret['code']) {
840
                return $ret;
841
            } else {
842
                return [
843 14
                    'code'    => $ret['code'],
844 14
                    'message' => $ret['message'],
845 14
                    'data'    => [],
846 14
                ];
847
            }
848
        } else {
849
            return [
850
                'code'    => self::COSAPI_NETWORK_ERROR,
851
                'message' => $rsp,
852
                'data'    => [],
853
            ];
854
        }
855
    }
856
857
    /**
858
     * 设置分片大小.
859
     *
860
     * @param string $sliceSize
861
     *
862
     * @return int
863
     */
864
    private static function getSliceSize($sliceSize)
865
    {
866
        $size = self::SLICE_SIZE_1M;
867
        if (!isset($sliceSize)) {
868
            return $size;
869
        }
870
871
        if ($sliceSize <= self::SLICE_SIZE_512K) {
872
            $size = self::SLICE_SIZE_512K;
873
        } elseif ($sliceSize <= self::SLICE_SIZE_1M) {
874
            $size = self::SLICE_SIZE_1M;
875
        } elseif ($sliceSize <= self::SLICE_SIZE_2M) {
876
            $size = self::SLICE_SIZE_2M;
877
        } else {
878
            $size = self::SLICE_SIZE_3M;
879
        }
880
881
        return $size;
882
    }
883
884
    /**
885
     * 内部方法, 规整文件路径.
886
     *
887
     * @param string $path     文件路径
888
     * @param bool   $isfolder 是否为文件夹
889
     *
890
     * @return string
891
     */
892 12
    private static function normalizerPath($path, $isfolder = false)
893
    {
894 12
        if (preg_match('/^\//', $path) == 0) {
895 12
            $path = '/'.$path;
896 12
        }
897
898 12
        if ($isfolder == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
899 3
            if (preg_match('/\/$/', $path) == 0) {
900 3
                $path = $path.'/';
901 3
            }
902 3
        }
903
904 12
        return $path;
905
    }
906
907
    /**
908
     * 判断authority值是否正确.
909
     *
910
     * @param string $authority
911
     *
912
     * @return bool
913
     */
914 1
    private static function isAuthorityValid($authority)
915
    {
916
        if ($authority == 'eInvalid'
917 1
        || $authority == 'eWRPrivate'
918 1
        || $authority == 'eWPrivateRPublic') {
919 1
            return true;
920
        }
921
922
        return false;
923
    }
924
925
    /**
926
     * 判断pattern值是否正确.
927
     *
928
     *
929
     * @param string $pattern
930
     *
931
     * @return bool
932
     */
933 1
    private static function isPatternValid($pattern)
934
    {
935
        if ($pattern == 'eListBoth'
936 1
        || $pattern == 'eListDirOnly'
937 1
        || $pattern == 'eListFileOnly') {
938 1
            return true;
939
        }
940
941
        return false;
942
    }
943
944
    /**
945
     * 判断是否符合自定义属性.
946
     *
947
     * @param string $key
948
     *
949
     * @return bool
950
     */
951
    private static function isCustomer_header($key)
952
    {
953
        if ($key == 'Cache-Control'
954
        || $key == 'Content-Type'
955
        || $key == 'Content-Disposition'
956
        || $key == 'Content-Language'
957
        || substr($key, 0, strlen('x-cos-meta-')) == 'x-cos-meta-') {
958
            return true;
959
        }
960
961
        return false;
962
    }
963
964
    /**
965
     * 增加自定义属性到data中.
966
     *
967
     * @param array $data
968
     * @param array $customer_headers_array
969
     */
970
    private static function add_customer_header(&$data, &$customer_headers_array)
971
    {
972
        if (count($customer_headers_array) < 1) {
973
            return;
974
        }
975
        foreach ($customer_headers_array as $key=>$value) {
976
            if (self::isCustomer_header($key)) {
977
                $data[$key] = $value;
978
            }
979
        }
980
    }
981
}
982