Passed
Pull Request — master (#48)
by
unknown
02:19
created

OssAdapter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 5
Bugs 3 Features 0
Metric Value
cc 1
eloc 10
c 5
b 3
f 0
nc 1
nop 8
dl 0
loc 12
ccs 0
cts 12
cp 0
crap 2
rs 9.9332

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
/*
4
 * This file is part of the iidestiny/flysystem-oss.
5
 *
6
 * (c) iidestiny <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Iidestiny\Flysystem\Oss;
13
14
use Carbon\Carbon;
15
use Iidestiny\Flysystem\Oss\Traits\SignatureTrait;
16
use League\Flysystem\AdapterInterface;
17
use League\Flysystem\Adapter\AbstractAdapter;
18
use League\Flysystem\Adapter\Polyfill\NotSupportingVisibilityTrait;
19
use League\Flysystem\Config;
20
use OSS\Core\OssException;
21
use OSS\OssClient;
22
23
/**
24
 * Class OssAdapter.
25
 *
26
 * @author iidestiny <[email protected]>
27
 */
28
class OssAdapter extends AbstractAdapter
29
{
30
    use NotSupportingVisibilityTrait;
31
    use SignatureTrait;
32
33
    // 系统参数
34
    const SYSTEM_FIELD = [
35
        'bucket' => '${bucket}',
36
        'etag' => '${etag}',
37
        'filename' => '${object}',
38
        'size' => '${size}',
39
        'mimeType' => '${mimeType}',
40
        'height' => '${imageInfo.height}',
41
        'width' => '${imageInfo.width}',
42
        'format' => '${imageInfo.format}',
43
    ];
44
45
    /**
46
     * @var
47
     */
48
    protected $accessKeyId;
49
50
    /**
51
     * @var
52
     */
53
    protected $accessKeySecret;
54
55
    /**
56
     * @var
57
     */
58
    protected $endpoint;
59
60
    /**
61
     * @var
62
     */
63
    protected $bucket;
64
65
    /**
66
     * @var
67
     */
68
    protected $isCName;
69
70
    /**
71
     * @var array
72
     */
73
    protected $buckets;
74
75
    /**
76
     * @var OssClient
77
     */
78
    protected $client;
79
80
    /**
81
     * @var array|mixed[]
82
     */
83
    protected $params;
84
85
    /**
86
     * @var bool
87
     */
88
    protected $useSSL = false;
89
90
    /**
91
     * OssAdapter constructor.
92
     *
93
     * @param       $accessKeyId
94
     * @param       $accessKeySecret
95
     * @param       $endpoint
96
     * @param       $bucket
97
     * @param bool  $isCName
98
     * @param       $prefix
99
     * @param array $buckets
100
     * @param mixed ...$params
101
     *
102
     * @throws OssException
103
     */
104
    public function __construct($accessKeyId, $accessKeySecret, $endpoint, $bucket, $isCName = false, $prefix = '', $buckets = [], ...$params)
105
    {
106
        $this->accessKeyId = $accessKeyId;
107
        $this->accessKeySecret = $accessKeySecret;
108
        $this->endpoint = $endpoint;
109
        $this->bucket = $bucket;
110
        $this->isCName = $isCName;
111
        $this->setPathPrefix($prefix);
112
        $this->buckets = $buckets;
113
        $this->params = $params;
114
        $this->initClient();
115
        $this->checkEndpoint();
116
    }
117
118
    /**
119
     * 调用不同的桶配置.
120
     *
121
     * @param $bucket
122
     *
123
     * @return $this
124
     *
125
     * @throws OssException
126
     */
127
    public function bucket($bucket)
128
    {
129
        if (!isset($this->buckets[$bucket])) {
130
            throw new \Exception('bucket is not exist.');
131
        }
132
        $bucketConfig = $this->buckets[$bucket];
133
134
        $this->accessKeyId = $bucketConfig['access_key'];
135
        $this->accessKeySecret = $bucketConfig['secret_key'];
136
        $this->endpoint = $bucketConfig['endpoint'];
137
        $this->bucket = $bucketConfig['bucket'];
138
        $this->isCName = $bucketConfig['isCName'];
139
140
        $this->initClient();
141
        $this->checkEndpoint();
142
143
        return $this;
144
    }
145
146
    /**
147
     * init oss client.
148
     *
149
     * @throws OssException
150
     */
151
    protected function initClient()
152
    {
153
        if (empty($this->client)) {
154
            $this->client = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint, $this->isCName, ...$this->params);
0 ignored issues
show
Bug introduced by
$this->params is expanded, but the parameter $securityToken of OSS\OssClient::__construct() does not expect variable arguments. ( Ignorable by Annotation )

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

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