Passed
Push — master ( c828b5...9c8737 )
by frey
45s
created

Adapter::getVisibility()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 6
Ratio 42.86 %

Code Coverage

Tests 2
CRAP Score 14.1113

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 6
loc 14
ccs 2
cts 7
cp 0.2857
rs 8.8571
cc 5
eloc 7
nc 3
nop 1
crap 14.1113
1
<?php
2
3
namespace Freyo\Flysystem\QcloudCOSv4;
4
5
use Freyo\Flysystem\QcloudCOSv4\Client\Conf;
6
use Freyo\Flysystem\QcloudCOSv4\Client\Cosapi;
7
use Freyo\Flysystem\QcloudCOSv4\Exceptions\RuntimeException;
8
use League\Flysystem\Adapter\AbstractAdapter;
9
use League\Flysystem\AdapterInterface;
10
use League\Flysystem\Config;
11
use League\Flysystem\Util;
12
13
/**
14
 * Class Adapter.
15
 */
16
class Adapter extends AbstractAdapter
17
{
18
    /**
19
     * @var
20
     */
21
    protected $bucket;
22
23
    /**
24
     * @var
25
     */
26
    protected $debug;
27
28
    /**
29
     * Adapter constructor.
30
     *
31
     * @param $config
32
     */
33
    public function __construct($config)
34
    {
35
        Conf::setAppId($config['app_id']);
36
        Conf::setSecretId($config['secret_id']);
37
        Conf::setSecretKey($config['secret_key']);
38
39
        $this->bucket = $config['bucket'];
40
        $this->debug = $config['debug'];
41
42
        $this->setPathPrefix($config['protocol'].'://'.$config['domain'].'/');
43
44
        Cosapi::setTimeout($config['timeout']);
45
        Cosapi::setRegion($config['region']);
46
    }
47
48
    /**
49
     * @return mixed
50
     */
51 15
    public function getBucket()
52
    {
53 15
        return $this->bucket;
54
    }
55
56
    /**
57
     * @param $path
58
     *
59
     * @return string
60
     */
61 1
    public function getUrl($path)
62
    {
63 1
        return $this->applyPathPrefix($path);
64
    }
65
66
    /**
67
     * @param string $path
68
     * @param string $contents
69
     * @param Config $config
70
     *
71
     * @throws RuntimeException
72
     *
73
     * @return array|bool
74
     */
75 View Code Duplication
    public function write($path, $contents, Config $config)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
76
    {
77
        $tmpfname = $this->writeTempFile($contents);
78
79
        try {
80
            $response = Cosapi::upload($this->getBucket(), $tmpfname, $path,
0 ignored issues
show
Bug introduced by
It seems like $tmpfname defined by $this->writeTempFile($contents) on line 77 can also be of type boolean; however, Freyo\Flysystem\QcloudCO...Client\Cosapi::upload() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
81
                                       null, null, $config->get('insertOnly', 1));
82
83
            $this->deleteTempFile($tmpfname);
84
85
            $response = $this->normalizeResponse($response);
86
87
            if (false === $response) {
88
                return false;
89
            }
90
91
            $this->setContentType($path, $contents);
92
        } catch (RuntimeException $exception) {
93
            $this->deleteTempFile($tmpfname);
94
95
            throw $exception;
96
        }
97
98
        return $response;
99
    }
100
101
    /**
102
     * @param string   $path
103
     * @param resource $resource
104
     * @param Config   $config
105
     *
106
     * @throws RuntimeException
107
     *
108
     * @return array|bool
109
     */
110 1 View Code Duplication
    public function writeStream($path, $resource, Config $config)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
111
    {
112 1
        $uri = stream_get_meta_data($resource)['uri'];
113
114 1
        $response = Cosapi::upload($this->getBucket(), $uri, $path,
115 1
                                   null, null, $config->get('insertOnly', 1));
116
117 1
        $response = $this->normalizeResponse($response);
118
119
        if (false === $response) {
120
            return false;
121
        }
122
123
        $this->setContentType($path, stream_get_contents($resource));
124
125
        return $response;
126
    }
127
128
    /**
129
     * @param string $path
130
     * @param string $contents
131
     * @param Config $config
132
     *
133
     * @throws RuntimeException
134
     *
135
     * @return array|bool
136
     */
137 View Code Duplication
    public function update($path, $contents, Config $config)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
138
    {
139
        $tmpfname = $this->writeTempFile($contents);
140
141
        try {
142
            $response = Cosapi::upload($this->getBucket(), $tmpfname, $path,
0 ignored issues
show
Bug introduced by
It seems like $tmpfname defined by $this->writeTempFile($contents) on line 139 can also be of type boolean; however, Freyo\Flysystem\QcloudCO...Client\Cosapi::upload() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
143
                                       null, null, $config->get('insertOnly', 0));
144
145
            $this->deleteTempFile($tmpfname);
146
147
            $response = $this->normalizeResponse($response);
148
149
            if (false === $response) {
150
                return false;
151
            }
152
153
            $this->setContentType($path, $contents);
154
        } catch (RuntimeException $exception) {
155
            $this->deleteTempFile($tmpfname);
156
157
            throw $exception;
158
        }
159
160
        return $response;
161
    }
162
163
    /**
164
     * @param string   $path
165
     * @param resource $resource
166
     * @param Config   $config
167
     *
168
     * @throws RuntimeException
169
     *
170
     * @return array|bool
171
     */
172 1 View Code Duplication
    public function updateStream($path, $resource, Config $config)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
173
    {
174 1
        $uri = stream_get_meta_data($resource)['uri'];
175
176 1
        $response = Cosapi::upload($this->getBucket(), $uri, $path,
177 1
                                   null, null, $config->get('insertOnly', 0));
178
179 1
        $response = $this->normalizeResponse($response);
180
181
        if (false === $response) {
182
            return false;
183
        }
184
185
        $this->setContentType($path, stream_get_contents($resource));
186
187
        return $response;
188
    }
189
190
    /**
191
     * @param string $path
192
     * @param string $newpath
193
     *
194
     * @return bool
195
     */
196 1
    public function rename($path, $newpath)
197
    {
198 1
        return (bool) $this->normalizeResponse(
199 1
            Cosapi::moveFile($this->getBucket(), $path, $newpath, 1)
0 ignored issues
show
Documentation introduced by
1 is of type integer, but the function expects a boolean.

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...
200 1
        );
201
    }
202
203
    /**
204
     * @param string $path
205
     * @param string $newpath
206
     *
207
     * @return bool
208
     */
209 1
    public function copy($path, $newpath)
210
    {
211 1
        return (bool) $this->normalizeResponse(
212 1
            Cosapi::copyFile($this->getBucket(), $path, $newpath, 1)
0 ignored issues
show
Documentation introduced by
1 is of type integer, but the function expects a boolean.

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...
213 1
        );
214
    }
215
216
    /**
217
     * @param string $path
218
     *
219
     * @return bool
220
     */
221 1
    public function delete($path)
222
    {
223 1
        return (bool) $this->normalizeResponse(
224 1
            Cosapi::delFile($this->getBucket(), $path)
225 1
        );
226
    }
227
228
    /**
229
     * @param string $dirname
230
     *
231
     * @return bool
232
     */
233 1
    public function deleteDir($dirname)
234
    {
235 1
        return (bool) $this->normalizeResponse(
236 1
            Cosapi::delFolder($this->getBucket(), $dirname)
237 1
        );
238
    }
239
240
    /**
241
     * @param string $dirname
242
     * @param Config $config
243
     *
244
     * @return bool
245
     */
246 1
    public function createDir($dirname, Config $config)
247
    {
248 1
        return (bool) $this->normalizeResponse(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return (bool) $this->nor...etBucket(), $dirname)); (boolean) is incompatible with the return type declared by the interface League\Flysystem\AdapterInterface::createDir of type array|false.

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...
249 1
            Cosapi::createFolder($this->getBucket(), $dirname)
250 1
        );
