Passed
Pull Request — master (#54)
by
unknown
02:23
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
126
        $this->initClient();
127
        $this->checkEndpoint();
128
129
        return $this;
130
    }
131
132
    /**
133
     * init oss client.
134
     * @throws OssException
135
     */
136
    protected function initClient(): void
137
    {
138
        $this->client = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint, $this->isCName);
139
    }
140
141
    /**
142
     * get ali sdk kernel class.
143
     * @return OssClient
144
     */
145
    public function getClient(): OssClient
146
    {
147
        return $this->client;
148
    }
149
150
    /**
151
     * oss 直传配置.
152
     * @param string $prefix
153
     * @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...
154
     * @param array  $customData
155
     * @param int    $expire
156
     * @param int    $contentLengthRangeValue
157
     * @param array  $systemData
158
     * @return false|string
159
     * @throws \Exception
160
     */
161
    public function signatureConfig($prefix = '', $callBackUrl = null, $customData = [], $expire = 30, $contentLengthRangeValue = 1048576000, $systemData = [])
162
    {
163
        if (!empty($prefix)) {
164
            $prefix = ltrim($prefix, '/');
165
        }
166
167
        // 系统参数
168
        $system = [];
169
        if (empty($systemData)) {
170
            $system = self::SYSTEM_FIELD;
171
        } else {
172
            foreach ($systemData as $key => $value) {
173
                if (!in_array($value, self::SYSTEM_FIELD)) {
174
                    throw new \InvalidArgumentException("Invalid oss system filed: ${value}");
175
                }
176
                $system[$key] = $value;
177
            }
178
        }
179
180
        // 自定义参数
181
        $callbackVar = [];
182
        $data        = [];
183
        if (!empty($customData)) {
184
            foreach ($customData as $key => $value) {
185
                $callbackVar['x:' . $key] = $value;
186
                $data[$key]               = '${x:' . $key . '}';
187
            }
188
        }
189
190
        $callbackParam      = [
191
            'callbackUrl'      => $callBackUrl,
192
            'callbackBody'     => urldecode(http_build_query(array_merge($system, $data))),
193
            'callbackBodyType' => 'application/x-www-form-urlencoded',
194
        ];
195
        $callbackString     = json_encode($callbackParam);
196
        $base64CallbackBody = base64_encode($callbackString);
197
198
        $now        = time();
199
        $end        = $now + $expire;
200
        $expiration = $this->gmt_iso8601($end);
201
202
        // 最大文件大小.用户可以自己设置
203
        $condition    = [
204
            0 => 'content-length-range',
205
            1 => 0,
206
            2 => $contentLengthRangeValue,
207
        ];
208
        $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...
209
210
        $start        = [
211
            0 => 'starts-with',
212
            1 => '$key',
213
            2 => $prefix,
214
        ];
215
        $conditions[] = $start;
216
217
        $arr          = [
218
            'expiration' => $expiration,
219
            'conditions' => $conditions,
220
        ];
221
        $policy       = json_encode($arr);
222
        $base64Policy = base64_encode($policy);
223
        $stringToSign = $base64Policy;
224
        $signature    = base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret, true));
225
226
        $response                 = [];
227
        $response['accessid']     = $this->accessKeyId;
228
        $response['host']         = $this->normalizeHost();
229
        $response['policy']       = $base64Policy;
230
        $response['signature']    = $signature;
231
        $response['expire']       = $end;
232
        $response['callback']     = $base64CallbackBody;
233
        $response['callback-var'] = $callbackVar;
234
        $response['dir']          = $prefix;  // 这个参数是设置用户上传文件时指定的前缀。
235
236
        return json_encode($response);
237
    }
238
239
    /**
240
     * sign url.
241
     * @param string $path
242
     * @param int    $timeout
243
     * @param array  $options
244
     * @return bool|string
245
     */
246
    public function signUrl(string $path, int $timeout, array $options = [])
247
    {
248
        $path = $this->applyPathPrefix($path);
249
250
        try {
251
            $path = $this->client->signUrl($this->bucket, $path, $timeout, OssClient::OSS_HTTP_GET, $options);
252
        } catch (OssException $exception) {
253
            return false;
254
        }
255
256
        return $path;
257
    }
