Completed
Push — master ( 57c4d8...bcb451 )
by frey
02:39
created

Adapter::createTemporaryFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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