Completed
Push — master ( 6432aa...f9b2ca )
by frey
05:24
created

Adapter::copy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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