Passed
Push — master ( d2b798...4bfa0a )
by Luo
04:04 queued 01:44
created

OssAdapter::setVisibility()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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

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