258
259
    /**
260
     * temporary file url.
261
     * @param string $path
262
     * @param int    $expiration
263
     * @param array  $options
264
     * @return bool|string
265
     */
266
    public function getTemporaryUrl(string $path, int $expiration, array $options = [])
267
    {
268
        return $this->signUrl($path, Carbon::now()->diffInSeconds($expiration), $options);
269
    }
270
271
    /**
272
     * write a file.
273
     * @param string                   $path
274
     * @param string                   $contents
275
     * @param \League\Flysystem\Config $config
276
     * @return array|bool
277
     */
278
    public function write(string $path, string $contents, Config $config)
279
    {
280
        $path = $this->applyPathPrefix($path);
281
282
        $options = [];
283
284
        if ($config->has('options')) {
285
            $options = $config->get('options');
286
        }
287
288
        $this->client->putObject($this->bucket, $path, $contents, $options);
289
290
        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...
291
    }
292
293
    /**
294
     * Write a new file using a stream.
295
     * @param string                   $path
296
     * @param resource                 $resource
297
     * @param \League\Flysystem\Config $config
298
     * @return array|bool|false
299
     */
300
    public function writeStream(string $path, $resource, Config $config)
301
    {
302
        $contents = stream_get_contents($resource);
303
304
        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...
305
    }
306
307
    /**
308
     * Update a file.
309
     * @param string                   $path
310
     * @param string                   $contents
311
     * @param \League\Flysystem\Config $config
312
     * @return array|bool
313
     */
314
    public function update(string $path, string $contents, Config $config)
315
    {
316
        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...
317
    }
318
319
    /**
320
     * Update a file using a stream.
321
     * @param string                   $path
322
     * @param resource                 $resource
323
     * @param \League\Flysystem\Config $config
324
     * @return array|bool
325
     */
326
    public function updateStream(string $path, $resource, Config $config)
327
    {
328
        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...
329
    }
330
331
    /**
332
     * rename a file.
333
     * @param string $path
334
     * @param string $newPath
335
     * @return bool
336
     * @throws \OSS\Core\OssException
337
     */
338
    public function rename(string $path, string $newPath)
339
    {
340
        if (!$this->copy($path, $newPath)) {
341
            return false;
342
        }
343
344
        return $this->delete($path);
345
    }
346
347
    /**
348
     * copy a file.
349
     * @param string $path
350
     * @param string $newPath
351
     * @return bool
352
     */
353
    public function copy(string $path, string $newPath)
354
    {
355
        $path    = $this->applyPathPrefix($path);
356
        $newpath = $this->applyPathPrefix($newPath);
357
358
        try {
359
            $this->client->copyObject($this->bucket, $path, $this->bucket, $newpath);
360
        } catch (OssException $exception) {
361
            return false;
362
        }
363
364
        return true;
365
    }
366
367
    /**
368
     * delete a file.
369
     * @param string $path
370
     * @return bool
371
     * @throws \OSS\Core\OssException
372
     */
373
    public function delete(string $path)
374
    {
375
        $path = $this->applyPathPrefix($path);
376
377
        try {
378
            $this->client->deleteObject($this->bucket, $path);
379
        } catch (OssException $ossException) {
380
            return false;
381
        }
382
383
        return !$this->has($path);
384
    }
385
386
    /**
387
     * Delete a directory.
388
     * @param string $dirname
389
     * @return bool
390
     * @throws \OSS\Core\OssException
391
     */
392
    public function deleteDir(string $dirname)
393
    {
394
        $fileList = $this->listContents($dirname, true);
395
        foreach ($fileList as $file) {
396
            $this->delete($file['path']);
397
        }
398
399
        return !$this->has($dirname);
400
    }
401
402
    /**
403
     * create a directory.
404
     * @param string                   $dirname
405
     * @param \League\Flysystem\Config $config
406
     * @return bool
407
     */
408
    public function createDir(string $dirname, Config $config)
409
    {
410
        $defaultFile = trim($dirname, '/') . '/oss.txt';
411
412
        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...
413
    }
