Passed
Pull Request — master (#54)
by
unknown
02:26
created

OssAdapter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
nc 1
nop 8
dl 0
loc 12
rs 9.9332
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Jason\Flysystem\Oss;
4
5
use Carbon\Carbon;
6
use Iidestiny\Flysystem\Oss\Traits\SignatureTrait;
0 ignored issues
show
Bug introduced by
The type Iidestiny\Flysystem\Oss\Traits\SignatureTrait was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use League\Flysystem\Adapter\AbstractAdapter;
8
use League\Flysystem\Adapter\Polyfill\NotSupportingVisibilityTrait;
9
use League\Flysystem\AdapterInterface;
10
use League\Flysystem\Config;
11
use OSS\Core\OssException;
12
use OSS\OssClient;
13
14
/**
15
 * Class OssAdapter.
16
 * @author iidestiny <[email protected]>
17
 */
18
class OssAdapter extends AbstractAdapter
19
{
20
21
    use NotSupportingVisibilityTrait;
22
    use SignatureTrait;
23
24
    // 系统参数
25
    const SYSTEM_FIELD = [
26
        'bucket'   => '${bucket}',
27
        'etag'     => '${etag}',
28
        'filename' => '${object}',
29
        'size'     => '${size}',
30
        'mimeType' => '${mimeType}',
31
        'height'   => '${imageInfo.height}',
32
        'width'    => '${imageInfo.width}',
33
        'format'   => '${imageInfo.format}',
34
    ];
35
36
    /**
37
     * @var
38
     */
39
    protected $accessKeyId;
40
41
    /**
42
     * @var
43
     */
44
    protected $accessKeySecret;
45
46
    /**
47
     * @var
48
     */
49
    protected $endpoint;
50
51
    /**
52
     * @var
53
     */
54
    protected $bucket;
55
56
    /**
57
     * @var
58
     */
59
    protected $isCName;
60
61
    /**
62
     * @var string
63
     */
64
    protected $cdnHost;
65
66
    /**
67
     * @var array
68
     */
69
    protected $buckets;
70
71
    /**
72
     * @var OssClient
73
     */
74
    protected $client;
75
76
    /**
77
     * @var bool
78
     */
79
    protected $useSSL = false;
80
81
    /**
82
     * OssAdapter constructor.
83
     * @param string      $accessKeyId
84
     * @param string      $accessKeySecret
85
     * @param string      $endpoint
86
     * @param string      $bucket
87
     * @param bool        $isCName
88
     * @param string      $prefix
89
     * @param array       $buckets
90
     * @param string|null $cdnHost
91
     * @throws \OSS\Core\OssException
92
     */
93
    public function __construct(string $accessKeyId, string $accessKeySecret, string $endpoint, string $bucket, bool $isCName = false, string $prefix = '', array $buckets = [], string $cdnHost = null)
94
    {
95
        $this->accessKeyId     = $accessKeyId;
96
        $this->accessKeySecret = $accessKeySecret;
97
        $this->endpoint        = $endpoint;
98
        $this->bucket          = $bucket;
99
        $this->isCName         = $isCName;
100
        $this->cdnHost         = $cdnHost;
101
        $this->setPathPrefix($prefix);
102
        $this->buckets = $buckets;
103
        $this->initClient();
104
        $this->checkEndpoint();
105
    }
106
107
    /**
108
     * 调用不同的桶配置.
109
     * @param $bucket
110
     * @return $this
111
     * @throws OssException|\Exception
112
     */
113
    public function bucket($bucket)
114
    {
115
        if (!isset($this->buckets[$bucket])) {
116
            throw new \Exception('bucket is not exist.');
117
        }
118
        $bucketConfig = $this->buckets[$bucket];
119
120
        $this->accessKeyId     = $bucketConfig['access_key'];
121
        $this->accessKeySecret = $bucketConfig['secret_key'];
122
        $this->endpoint        = $bucketConfig['endpoint'];
123
        $this->bucket          = $bucketConfig['bucket'];
124
        $this->isCName         = $bucketConfig['isCName'];
125
        $this->cdnHost         = $bucketConfig['cdnHost'];
126
127
        $this->initClient();
128
        $this->checkEndpoint();
129
130
        return $this;
131
    }
132
133
    /**
134
     * init oss client.
135
     * @throws OssException
136
     */
137
    protected function initClient(): void
138
    {
139
        $this->client = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint, $this->isCName);
140
    }
141
142
    /**
143
     * get ali sdk kernel class.
144
     * @return OssClient
145
     */
146
    public function getClient(): OssClient
147
    {
148
        return $this->client;
149
    }
150
151
    /**
152
     * oss 直传配置.
153
     * @param string $prefix
154
     * @param null   $callBackUrl
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $callBackUrl is correct as it would always require null to be passed?
Loading history...
155
     * @param array  $customData
156
     * @param int    $expire
157
     * @param int    $contentLengthRangeValue
158
     * @param array  $systemData
159
     * @return false|string
160
     * @throws \Exception
161
     */
162
    public function signatureConfig($prefix = '', $callBackUrl = null, $customData = [], $expire = 30, $contentLengthRangeValue = 1048576000, $systemData = [])
163
    {
164
        if (!empty($prefix)) {
165
            $prefix = ltrim($prefix, '/');
166
        }
167
168
        // 系统参数
169
        $system = [];
170
        if (empty($systemData)) {
171
            $system = self::SYSTEM_FIELD;
172
        } else {
173
            foreach ($systemData as $key => $value) {
174
                if (!in_array($value, self::SYSTEM_FIELD)) {
175
                    throw new \InvalidArgumentException("Invalid oss system filed: ${value}");
176
                }
177
                $system[$key] = $value;
178
            }
179
        }
180
181
        // 自定义参数
182
        $callbackVar = [];
183
        $data        = [];
184
        if (!empty($customData)) {
185
            foreach ($customData as $key => $value) {
186
                $callbackVar['x:' . $key] = $value;
187
                $data[$key]               = '${x:' . $key . '}';
188
            }
189
        }
190
191
        $callbackParam      = [
192
            'callbackUrl'      => $callBackUrl,
193
            'callbackBody'     => urldecode(http_build_query(array_merge($system, $data))),
194
            'callbackBodyType' => 'application/x-www-form-urlencoded',
195
        ];
196
        $callbackString     = json_encode($callbackParam);
197
        $base64CallbackBody = base64_encode($callbackString);
198
199
        $now        = time();
200
        $end        = $now + $expire;
201
        $expiration = $this->gmt_iso8601($end);
202
203
        // 最大文件大小.用户可以自己设置
204
        $condition    = [
205
            0 => 'content-length-range',
206
            1 => 0,
207
            2 => $contentLengthRangeValue,
208
        ];
209
        $conditions[] = $condition;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$conditions was never initialized. Although not strictly required by PHP, it is generally a good practice to add $conditions = array(); before regardless.
Loading history...
210
211
        $start        = [
212
            0 => 'starts-with',
213
            1 => '$key',
214
            2 => $prefix,
215
        ];
216
        $conditions[] = $start;
217
218
        $arr          = [
219
            'expiration' => $expiration,
220
            'conditions' => $conditions,
221
        ];
222
        $policy       = json_encode($arr);
223
        $base64Policy = base64_encode($policy);
224
        $stringToSign = $base64Policy;
225
        $signature    = base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret, true));