251
    }
252
253
    /**
254
     * @param string $path
255
     * @param string $visibility
256
     *
257
     * @return bool
258
     */
259 1
    public function setVisibility($path, $visibility)
260
    {
261 1
        $visibility = $visibility === AdapterInterface::VISIBILITY_PUBLIC ? 'eWPrivateRPublic' : 'eWRPrivate';
262
263 1
        return (bool) $this->normalizeResponse(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return (bool) $this->nor...h, null, $visibility)); (boolean) is incompatible with the return type declared by the interface League\Flysystem\AdapterInterface::setVisibility of type array|false.

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...
264 1
            Cosapi::update($this->getBucket(), $path, null, $visibility)
265 1
        );
266
    }
267
268
    /**
269
     * @param string $path
270
     *
271
     * @return bool
272
     */
273 1
    public function has($path)
274
    {
275
        try {
276 1
            return (bool) $this->getMetadata($path);
277 1
        } catch (RuntimeException $exception) {
278 1
            return false;
279
        }
280
    }
281
282
    /**
283
     * @param string $path
284
     *
285
     * @return array
286
     */
287
    public function read($path)
288
    {
289
        return ['contents' => file_get_contents($this->getUrl($path))];
290
    }
291
292
    /**
293
     * @param string $path
294
     *
295
     * @return array
296
     */
