Test Failed
Branch master (3fe7f2)
by Jim
07:34
created

AliyunOssAdapter::read()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.8666
c 0
b 0
f 0
cc 2
nc 3
nop 1
crap 2
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: lenovo
5
 * Date: 8/23/2018
6
 * Time: 10:02 AM
7
 */
8
9
namespace JimChen\Flysystem\AliyunOss;
10
11
use OSS\OssClient;
12
use GuzzleHttp\Psr7;
13
use League\Flysystem\Util;
14
use OSS\Core\OssException;
15
use League\Flysystem\Config;
16
use League\Flysystem\Adapter\AbstractAdapter;
17
use League\Flysystem\Adapter\CanOverwriteFiles;
18
19
class AliyunOssAdapter extends AbstractAdapter implements CanOverwriteFiles
20
{
21
    /**
22
     * @var OssClient
23
     */
24
    protected $ossClient;
25
26
    /**
27
     * @var string
28
     */
29
    protected $bucket;
30
31
    /**
32
     * @var array
33
     */
34
    protected $options = [];
35
36
    /**
37
     * Constructor.
38
     *
39
     * @param OssClient $ossClient
40
     * @param string    $bucket
41
     * @param string    $prefix
42
     * @param array     $options
43
     */
44 86
    public function __construct(OssClient $ossClient, $bucket, $prefix = '', array $options = [])
45
    {
46 86
        $this->ossClient = $ossClient;
47 86
        $this->bucket = $bucket;
48 86
        $this->setPathPrefix($prefix);
49 86
        $this->options = $options;
50 86
    }
51
52
    /**
53
     * Get the OssClient bucket.
54
     *
55
     * @return string
56
     */
57 4
    public function getBucket()
58
    {
59 4
        return $this->bucket;
60
    }
61
62
    /**
63
     * Set the OssClient bucket.
64
     *
65
     * @param string $bucket
66
     */
67 2
    public function setBucket($bucket)
68
    {
69 2
        $this->bucket = $bucket;
70 2
    }
71
72
    /**
73
     * Get the OssClient instance.
74
     *
75
     * @return OssClient
76
     */
77 2
    public function getClient()
78
    {
79 2
        return $this->ossClient;
80
    }
81
82
    /**
83
     * Write a new file.
84
     *
85
     * @param string $path
86
     * @param string $contents
87
     * @param Config $config Config object
88
     *
89
     * @return array
90
     */
91 4
    public function write($path, $contents, Config $config)
92
    {
93 4
        return $this->upload($path, $contents, $config);
94
    }
95
96
    /**
97
     * Update a file.
98
     *
99
     * @param string $path
100
     * @param string $contents
101
     * @param Config $config Config object
102
     *
103
     * @return array
104
     */
105 2
    public function update($path, $contents, Config $config)
106
    {
107 2
        return $this->upload($path, $contents, $config);
108
    }
109
110
    /**
111
     * Write a new file using a stream.
112
     *
113
     * @param string   $path
114
     * @param resource $resource
115
     * @param Config   $config Config object
116
     *
117
     * @return array|false false on failure file meta data on success
118
     */
119 2
    public function writeStream($path, $resource, Config $config)
120
    {
121 2
        return $this->upload($path, $resource, $config);
0 ignored issues
show
Documentation introduced by
$resource is of type resource, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
122
    }
123
124
    /**
125
     * Update a file using a stream.
126
     *
127
     * @param string   $path
128
     * @param resource $resource
129
     * @param Config   $config Config object
130
     *
131
     * @return array|false false on failure file meta data on success
132
     */
133 2
    public function updateStream($path, $resource, Config $config)
134
    {
135 2
        return $this->upload($path, $resource, $config);
0 ignored issues
show
Documentation introduced by
$resource is of type resource, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
136
    }
137
138
    /**
139
     * Rename a file.
140
     *
141
     * @param string $path
142
     * @param string $newpath
143
     *
144
     * @return bool
145
     */
146 4
    public function rename($path, $newpath)
147
    {
148 4
        if (!$this->copy($path, $newpath)) {
149 2
            return false;
150
        }
151
152 2
        return $this->delete($path);
153
    }
154
155
    /**
156
     * Copy a file.
157
     *
158
     * @param string $path
159
     * @param string $newpath
160
     *
161
     * @return bool
162
     */
163 8
    public function copy($path, $newpath)
164
    {
165
        try {
166 8
            $this->ossClient->copyObject(
167 8
                $this->bucket,
168 8
                $this->applyPathPrefix($path),
169 8
                $this->bucket,
170 8
                $this->applyPathPrefix($newpath),
171 8
                $this->options
172 8
            );
173 8
        } catch (OssException $e) {
174 4
            return false;
175
        }
176
177 4
        return true;
178
    }
179
180
    /**
181
     * Delete a file.
182
     *
183
     * @param string $path
184
     *
185
     * @return bool
186
     */
187 4
    public function delete($path)
188
    {
189 4
        $object = $this->applyPathPrefix($path);
190
191 4
        $this->ossClient->deleteObject($this->bucket, $object, $this->options);
192
193 4
        return !$this->has($path);
194
    }
195
196
    /**
197
     * Delete a directory.
198
     *
199
     * @param string $dirname
200
     *
201
     * @return bool
202
     */
203 2
    public function deleteDir($dirname)
204
    {
205 2
        return false;
206
    }
207
208
    /**
209
     * Create a directory.
210
     *
211
     * @param string $dirname directory name
212
     * @param Config $config
213
     *
214
     * @return array|false
215
     */
216 4
    public function createDir($dirname, Config $config)
217
    {
218
        try {
219 4
            $key = $this->applyPathPrefix($dirname);
220 4
            $options = $this->getOptionsFromConfig($config);
221
222 4
            $response = $this->ossClient->createObjectDir($this->bucket, rtrim($key, '/'), $options);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $response is correct as $this->ossClient->create...m($key, '/'), $options) (which targets OSS\OssClient::createObjectDir()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
223 4
        } catch (OssException $e) {
224 2
            return false;
225
        }
226
227 2
        return $this->normalizeResponse($response, $key);
0 ignored issues
show
Documentation introduced by
$response is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
228
    }
229
230
    /**
231
     * Set the visibility for a file.
232
     *
233
     * @param string $path
234
     * @param string $visibility
235
     *
236
     * @return array|false file meta data
237
     */
238 6
    public function setVisibility($path, $visibility)
239
    {
240
        try {
241 6
            $this->ossClient->putObjectAcl(
242 6
                $this->bucket,
243 6
                $this->applyPathPrefix($path),
244 6
                in_array($visibility, OssClient::$OSS_ACL_TYPES) ? $visibility : 'default'
0 ignored issues
show
Bug introduced by
The property OSS_ACL_TYPES cannot be accessed from this context as it is declared private in class OSS\OssClient.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
245 6
            );
246 6
        } catch (OssException $e) {
247 2
            return false;
248
        }
249
250 4
        return compact('path', 'visibility');
251
    }
252
253
    /**
254
     * Check whether a file exists.
255
     *
256
     * @param string $path
257
     *
258
     * @return bool
259
     */
260 12
    public function has($path)
261
    {
262 12
        $location = $this->applyPathPrefix($path);
263
264 12
        if ($this->ossClient->doesObjectExist($this->bucket, $location, $this->options)) {
265 2
            return true;
266
        }
267
268 10
        return $this->doesDirectoryExist($location);
269
    }
270
271
    /**
272
     * Read a file.
273
     *
274
     * @param string $path
275
     *
276
     * @return array|false
277
     */
278 6
    public function read($path)
279
    {
280
        try {
281 6
            $response = $this->ossClient->getObject($this->bucket, $this->applyPathPrefix($path));
282
283 2
            return $this->normalizeResponse([
284 2
                'contents' => $response,
285 2
            ], $path);
286 4
        } catch (OssException $e) {
287 4
            return false;
288
        }
289
    }
290
291
    /**
292
     * Read a file as a stream.
293
     *
294
     * @param string $path
295
     *
296
     * @return array|false
297
     */
298 4
    public function readStream($path)
299
    {
300
        try {
301 4
            $object = $this->applyPathPrefix($path);
302 4
            $url = $this->ossClient->signUrl($this->bucket, $object, 60, OssClient::OSS_HTTP_GET, $this->options);
303 2
            $handle = fopen($url, 'r');
304
305 2
            return $this->normalizeResponse([
306 2
                'stream' => $handle,
307 2
            ], $path);
308 2
        } catch (OssException $e) {
309 2
            return false;
310
        }
311
    }
312
313
    /**
314
     * List contents of a directory.
315
     *
316
     * @param string $directory
317
     * @param bool   $recursive
318
     *
319
     * @return array|false
320
     */
321 6
    public function listContents($directory = '', $recursive = false)
322
    {
323 6
        $prefix = $this->applyPathPrefix(rtrim($directory, '/') . '/');
324
325
        $options = [
326 6
            OssClient::OSS_PREFIX    => ltrim($prefix, '/'),
327 6
            OssClient::OSS_MARKER    => '',
328 6
            OssClient::OSS_MAX_KEYS  => 100,
329 6
        ];
330
331 6
        if ($recursive) {
332 2
            $options[OssClient::OSS_DELIMITER] = '';
333 2
        } else {
334 4
            $options[OssClient::OSS_DELIMITER] = '/';
335
        }
336
337
        try {
338 6
            $listing = $this->retrieveListing($options);
339 6
        } catch (OssException $e) {
340 2
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface League\Flysystem\ReadInterface::listContents of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
341
        }
342
343 4
        $normalizer = [$this, 'normalizeResponse'];
344 4
        $normalized = array_map($normalizer, $listing);
345
346 4
        return Util::emulateDirectories($normalized);
347
    }
348
349
    /**
350
     * @param array $options
351
     *
352
     * @return array
353
     * @throws OssException
354
     */
355 6
    protected function retrieveListing(array $options)
356
    {
357 6
        $result = $this->ossClient->listObjects($this->bucket, $options);
358 4
        $listing = [];
359
360 4
        foreach ($result->getObjectList() as $object) {
361 4
            $listing[] = [
362 4
                'key'           => $object->getKey(),
363 4
                'last_modified' => $object->getLastModified(),
364 4
                'etag'          => $object->getETag(),
365 4
                'type'          => $object->getType(),
366 4
                'size'          => $object->getSize(),
367 4
                'storage_class' => $object->getStorageClass(),
368
            ];
369 4
        }
370
371 4
        foreach ($result->getPrefixList() as $object) {
372 4
            $listing[] = [
373 4
                'prefix' => $object->getPrefix(),
374
            ];
375 4
        }
376
377 4
        return $listing;
378
    }
379
380
    /**
381
     * Get all the meta data of a file or directory.
382
     *
383
     * @param string $path
384
     *
385
     * @return array|false
386
     */
387 12
    public function getMetadata($path)
388
    {
389 12
        $response = $this->getObjectMeta($path);
390
391 10
        if (empty($response)) {
392 10
            return false;
393
        }
394
395 8
        return $this->normalizeResponse($response, $path);
396
    }
397
398
    /**
399
     * Get the size of a file.
400
     *
401
     * @param string $path
402
     *
403
     * @return array|false
404
     */
405 2
    public function getSize($path)
406
    {
407 2
        if ($metaData = $this->getMetadata($path)) {
408 2
            return $this->normalizeResponse([
409 2
                'size' => $metaData['content-length'],
410 2
            ], $path);
411
        }
412
413 2
        return false;
414
    }
415
416
    /**
417
     * Get the mimetype of a file.
418
     *
419
     * @param string $path
420
     *
421
     * @return array|false
422
     */
423 2
    public function getMimetype($path)
424
    {
425 2
        if ($metaData = $this->getMetadata($path)) {
426 2
            $contentType = array_key_exists('content_type',
427 2
                $metaData) ? $metaData['content_type'] : $metaData['info']['content_type'];
428
429
            return [
430 2
                'mimetype' => $contentType,
431 2
            ];
432
        }
433
434 2
        return false;
435
    }
436
437
    /**
438
     * Get the last modified time of a file as a timestamp.
439
     *
440
     * @param string $path
441
     *
442
     * @return array|false
443
     */
444 2
    public function getTimestamp($path)
445
    {
446 2
        if ($metaData = $this->getMetadata($path)) {
447 2
            return $this->normalizeResponse([
448 2
                'timestamp' => strtotime($metaData['last-modified']),
449 2
            ], $path);
450
        }
451
452 2
        return false;
453
    }
454
455
    /**
456
     * Get the visibility of a file.
457
     *
458
     * @param string $path
459
     *
460
     * @return array|false
461
     */
462 6
    public function getVisibility($path)
463
    {
464
        try {
465
            return [
466 6
                'visibility' => $this->ossClient->getObjectAcl($this->bucket, $this->applyPathPrefix($path)),
467 4
            ];
468 2
        } catch (OssException $e) {
469 2
            return false;
470
        }
471
    }
472
473
    /**
474
     * {@inheritdoc}
475
     */
476 74
    public function applyPathPrefix($path)
477
    {
478 74
        return ltrim(parent::applyPathPrefix($path), '/');
479
    }
480
481
    /**
482
     * {@inheritdoc}
483
     */
484 86
    public function setPathPrefix($prefix)
485
    {
486 86
        $prefix = ltrim($prefix, '/');
487
488 86
        parent::setPathPrefix($prefix);
489 86
    }
490
491
    /**
492
     * Get the object meta.
493
     *
494
     * @param $path
495
     *
496
     * @return array|bool
497
     */
498 12
    protected function getObjectMeta($path)
499
    {
500
        try {
501 12
            return $this->ossClient->getObjectMeta($this->bucket, $this->applyPathPrefix($path));
502 12
        } catch (OssException $e) {
503 12
            if ($e->getHTTPStatus() === 404) {
504 10
                return false;
505
            }
506
507 2
            throw $e;
508
        }
509
    }
510
511
    /**
512
     * Upload an object.
513
     *
514
     * @param string $path
515
     * @param string $body
516
     * @param Config $config
517
     *
518
     * @return array|false
519
     * @throws \InvalidArgumentException
520
     */
521 10
    protected function upload($path, $body, Config $config)
522
    {
523 10
        $key = $this->applyPathPrefix($path);
524
525
        try {
526 10
            $options = $this->getOptionsFromConfig($config, [
527 10
                OssClient::OSS_LENGTH,
528 10
                OssClient::OSS_CONTENT_TYPE,
529 10
                OssClient::OSS_CALLBACK,
530 10
            ]);
531 10
            $response = $this->ossClient->putObject($this->bucket, $key, Psr7\stream_for($body)->getContents(),
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $response is correct as $this->ossClient->putObj...etContents(), $options) (which targets OSS\OssClient::putObject()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
532 10
                $options);
533 10
        } catch (OssException $e) {
534 2
            return false;
535
        }
536
537 8
        return $this->normalizeResponse($response, $key);
0 ignored issues
show
Documentation introduced by
$response is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
538
    }
539
540
    /**
541
     * Get options from the config.
542
     *
543
     * @param Config $config
544
     * @param array  $keys
545
     *
546
     * @return array
547
     */
548 14
    protected function getOptionsFromConfig(Config $config, $keys = [])
549
    {
550 14
        $options = $this->options;
551
552 14
        foreach ((array)$keys as $key) {
553 10
            if ($value = $config->get($key)) {
554
                $options[$key] = $value;
555
            }
556 14
        }
557
558 14
        return $options;
559
    }
560
561
    /**
562
     * Normalize the object result array.
563
     *
564
     * @param array $response
565
     * @param null  $path
566
     *
567
     * @return array
568
     */
569 26
    protected function normalizeResponse(array $response, $path = null)
570
    {
571
        $result = [
572 26
            'path' => $path ?: $this->removePathPrefix(
573 4
                isset($response['key']) ? $response['key'] : $response['prefix']
574 26
            ),
575 26
        ];
576
577 26
        $result = array_merge($result, Util::pathinfo($result['path']));
578
579 26
        if (substr($result['path'], -1) === '/') {
580 4
            $result['type'] = 'dir';
581 4
            $result['path'] = rtrim($result['path'], '/');
582
583 4
            return $result;
584
        }
585
586 26
        return array_merge($result, $response, [
587 26
            'type' => 'file',
588 26
        ]);
589
    }
590
591
    /**
592
     * @param $location
593
     *
594
     * @return bool
595
     */
596 10
    protected function doesDirectoryExist($location)
597
    {
598
        // Maybe this isn't an actual key, but a prefix.
599
        // Do a prefix listing of objects to determine.
600
        try {
601 10
            $result = $this->ossClient->listObjects($this->bucket, [
602 10
                OssClient::OSS_PREFIX   => rtrim($location, '/') . '/',
603 10
                OssClient::OSS_MAX_KEYS => 1,
604 10
            ]);
605
606 6
            return $result->getObjectList() || $result->getPrefixList();
607 4
        } catch (OssException $e) {
608 4
            if ($e->getHTTPStatus() === 403) {
609 2
                return false;
610
            }
611
612 2
            throw $e;
613
        }
614
    }
615
}
616