226
227
        $response                 = [];
228
        $response['accessid']     = $this->accessKeyId;
229
        $response['host']         = $this->normalizeHost();
230
        $response['policy']       = $base64Policy;
231
        $response['signature']    = $signature;
232
        $response['expire']       = $end;
233
        $response['callback']     = $base64CallbackBody;
234
        $response['callback-var'] = $callbackVar;
235
        $response['dir']          = $prefix;  // 这个参数是设置用户上传文件时指定的前缀。
236
237
        return json_encode($response);
238
    }
239
240
    /**
241
     * sign url.
242
     * @param string $path
243
     * @param int    $timeout
244
     * @param array  $options
245
     * @return bool|string
246
     */
247
    public function signUrl(string $path, int $timeout, array $options = [])
248
    {
249
        $path = $this->applyPathPrefix($path);
250
251
        try {
252
            $path = $this->client->signUrl($this->bucket, $path, $timeout, OssClient::OSS_HTTP_GET, $options);
253
        } catch (OssException $exception) {
254
            return false;
255
        }
256
257
        return $path;
258
    }
259
260
    /**
261
     * temporary file url.
262
     * @param string $path
263
     * @param int    $expiration
264
     * @param array  $options
265
     * @return bool|string
266
     */
267
    public function getTemporaryUrl(string $path, int $expiration, array $options = [])