297
    public function readStream($path)
298
    {
299
        return ['stream' => fopen($this->getUrl($path), 'r')];
300
    }
301
302
    /**
303
     * @param string $directory
304
     * @param bool   $recursive
305
     *
306
     * @return array|bool
307
     */
308 1
    public function listContents($directory = '', $recursive = false)
309
    {
310 1
        return $this->normalizeResponse(
311 1
            Cosapi::listFolder($this->getBucket(), $directory)
312 1
        );
313
    }
314
315
    /**
316
     * @param string $path
317
     *
318
     * @return array|bool
319
     */
320 6
    public function getMetadata($path)
321
    {
322 6
        return $this->normalizeResponse(
323 6
            Cosapi::stat($this->getBucket(), $path)
324 6
        );
325
    }
326
327
    /**
328
     * @param string $path
329
     *
330
     * @return array|bool
331
     */
332 1
    public function getSize($path)
333
    {
334 1
        $stat = $this->getMetadata($path);
335
336
        if (isset($stat['filesize'])) {
337
            return ['size' => $stat['filesize']];
338
        }
339
340
        return false;
341
    }
342
343
    /**
344
     * @param string $path
345
     *
346
     * @return array|bool
347
     */
348 1
    public function getMimetype($path)
349
    {
350 1
        $stat = $this->getMetadata($path);
351
352
        if (isset($stat['custom_headers']['Content-Type'])) {
353
            return ['mimetype' => $stat['custom_headers']['Content-Type']];
354
        }
355
356
        return false;
357
    }
358
359
    /**
360
     * @param string $path
361
     *
362
     * @return array|bool
363
     */
364 1
    public function getTimestamp($path)
365
    {
366 1
        $stat = $this->getMetadata($path);
367
368
        if (isset($stat['ctime'])) {
369
            return ['timestamp' => $stat['ctime']];
370
        }
371
372
        return false;
373
    }
374
375
    /**
376
     * @param string $path
377
     *
378
     * @return array|bool
379
     */
380 1
    public function getVisibility($path)
381
    {
382 1
        $stat = $this->getMetadata($path);
383
384 View Code Duplication
        if (isset($stat['authority']) && $stat['authority'] === 'eWPrivateRPublic') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
385
            return ['visibility' => AdapterInterface::VISIBILITY_PUBLIC];
386
        }
387
388 View Code Duplication
        if (isset($stat['authority']) && $stat['authority'] === 'eWPrivateRPublic') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
389
            return ['visibility' => AdapterInterface::VISIBILITY_PRIVATE];
390
        }
391
392
        return false;
393
    }
394
395
    /**
396
     * @param $content
397
     *
398
     * @return string|bool
399
     */
400
    private function writeTempFile($content)
401
    {
402
        $tmpfname = tempnam('/tmp', 'dir');
403
404
        chmod($tmpfname, 0777);
405
406
        file_put_contents($tmpfname, $content);
407
408
        return $tmpfname;
409
    }
410
411
    /**
412
     * @param $tmpfname
413
     *
414
     * @return bool
415
     */
416
    private function deleteTempFile($tmpfname)
417
    {
418
        return unlink($tmpfname);
419
    }
420
421
    /**
422
     * @param $path
423
     * @param $content
424
     *
425
     * @return bool
426
     */
427
    protected function setContentType($path, $content)
428
    {
429
        $custom_headers = [
430
            'Content-Type' => Util::guessMimeType($path, $content),
431
        ];
432
433
        return $this->normalizeResponse(
434
            Cosapi::update($this->getBucket(), $path, null, null, $custom_headers)
435
        );
436
    }
437
438
    /**
439
     * @param $response
440
     *
441
     * @throws RuntimeException
442
     *
443
     * @return mixed
444
     */
445 15
    protected function normalizeResponse($response)
446
    {
447 15
        if ($response['code'] == 0) {
448
            return isset($response['data']) ? $response['data'] : true;
449
        }
450
451 15
        if ($this->debug) {
452 15
            throw new RuntimeException($response['message'], $response['code']);
453
        }
454
455
        return false;
456
    }
457
}
458