Passed
Push — master ( bcdf9c...292b00 )
by Luo
02:34
created

OssAdapter::setVisibility()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 4
nop 2
dl 0
loc 12
ccs 0
cts 10
cp 0
crap 12
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 Iidestiny\Flysystem\Oss\Traits\SignatureTrait;
15
use League\Flysystem\AdapterInterface;
16
use League\Flysystem\Adapter\AbstractAdapter;
17
use League\Flysystem\Adapter\Polyfill\NotSupportingVisibilityTrait;
18
use League\Flysystem\Config;
19
use OSS\Core\OssException;
20
use OSS\OssClient;
21
22
/**
23
 * Class OssAdapter.
24
 *
25
 * @author iidestiny <[email protected]>
26
 */
27
class OssAdapter extends AbstractAdapter
28
{
29
    use NotSupportingVisibilityTrait, SignatureTrait;
30
31
    /**
32
     * @var
33
     */
34
    protected $accessKeyId;
35
36
    /**
37
     * @var
38
     */
39
    protected $accessKeySecret;
40
41
    /**
42
     * @var
43
     */
44
    protected $endpoint;
45
46
    /**
47
     * @var
48
     */
49
    protected $bucket;
50
51
    /**
52
     * @var
53
     */
54
    protected $isCName;
55
56
    /**
57
     * @var OssClient
58
     */
59
    protected $client;
60
61
    /**
62
     * @var array|mixed[]
63
     */
64
    protected $params;
65
66
    /**
67
     * OssAdapter constructor.
68
     *
69
     * @param       $accessKeyId
70
     * @param       $accessKeySecret
71
     * @param       $endpoint
72
     * @param       $bucket
73
     * @param bool  $isCName
74
     * @param mixed ...$params
75
     *
76
     * @throws OssException
77
     */
78
    public function __construct($accessKeyId, $accessKeySecret, $endpoint, $bucket, $isCName = false, ...$params)
79
    {
80
        $this->accessKeyId     = $accessKeyId;
81
        $this->accessKeySecret = $accessKeySecret;
82
        $this->endpoint        = $endpoint;
83
        $this->bucket          = $bucket;
84
        $this->isCName         = $isCName;
85
        $this->params          = $params;
86
        $this->initClient();
87
    }
88
89
    /**
90
     * init oss client.
91
     *
92
     * @throws OssException
93
     */
94
    protected function initClient()
95
    {
96
        if (empty($this->client)) {
97
            $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

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