Test Failed
Push — master ( 065091...7ed8aa )
by frey
02:25
created

Adapter::updateStream()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15

Duplication

Lines 15
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 15
loc 15
ccs 0
cts 11
cp 0
rs 9.7666
c 0
b 0
f 0
cc 2
nc 2
nop 3
crap 6
1
<?php
2
3
namespace Freyo\Flysystem\QcloudCOSv4;
4
5
use Carbon\Carbon;
6
use DateTimeInterface;
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
use QCloud\Cos\Api;
13
14
/**
15
 * Class Adapter.
16
 */
17
class Adapter extends AbstractAdapter
18
{
19
    /**
20
     * @var Api
21
     */
22
    protected $cosApi;
23
24
    /**
25
     * @var string
26
     */
27
    protected $bucket;
28
29
    /**
30
     * @var bool
31
     */
32
    protected $debug;
33
34
    /**
35
     * Adapter constructor.
36
     *
37
     * @param Api   $cosApi
38
     * @param array $config
39
     */
40
    public function __construct(Api $cosApi, array $config)
41
    {
42
        $this->cosApi = $cosApi;
43
44
        $this->bucket = $config['bucket'];
45
        $this->debug = $config['debug'];
46
47
        $this->setPathPrefix($config['protocol'].'://'.$config['domain'].'/');
48
    }
49
50
    /**
51
     * @return string
52
     */
53
    public function getBucket()
54
    {
55
        return $this->bucket;
56
    }
57
58
    /**
59
     * @param string $path
60
     *
61
     * @return string
62
     */
63
    public function getUrl($path)
64
    {
65
        return $this->applyPathPrefix($path);
66
    }
67
68
    /**
69
     * @param string             $path
70
     * @param \DateTimeInterface $expiration
71
     * @param array              $options
72
     *
73
     * @return string|bool
74
     */
75
    public function getTemporaryUrl($path, DateTimeInterface $expiration, array $options = [])
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
76
    {
77
        $response = $this->cosApi->getDownloadUrl(
78
            $this->getBucket(), $path, Carbon::now()->diffInSeconds($expiration)
79
        );
80
81
        $response = $this->normalizeResponse($response);
82
        
83
        if (false !== $response) {
84
            $url = parse_url($response['access_url']);
85
            return $this->getUrl($url['path']).'?'.$url['query'];
86
        }
87
88
        return $response;
89
    }
90
91
    /**
92
     * @param string $path
93
     * @param string $contents
94
     * @param Config $config
95
     *
96
     * @throws RuntimeException
97
     *
98
     * @return array|bool
99
     */
100 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...
101
    {
102
        $temporaryPath = $this->createTemporaryFile($contents);
103
104
        $response = $this->cosApi->upload($this->getBucket(), $temporaryPath, $path,
0 ignored issues
show
Bug introduced by
It seems like $temporaryPath defined by $this->createTemporaryFile($contents) on line 102 can also be of type boolean; however, QCloud\Cos\Api::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...
105
            null, null, $config->get('insertOnly', 1));
106
107
        $response = $this->normalizeResponse($response);
108
109
        if (false !== $response) {
110
            $this->setContentType($path, $contents);
111
        }
112
113
        return $response;
114
    }
115
116
    /**
117
     * @param string   $path
118
     * @param resource $resource
119
     * @param Config   $config
120
     *
121
     * @throws RuntimeException
122
     *
123
     * @return array|bool
124
     */
125 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...
126
    {
127
        $uri = stream_get_meta_data($resource)['uri'];
128
129
        $response = $this->cosApi->upload($this->getBucket(), $uri, $path,
130
            null, null, $config->get('insertOnly', 1));
131
132
        $response = $this->normalizeResponse($response);
133
134
        if (false !== $response) {
135
            $this->setContentType($path, stream_get_contents($resource));
136
        }
137
138
        return $response;
139
    }
140
141
    /**
142
     * @param string $path
143
     * @param string $contents
144
     * @param Config $config
145
     *
146
     * @throws RuntimeException
147
     *
148
     * @return array|bool
149
     */
150 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...
151
    {
152
        $temporaryPath = $this->createTemporaryFile($contents);
153
154
        $response = $this->cosApi->upload($this->getBucket(), $temporaryPath, $path,
0 ignored issues
show
Bug introduced by
It seems like $temporaryPath defined by $this->createTemporaryFile($contents) on line 152 can also be of type boolean; however, QCloud\Cos\Api::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...
155
            null, null, $config->get('insertOnly', 0));
156
157
        $response = $this->normalizeResponse($response);
158
159
        if (false !== $response) {
160
            $this->setContentType($path, $contents);
161
        }
162
163
        return $response;
164
    }
165
166
    /**
167
     * @param string   $path
168
     * @param resource $resource
169
     * @param Config   $config
170
     *
171
     * @throws RuntimeException
172
     *
173
     * @return array|bool
174
     */
175 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...
176
    {
177
        $uri = stream_get_meta_data($resource)['uri'];
178
179
        $response = $this->cosApi->upload($this->getBucket(), $uri, $path,
180
            null, null, $config->get('insertOnly', 0));
181
182
        $response = $this->normalizeResponse($response);
183
184
        if (false !== $response) {
185
            $this->setContentType($path, stream_get_contents($resource));
186
        }
187
188
        return $response;
189
    }
190
191
    /**
192
     * @param string $path
193
     * @param string $newpath
194
     *
195
     * @return bool
196
     */
197
    public function rename($path, $newpath)
198
    {
199
        return (bool) $this->normalizeResponse(
200
            $this->cosApi->moveFile($this->getBucket(), $path, $newpath, true)
201
        );
202
    }
203
204
    /**
205
     * @param string $path
206
     * @param string $newpath
207
     *
208
     * @return bool
209
     */
210
    public function copy($path, $newpath)
211
    {
212
        return (bool) $this->normalizeResponse(
213
            $this->cosApi->copyFile($this->getBucket(), $path, $newpath, true)
214
        );
215
    }
216
217
    /**
218
     * @param string $path
219
     *
220
     * @return bool
221
     */
222
    public function delete($path)
223
    {
224
        return (bool) $this->normalizeResponse(
225
            $this->cosApi->delFile($this->getBucket(), $path)
226
        );
227
    }
228
229
    /**
230
     * @param string $dirname
231
     *
232
     * @return bool
233
     */
234
    public function deleteDir($dirname)
235
    {
236
        return (bool) $this->normalizeResponse(
237
            $this->cosApi->delFolder($this->getBucket(), $dirname)
238
        );
239
    }
240
241
    /**
242
     * @param string $dirname
243
     * @param Config $config
244
     *
245
     * @return array|bool
246
     */
247
    public function createDir($dirname, Config $config)
248
    {
249
        return $this->normalizeResponse(
250
            $this->cosApi->createFolder($this->getBucket(), $dirname)
251
        );
252
    }
253
254
    /**
255
     * @param string $path
256
     * @param string $visibility
257
     *
258
     * @return bool
259
     */
260
    public function setVisibility($path, $visibility)
261
    {
262
        $visibility = ($visibility === AdapterInterface::VISIBILITY_PUBLIC)
263
            ? 'eWPrivateRPublic' : 'eWRPrivate';
264
265
        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...
266
            $this->cosApi->update($this->getBucket(), $path, null, $visibility)
267
        );
268
    }
