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