Passed
Push — master ( 62974d...a504b0 )
by Luo
04:22 queued 11s
created

OssAdapter::getClient()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
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
     * @var
35
     */
36
    protected $accessKeyId;
37
38
    /**
39
     * @var
40
     */
41
    protected $accessKeySecret;
42
43
    /**
44
     * @var
45
     */
46
    protected $endpoint;
47
48
    /**
49
     * @var
50
     */
51
    protected $bucket;
52
53
    /**
54
     * @var
55
     */
56
    protected $isCName;
57
58
    /**
59
     * @var array
60
     */
61
    protected $buckets;
62
63
    /**
64
     * @var OssClient
65
     */
66
    protected $client;
67
68
    /**
69
     * @var array|mixed[]
70
     */
71
    protected $params;
72
73
    /**
74
     * @var bool
75
     */
76
    protected $useSSL = false;
77
78
    /**
79
     * OssAdapter constructor.
80
     *
81
     * @param       $accessKeyId
82
     * @param       $accessKeySecret
83
     * @param       $endpoint
84
     * @param       $bucket
85
     * @param bool  $isCName
86
     * @param       $prefix
87
     * @param array $buckets
88
     * @param mixed ...$params
89
     *
90
     * @throws OssException
91
     */
92
    public function __construct($accessKeyId, $accessKeySecret, $endpoint, $bucket, $isCName = false, $prefix = '', $buckets = [], ...$params)
93
    {
94
        $this->accessKeyId     = $accessKeyId;
95
        $this->accessKeySecret = $accessKeySecret;
96
        $this->endpoint        = $endpoint;
97
        $this->bucket          = $bucket;
98
        $this->isCName         = $isCName;
99
        $this->setPathPrefix($prefix);
100
        $this->buckets = $buckets;
101
        $this->params = $params;
102
        $this->initClient();
103
        $this->checkEndpoint();
104
    }
105
106
    /**
107
     * 调用不同的桶配置.
108
     */
109
    public function bucket($bucket)
110
    {
111
        if (!isset($this->buckets[$bucket])) {
112
            throw new \Exception('bucket is not exist.');
113
        }
114
        $bucketConfig = $this->buckets[$bucket];
115
116
        $this->accessKeyId = $bucketConfig['access_key'];
117
        $this->accessKeySecret = $bucketConfig['secret_key'];
118
        $this->endpoint = $bucketConfig['endpoint'];
119
        $this->bucket = $bucketConfig['bucket'];
120
        $this->isCName = $bucketConfig['isCName'];
121
122
        $this->initClient();
123
        $this->checkEndpoint();
124
125
        return $this;
126
    }
127
128
    /**
129
     * init oss client.
130
     *
131
     * @throws OssException
132
     */
133
    protected function initClient()
134
    {
135
        if (empty($this->client)) {
136
            $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

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