268
    {
269
        return $this->signUrl($path, Carbon::now()->diffInSeconds($expiration), $options);
270
    }
271
272
    /**
273
     * write a file.
274
     * @param string                   $path
275
     * @param string                   $contents
276
     * @param \League\Flysystem\Config $config
277
     * @return array|bool
278
     */
279
    public function write(string $path, string $contents, Config $config)
280
    {
281
        $path = $this->applyPathPrefix($path);
282
283
        $options = [];
284
285
        if ($config->has('options')) {
286
            $options = $config->get('options');
287
        }
288
289
        $this->client->putObject($this->bucket, $path, $contents, $options);
290
291
        return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the return type mandated by League\Flysystem\AdapterInterface::write() of array|false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
292
    }
293
294
    /**
295
     * Write a new file using a stream.
296
     * @param string                   $path
297
     * @param resource                 $resource
298
     * @param \League\Flysystem\Config $config
299
     * @return array|bool|false
300
     */
301
    public function writeStream(string $path, $resource, Config $config)
302
    {
303
        $contents = stream_get_contents($resource);
304
305
        return $this->write($path, $contents, $config);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->write($path, $contents, $config) returns the type true which is incompatible with the return type mandated by League\Flysystem\AdapterInterface::writeStream() of array|false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
306
    }
307
308
    /**
309
     * Update a file.
310
     * @param string                   $path
311
     * @param string                   $contents
312
     * @param \League\Flysystem\Config $config
313
     * @return array|bool
314
     */
315
    public function update(string $path, string $contents, Config $config)
316
    {
317
        return $this->write($path, $contents, $config);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->write($path, $contents, $config) returns the type true which is incompatible with the return type mandated by League\Flysystem\AdapterInterface::update() of array|false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
318
    }
319
320
    /**
321
     * Update a file using a stream.
322
     * @param string                   $path
323
     * @param resource                 $resource
324
     * @param \League\Flysystem\Config $config
325
     * @return array|bool
326
     */
327
    public function updateStream(string $path, $resource, Config $config)
328
    {
329
        return $this->writeStream($path, $resource, $config);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->writeStrea...th, $resource, $config) returns the type true which is incompatible with the return type mandated by League\Flysystem\AdapterInterface::updateStream() of array|false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
330
    }
331
332
    /**
333
     * rename a file.
334
     * @param string $path
335
     * @param string $newPath
336
     * @return bool
337
     * @throws \OSS\Core\OssException
338
     */
339
    public function rename(string $path, string $newPath)
340
    {
341
        if (!$this->copy($path, $newPath)) {
342
            return false;
343
        }
344
345
        return $this->delete($path);
346
    }
347
348
    /**
349
     * copy a file.
350
     * @param string $path
351
     * @param string $newPath
352
     * @return bool
353
     */
354
    public function copy(string $path, string $newPath)
355
    {
356
        $path    = $this->applyPathPrefix($path);
357
        $newpath = $this->applyPathPrefix($newPath);
358
359
        try {
360
            $this->client->copyObject($this->bucket, $path, $this->bucket, $newpath);
361
        } catch (OssException $exception) {
362
            return false;
363
        }
364
365
        return true;
366
    }
367
368
    /**
369
     * delete a file.
370
     * @param string $path
371
     * @return bool
372
     * @throws \OSS\Core\OssException
373
     */
374
    public function delete(string $path)
375
    {
376
        $path = $this->applyPathPrefix($path);
377
378
        try {
379
            $this->client->deleteObject($this->bucket, $path);
380
        } catch (OssException $ossException) {
381
            return false;
382
        }
383
384
        return !$this->has($path);
385
    }
386
387
    /**
388
     * Delete a directory.
389
     * @param string $dirname
390
     * @return bool
391
     * @throws \OSS\Core\OssException
392
     */
393
    public function deleteDir(string $dirname)
394
    {
395
        $fileList = $this->listContents($dirname, true);
396
        foreach ($fileList as $file) {
397
            $this->delete($file['path']);
398
        }
399
400
        return !$this->has($dirname);
401
    }
402
403
    /**
404
     * create a directory.
405
     * @param string                   $dirname
406
     * @param \League\Flysystem\Config $config
407
     * @return bool
408
     */