269
270
    /**
271
     * @param string $path
272
     *
273
     * @return bool
274
     */
275
    public function has($path)
276
    {
277
        try {
278
            return (bool) $this->getMetadata($path);
279
        } catch (RuntimeException $exception) {
280
            return false;
281
        }
282
    }
283
284
    /**
285
     * @param string $path
286
     *
287
     * @return array|bool
288
     */
289
    public function read($path)
290
    {
291
        $contents = file_get_contents($this->applyPathPrefix($path));
292
293
        return $contents !== false ? compact('contents') : false;
294
    }
295
296
    /**
297
     * @param string $path
298
     *
299
     * @return array|bool
300
     */
301
    public function readStream($path)
302
    {
303
        $stream = fopen($this->applyPathPrefix($path), 'r');
304
305
        return $stream !== false ? compact('stream') : false;
306
    }
307
308
    /**
309
     * @param string $directory
310
     * @param bool   $recursive
311
     *
312
     * @return array|bool
313
     */
314
    public function listContents($directory = '', $recursive = false)
315
    {
316
        return $this->normalizeResponse(
317
            $this->cosApi->listFolder($this->getBucket(), $directory)
318
        );
319
    }
320
321
    /**
322
     * @param string $path
323
     *
324
     * @return array|bool
325
     */