414
415
    /**
416
     * visibility.
417
     * @param string $path
418
     * @param string $visibility
419
     * @return array|bool|false
420
     */
421
    public function setVisibility(string $path, string $visibility)
422
    {
423
        $object = $this->applyPathPrefix($path);
424
        $acl    = (AdapterInterface::VISIBILITY_PUBLIC === $visibility) ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE;
425
426
        try {
427
            $this->client->putObjectAcl($this->bucket, $object, $acl);
428
        } catch (OssException $exception) {
429
            return false;
430
        }
431
432
        return compact('visibility');
433
    }
434
435
    /**
436
     * Check whether a file exists.
437
     * @param string $path
438
     * @return array|bool|null
439
     */
440
    public function has(string $path)
441
    {
442
        $path = $this->applyPathPrefix($path);
443
444
        return $this->client->doesObjectExist($this->bucket, $path);
445
    }
446
447
    /**
448
     * Get resource url.
449
     * @param string $path
450
     * @return string
451
     */
452
    public function getUrl(string $path)
453
    {
454
        $path = $this->applyPathPrefix($path);
455
456
        return $this->normalizeHost() . ltrim($path, '/');
457
    }
458
459
    /**
460
     * read a file.
461
     * @param string $path
462
     * @return array|bool|false
463
     */
464
    public function read(string $path)
465
    {
466
        try {
467
            $contents = $this->getObject($path);
468
        } catch (OssException $exception) {
469
            return false;
470
        }
471
472
        return compact('contents', 'path');
473
    }
474
475
    /**
476
     * read a file stream.
477
     * @param string $path
478
     * @return array|bool|false
479
     */
480
    public function readStream(string $path)
481
    {
482
        try {
483
            $stream = $this->getObject($path);
484
        } catch (OssException $exception) {
485
            return false;
486
        }
487
488
        return compact('stream', 'path');
489
    }
490
491
    /**
492
     * Lists all files in the directory.
493
     * @param string $directory
494
     * @param bool   $recursive
495
     * @return array
496
     * @throws OssException
497
     */
498
    public function listContents(string $directory = '', bool $recursive = false)
499
    {
500
        $list = [];
501
502
        $result = $this->listDirObjects($directory, true);
503
504
        if (!empty($result['objects'])) {
505
            foreach ($result['objects'] as $files) {
506
                if (!$fileInfo = $this->normalizeFileInfo($files)) {
507
                    continue;
508
                }
509
510
                $list[] = $fileInfo;
511
            }
512
        }
513
514
        return $list;
515
    }
516
517
    /**
518
     * get meta data.
519
     * @param string $path
520
     * @return array|bool|false
521
     */
522
    public function getMetadata(string $path)
523
    {
524
        $path = $this->applyPathPrefix($path);
525
526
        try {
527
            $metadata = $this->client->getObjectMeta($this->bucket, $path);
528
        } catch (OssException $exception) {
529
            return false;
530
        }
531
532
        return $metadata;
533
    }
534
535
    /**
536
     * get the size of file.
537
     * @param string $path
538
     * @return array|false
539
     */
540
    public function getSize(string $path)
541
    {
542
        return $this->normalizeFileInfo(['Key' => $path]);
543
    }
544
545
    /**
546
     * get mime type.
547
     * @param string $path
548
     * @return array|false
549
     */
550
    public function getMimetype(string $path)
551
    {
552
        return $this->normalizeFileInfo(['Key' => $path]);
553
    }
554
555
    /**
556
     * get timestamp.
557
     * @param string $path
558
     * @return array|false
559
     */
560
    public function getTimestamp(string $path)
561
    {
562
        return $this->normalizeFileInfo(['Key' => $path]);
563
    }
564
565
    /**
566
     * normalize Host.
567
     * @return string
568
     */
569
    protected function normalizeHost()
570
    {
571
        if ($this->isCName) {
572
            if (!empty($this->cdnHost)) {
573
                $domain = $this->cdnHost;
574
            } else {
575
                $domain = $this->endpoint;
576
            }
577
        } else {
578
            $domain = $this->bucket . '.' . $this->endpoint;
579
        }
580
581
        if ($this->useSSL) {
582
            $domain = "https://{$domain}";
583
        } else {
584
            $domain = "http://{$domain}";
585
        }
586
587
        return rtrim($domain, '/') . '/';
588
    }