409
    public function createDir(string $dirname, Config $config)
410
    {
411
        $defaultFile = trim($dirname, '/') . '/oss.txt';
412
413
        return $this->write($defaultFile, '当虚拟目录下有其他文件时,可删除此文件~', $config);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->write($def... returns the type true which is incompatible with the return type mandated by League\Flysystem\AdapterInterface::createDir() of array|false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
414
    }
415
416
    /**
417
     * visibility.
418
     * @param string $path
419
     * @param string $visibility
420
     * @return array|bool|false
421
     */
422
    public function setVisibility(string $path, string $visibility)
423
    {
424
        $object = $this->applyPathPrefix($path);
425
        $acl    = (AdapterInterface::VISIBILITY_PUBLIC === $visibility) ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE;
426
427
        try {
428
            $this->client->putObjectAcl($this->bucket, $object, $acl);
429
        } catch (OssException $exception) {
430
            return false;
431
        }
432
433
        return compact('visibility');
434
    }
435
436
    /**
437
     * Check whether a file exists.
438
     * @param string $path
439
     * @return array|bool|null
440
     */
441
    public function has(string $path)
442
    {
443
        $path = $this->applyPathPrefix($path);
444
445
        return $this->client->doesObjectExist($this->bucket, $path);
446
    }
447
448
    /**
449
     * Get resource url.
450
     * @param string $path
451
     * @return string
452
     */
453
    public function getUrl(string $path)
454
    {
455
        $path = $this->applyPathPrefix($path);
456
457
        return $this->normalizeHost() . ltrim($path, '/');
458
    }
459
460
    /**
461
     * read a file.
462
     * @param string $path
463
     * @return array|bool|false
464
     */
465
    public function read(string $path)
466
    {
467
        try {
468
            $contents = $this->getObject($path);
469
        } catch (OssException $exception) {
470
            return false;
471
        }
472
473
        return compact('contents', 'path');
474
    }
475
476
    /**
477
     * read a file stream.
478
     * @param string $path
479
     * @return array|bool|false
480
     */
481
    public function readStream(string $path)
482
    {
483
        try {
484
            $stream = $this->getObject($path);
485
        } catch (OssException $exception) {
486
            return false;
487
        }
488
489
        return compact('stream', 'path');
490
    }
491
492
    /**
493
     * Lists all files in the directory.
494
     * @param string $directory
495
     * @param bool   $recursive
496
     * @return array
497
     * @throws OssException
498
     */
499
    public function listContents(string $directory = '', bool $recursive = false)
500
    {
501
        $list = [];
502
503
        $result = $this->listDirObjects($directory, true);
504
505
        if (!empty($result['objects'])) {
506
            foreach ($result['objects'] as $files) {
507
                if (!$fileInfo = $this->normalizeFileInfo($files)) {
508
                    continue;
509
                }
510
511
                $list[] = $fileInfo;
512
            }
513
        }
514
515
        return $list;
516
    }
517
518
    /**
519
     * get meta data.
520
     * @param string $path
521
     * @return array|bool|false
522
     */
523
    public function getMetadata(string $path)
524
    {
525
        $path = $this->applyPathPrefix($path);
526
527
        try {
528
            $metadata = $this->client->getObjectMeta($this->bucket, $path);
529
        } catch (OssException $exception) {
530
            return false;
531
        }
532
533
        return $metadata;
534
    }
535
536
    /**
537
     * get the size of file.
538
     * @param string $path
539
     * @return array|false
540
     */
541
    public function getSize(string $path)
542
    {
543
        return $this->normalizeFileInfo(['Key' => $path]);
544
    }
545
546
    /**
547
     * get mime type.
548
     * @param string $path
549
     * @return array|false
550
     */
551
    public function getMimetype(string $path)
552
    {
553
        return $this->normalizeFileInfo(['Key' => $path]);
554
    }
555
556
    /**
557
     * get timestamp.
558
     * @param string $path
559
     * @return array|false
560
     */
561
    public function getTimestamp(string $path)
562
    {
563
        return $this->normalizeFileInfo(['Key' => $path]);
564
    }
565
566
    /**
567
     * normalize Host.
568
     * @return string
569
     */
570
    protected function normalizeHost()
571
    {
572
        if ($this->isCName) {
573
            if (!empty($this->cdnHost)) {
574
                $domain = $this->cdnHost;
575
            } else {
576
                $domain = $this->endpoint;
577
            }
578
        } else {
579
            $domain = $this->bucket . '.' . $this->endpoint;
580
        }
581
582
        if ($this->useSSL) {
583
            $domain = "https://{$domain}";
584
        } else {
585
            $domain = "http://{$domain}";
586
        }
587
588
        return rtrim($domain, '/') . '/';
589
    }
590
591
    /**
592
     * Check the endpoint to see if SSL can be used.
593
     */
594
    protected function checkEndpoint()
595
    {
596
        if (0 === strpos($this->endpoint, 'http://')) {
597
            $this->endpoint = substr($this->endpoint, strlen('http://'));
598
            $this->useSSL   = false;
599
        } elseif (0 === strpos($this->endpoint, 'https://')) {
600
            $this->endpoint = substr($this->endpoint, strlen('https://'));
601
            $this->useSSL   = true;
602
        }
603
    }
604
605
    /**
606
     * Read an object from the OssClient.
607
     * @param string $path
608
     * @return string
609
     */
610
    protected function getObject(string $path)
611
    {
612
        $path = $this->applyPathPrefix($path);
613
614
        return $this->client->getObject($this->bucket, $path);
615
    }
616
617
    /**
618
     * File list core method.
619
     * @param string $dirname
620
     * @param bool   $recursive
621
     * @return array
622
     * @throws OssException
623
     */
624
    public function listDirObjects(string $dirname = '', bool $recursive = false)
625
    {
626
        $delimiter  = '/';
627
        $nextMarker = '';
628
        $maxkeys    = 1000;
629
630
        $result = [];
631
632
        while (true) {
633
            $options = [
634
                'delimiter' => $delimiter,
635
                'prefix'    => $dirname,
636
                'max-keys'  => $maxkeys,
637
                'marker'    => $nextMarker,
638
            ];
639
640
            try {
641
                $listObjectInfo = $this->client->listObjects($this->bucket, $options);
642
            } catch (OssException $exception) {
643
                throw $exception;
644
            }
645
646
            $nextMarker = $listObjectInfo->getNextMarker();
647
            $objectList = $listObjectInfo->getObjectList();
648
            $prefixList = $listObjectInfo->getPrefixList();
649
650
            if (!empty($objectList)) {
651
                foreach ($objectList as $objectInfo) {
652
                    $object['Prefix']       = $dirname;
653
                    $object['Key']          = $objectInfo->getKey();
654
                    $object['LastModified'] = $objectInfo->getLastModified();
655
                    $object['eTag']         = $objectInfo->getETag();
656
                    $object['Type']         = $objectInfo->getType();
657
                    $object['Size']         = $objectInfo->getSize();
658
                    $object['StorageClass'] = $objectInfo->getStorageClass();
659
                    $result['objects'][]    = $object;
660
                }
661
            } else {
662
                $result['objects'] = [];
663
            }
664
665
            if (!empty($prefixList)) {
666
                foreach ($prefixList as $prefixInfo) {
667
                    $result['prefix'][] = $prefixInfo->getPrefix();
668
                }
669
            } else {
670
                $result['prefix'] = [];
671
            }
672
673
            // Recursive directory
674
            if ($recursive) {
675
                foreach ($result['prefix'] as $prefix) {
676
                    $next              = $this->listDirObjects($prefix, $recursive);
677
                    $result['objects'] = array_merge($result['objects'], $next['objects']);
678
                }
679
            }
680
681
            if ('' === $nextMarker) {
682
                break;
683
            }
684
        }
685
686
        return $result;
687
    }
688
689
    /**
690
     * Notes: normalize file info.
691
     * @Author: <C.Jason>
692
     * @Date  : 2020/9/7 10:48 上午
693
     * @param array $stats
694
     * @return array
695
     */
696
    protected function normalizeFileInfo(array $stats)
697
    {
698
        $filePath = ltrim($stats['Key'], '/');
699
700
        $meta = $this->getMetadata($filePath) ?? [];
701
702
        if (empty($meta)) {
703
            return [];
704
        }
705
706
        return [
707
            'type'      => 'file',
708
            'mimetype'  => $meta['content-type'],
709
            'path'      => $filePath,
710
            'timestamp' => $meta['info']['filetime'],
711
            'size'      => $meta['content-length'],
712
        ];
713
    }
714
715
}
716