326
    public function getMetadata($path)
327
    {
328
        return $this->normalizeResponse(
329
            $this->cosApi->stat($this->getBucket(), $path)
330
        );
331
    }
332
333
    /**
334
     * @param string $path
335
     *
336
     * @return array|bool
337
     */
338
    public function getSize($path)
339
    {
340
        $stat = $this->getMetadata($path);
341
342
        return isset($stat['filesize']) ? ['size' => $stat['filesize']] : false;
343
    }
344
345
    /**
346
     * @param string $path
347
     *
348
     * @return array|bool
349
     */
350
    public function getMimetype($path)
351
    {
352
        $stat = $this->getMetadata($path);
353
354
        return isset($stat['custom_headers']['Content-Type'])
355
            ? ['mimetype' => $stat['custom_headers']['Content-Type']] : false;
356
    }
357
358
    /**
359
     * @param string $path
360
     *
361
     * @return array|bool
362
     */
363
    public function getTimestamp($path)
364
    {
365
        $stat = $this->getMetadata($path);
366
367
        return isset($stat['ctime']) ? ['timestamp' => $stat['ctime']] : false;
368
    }
369
370
    /**
371
     * @param string $path
372
     *
373
     * @return array|bool
374
     */
375
    public function getVisibility($path)
376
    {
377
        $stat = $this->getMetadata($path);
378
379 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...
380
            return ['visibility' => AdapterInterface::VISIBILITY_PUBLIC];
381
        }
382
383 View Code Duplication
        if (isset($stat['authority']) && $stat['authority'] === 'eWRPrivate') {
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...
384
            return ['visibility' => AdapterInterface::VISIBILITY_PRIVATE];
385
        }
386
387
        return false;
388
    }
389
390
    /**
391
     * Creates a temporary file.
392
     *
393
     * @param string $content
394
     *
395
     * @throws RuntimeException
396
     *
397
     * @return string
398
     */
399
    protected function createTemporaryFile($content)
400
    {
401
        $temporaryPath = $this->getTemporaryPath();
402
403
        if (false === $temporaryPath) {
404
            throw new RuntimeException("Unable to create temporary file in '{$temporaryPath}'.");
405
        }
406
407
        file_put_contents($temporaryPath, $content);
408
409
        // The file is automatically removed when closed, or when the script ends.
410
        register_shutdown_function(function () use ($temporaryPath) {
411
            unlink($temporaryPath);
412
        });
413
414
        return $temporaryPath;
415
    }
416
417
    /**
418
     * Gets a temporary file path.
419
     *
420
     * @return bool|string
421
     */
422
    protected function getTemporaryPath()
423
    {
424
        return tempnam(sys_get_temp_dir(), uniqid('tencentyun', true));
425
    }
426
427
    /**
428
     * @param string $path
429
     * @param string $content
430
     *
431
     * @return bool
432
     */
433
    protected function setContentType($path, $content)
434
    {
435
        $custom_headers = [
436
            'Content-Type' => Util::guessMimeType($path, $content),
437
        ];
438
439
        return $this->normalizeResponse(
440
            $this->cosApi->update($this->getBucket(), $path, null, null, $custom_headers)
441
        );
442
    }
443
444
    /**
445
     * @param $response
446
     *
447
     * @throws RuntimeException
448
     *
449
     * @return mixed
450
     */
451
    protected function normalizeResponse($response)
452
    {
453
        if ($response['code'] == 0) {
454
            return isset($response['data']) ? $response['data'] : true;
455
        }
456
457
        if ($this->debug) {
458
            throw new RuntimeException($response['message'], $response['code']);
459
        }
460
461
        return false;
462
    }
463
}
464