589
590
    /**
591
     * Check the endpoint to see if SSL can be used.
592
     */
593
    protected function checkEndpoint()
594
    {
595
        if (0 === strpos($this->endpoint, 'http://')) {
596
            $this->endpoint = substr($this->endpoint, strlen('http://'));
597
            $this->useSSL   = false;
598
        } elseif (0 === strpos($this->endpoint, 'https://')) {
599
            $this->endpoint = substr($this->endpoint, strlen('https://'));
600
            $this->useSSL   = true;
601
        }
602
    }
603
604
    /**
605
     * Read an object from the OssClient.
606
     * @param string $path
607
     * @return string
608
     */
609
    protected function getObject(string $path)
610
    {
611
        $path = $this->applyPathPrefix($path);
612
613
        return $this->client->getObject($this->bucket, $path);
614
    }
615
616
    /**
617
     * File list core method.
618
     * @param string $dirname
619
     * @param bool   $recursive
620
     * @return array
621
     * @throws OssException
622
     */
623
    public function listDirObjects(string $dirname = '', bool $recursive = false)
624
    {
625
        $delimiter  = '/';
626
        $nextMarker = '';
627
        $maxkeys    = 1000;
628
629
        $result = [];
630
631
        while (true) {
632
            $options = [
633
                'delimiter' => $delimiter,
634
                'prefix'    => $dirname,
635
                'max-keys'  => $maxkeys,
636
                'marker'    => $nextMarker,
637
            ];
638
639
            try {
640
                $listObjectInfo = $this->client->listObjects($this->bucket, $options);
641
            } catch (OssException $exception) {
642
                throw $exception;
643
            }
644
645
            $nextMarker = $listObjectInfo->getNextMarker();
646
            $objectList = $listObjectInfo->getObjectList();
647
            $prefixList = $listObjectInfo->getPrefixList();
648
649
            if (!empty($objectList)) {
650
                foreach ($objectList as $objectInfo) {
651
                    $object['Prefix']       = $dirname;
652
                    $object['Key']          = $objectInfo->getKey();
653
                    $object['LastModified'] = $objectInfo->getLastModified();
654
                    $object['eTag']         = $objectInfo->getETag();
655
                    $object['Type']         = $objectInfo->getType();
656
                    $object['Size']         = $objectInfo->getSize();
657
                    $object['StorageClass'] = $objectInfo->getStorageClass();
658
                    $result['objects'][]    = $object;
659
                }
660
            } else {
661
                $result['objects'] = [];
662
            }
663
664
            if (!empty($prefixList)) {
665
                foreach ($prefixList as $prefixInfo) {
666
                    $result['prefix'][] = $prefixInfo->getPrefix();
667
                }
668
            } else {
669
                $result['prefix'] = [];
670
            }
671
672
            // Recursive directory
673
            if ($recursive) {
674
                foreach ($result['prefix'] as $prefix) {
675
                    $next              = $this->listDirObjects($prefix, $recursive);
676
                    $result['objects'] = array_merge($result['objects'], $next['objects']);
677
                }
678
            }
679
680
            if ('' === $nextMarker) {
681
                break;
682
            }
683
        }
684
685
        return $result;
686
    }
687
688
    /**
689
     * Notes: normalize file info.
690
     * @Author: <C.Jason>
691
     * @Date  : 2020/9/7 10:48 上午
692
     * @param array $stats
693
     * @return array
694
     */
695
    protected function normalizeFileInfo(array $stats)
696
    {
697
        $filePath = ltrim($stats['Key'], '/');
698
699
        $meta = $this->getMetadata($filePath) ?? [];
700
701
        if (empty($meta)) {
702
            return [];
703
        }
704
705
        return [
706
            'type'      => 'file',
707
            'mimetype'  => $meta['content-type'],
708
            'path'      => $filePath,
709
            'timestamp' => $meta['info']['filetime'],
710
            'size'      => $meta['content-length'],
711
        ];
712
    }
713
714
}
715