Completed
Push — master ( 4ab4f4...b4fd54 )
by Luo
04:09
created

OssAdapter::initClient()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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

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