Completed
Push — master ( 31b048...ee86b6 )
by frey
9s
created

Adapter::write()   B

Complexity

Conditions 3
Paths 7

Size

Total Lines 24
Code Lines 13

Duplication

Lines 24
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
eloc 13
nc 7
nop 3
dl 24
loc 24
rs 8.9713
c 0
b 0
f 0
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
     * Adapter constructor.
25
     *
26
     * @param $config
27
     */
28
    public function __construct($config)
29
    {
30
        Conf::setAppId($config['app_id']);
31
        Conf::setSecretId($config['secret_id']);
32
        Conf::setSecretKey($config['secret_key']);
33
34
        $this->bucket = $config['bucket'];
35
36
        $this->setPathPrefix($config['protocol'].'://'.$config['domain'].'/');
37
38
        Cosapi::setTimeout($config['timeout']);
39
    }
40
41
    /**
42
     * @return mixed
43
     */
44
    public function getBucket()
45
    {
46
        return $this->bucket;
47
    }
48
49
    /**
50
     * @param $path
51
     *
52
     * @return string
53
     */
54
    public function getUrl($path)
55
    {
56
        return $this->applyPathPrefix($path);
57
    }
58
59
    /**
60
     * @param string $path
61
     * @param string $contents
62
     * @param Config $config
63
     *
64
     * @throws RuntimeException
65
     *
66
     * @return bool
67
     */
68 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...
69
    {
70
        $tmpfname = $this->writeTempFile($contents);
71
72
        try {
73
            $response = $this->normalizeResponse(
74
                Cosapi::upload($this->getBucket(), $tmpfname, $path)
0 ignored issues
show
Bug introduced by
It seems like $tmpfname defined by $this->writeTempFile($contents) on line 70 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...
75
            );
76
77
            $this->deleteTempFile($tmpfname);
78
79
            $this->setContentType($path, $contents);
80
        } catch (RuntimeException $exception) {
81
            $this->deleteTempFile($tmpfname);
82
83
            if ($exception->getCode() == -4018) {
84
                return $this->getMetadata($path);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getMetadata($path); (boolean) is incompatible with the return type declared by the interface League\Flysystem\AdapterInterface::write 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...
85
            }
86
87
            throw $exception;
88
        }
89
90
        return $response;
91
    }
92
93
    /**
94
     * @param string   $path
95
     * @param resource $resource
96
     * @param Config   $config
97
     *
98
     * @return bool
99
     */
100
    public function writeStream($path, $resource, Config $config)
101
    {
102
        $uri = stream_get_meta_data($resource)['uri'];
103
104
        try {
105
            $response = $this->normalizeResponse(
106
                Cosapi::upload($this->getBucket(), $uri, $path)
107
            );
108
109
            $this->setContentType($path, stream_get_contents($resource));
110
        } catch (RuntimeException $exception) {
111
            if ($exception->getCode() == -4018) {
112
                return $this->getMetadata($path);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getMetadata($path); (boolean) is incompatible with the return type declared by the interface League\Flysystem\AdapterInterface::writeStream 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...
113
            }
114
115
            throw $exception;
116
        }
117
118
        return $response;
119
    }
120
121
    /**
122
     * @param string $path
123
     * @param string $contents
124
     * @param Config $config
125
     *
126
     * @return bool
127
     */
128 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...
129
    {
130
        $tmpfname = $this->writeTempFile($contents);
131
132
        try {
133
            $response = $this->normalizeResponse(
134
                Cosapi::upload($this->getBucket(), $tmpfname, $path, null, null, 0)
0 ignored issues
show
Bug introduced by
It seems like $tmpfname defined by $this->writeTempFile($contents) on line 130 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...
135
            );
136
137
            $this->deleteTempFile($tmpfname);
138
139
            $this->setContentType($path, $contents);
140
        } catch (RuntimeException $exception) {
141
            $this->deleteTempFile($tmpfname);
142
143
            if ($exception->getCode() == -4018) {
144
                return $this->getMetadata($path);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getMetadata($path); (boolean) is incompatible with the return type declared by the interface League\Flysystem\AdapterInterface::update 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...
145
            }
146
147
            throw $exception;
148
        }
149
150
        return $response;
151
    }
152
153
    /**
154
     * @param string   $path
155
     * @param resource $resource
156
     * @param Config   $config
157
     *
158
     * @return bool
159
     */
160
    public function updateStream($path, $resource, Config $config)
161
    {
162
        $uri = stream_get_meta_data($resource)['uri'];
163
164
        try {
165
            $response = $this->normalizeResponse(
166
                Cosapi::upload($this->getBucket(), $uri, $path, $path, null, null, 0)
0 ignored issues
show
Unused Code introduced by
The call to Cosapi::upload() has too many arguments starting with 0.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
167
            );
168
169
            $this->setContentType($path, stream_get_contents($resource));
170
        } catch (RuntimeException $exception) {
171
            if ($exception->getCode() == -4018) {
172
                return $this->getMetadata($path);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getMetadata($path); (boolean) is incompatible with the return type declared by the interface League\Flysystem\AdapterInterface::updateStream 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...
173
            }
174
175
            throw $exception;
176
        }
177
178
        return $response;
179
    }
180
181
    /**
182
     * @param string $path
183
     * @param string $newpath
184
     *
185
     * @return bool
186
     */
187
    public function rename($path, $newpath)
188
    {
189
        return $this->normalizeResponse(
190
            Cosapi::move($this->getBucket(), $path, $newpath, 1)
191
        );
192
    }
193
194
    /**
195
     * @param string $path
196
     * @param string $newpath
197
     *
198
     * @return bool
199
     */
200
    public function copy($path, $newpath)
201
    {
202
        $resource = $this->read($path);
203
204
        return $this->update($newpath, $resource['contents'], new Config());
205
    }
206
207
    /**
208
     * @param string $path
209
     *
210
     * @return bool
211
     */
212
    public function delete($path)
213
    {
214
        return $this->normalizeResponse(
215
            Cosapi::delFile($this->getBucket(), $path)
216
        );
217
    }
218
219
    /**
220
     * @param string $dirname
221
     *
222
     * @return bool
223
     */
224
    public function deleteDir($dirname)
225
    {
226
        return $this->normalizeResponse(
227
            Cosapi::delFolder($this->getBucket(), $dirname)
228
        );
229
    }
230
231
    /**
232
     * @param string $dirname
233
     * @param Config $config
234
     *
235
     * @return bool
236
     */
237
    public function createDir($dirname, Config $config)
238
    {
239
        return $this->normalizeResponse(
240
            Cosapi::createFolder($this->getBucket(), $dirname)
241
        );
242
    }
243
244
    /**
245
     * @param string $path
246
     * @param string $visibility
247
     *
248
     * @return mixed
249
     */
250
    public function setVisibility($path, $visibility)
251
    {
252
        $visibility = $visibility === AdapterInterface::VISIBILITY_PUBLIC ? 'eWPrivateRPublic' : 'eWRPrivate';
253
254
        return $this->normalizeResponse(
255
            Cosapi::update($this->getBucket(), $path, null, $visibility)
256
        );
257
    }
258
259
    /**
260
     * @param string $path
261
     *
262
     * @return bool
263
     */
264
    public function has($path)
265
    {
266
        try {
267
            $this->getMetadata($path);
268
        } catch (RuntimeException $exception) {
269
            return false;
270
        }
271
272
        return true;
273
    }
274
275
    /**
276
     * @param string $path
277
     *
278
     * @return array
279
     */
280
    public function read($path)
281
    {
282
        return ['contents' => file_get_contents($this->getUrl($path))];
283
    }
284
285
    /**
286
     * @param string $path
287
     *
288
     * @return array
289
     */
290
    public function readStream($path)
291
    {
292
        return ['stream' => fopen($this->getUrl($path), 'r')];
293
    }
294
295
    /**
296
     * @param string $directory
297
     * @param bool   $recursive
298
     *
299
     * @return bool
300
     */
301
    public function listContents($directory = '', $recursive = false)
302
    {
303
        return $this->normalizeResponse(
304
            Cosapi::listFolder($this->getBucket(), $directory)
305
        );
306
    }
307
308
    /**
309
     * @param string $path
310
     *
311
     * @return bool
312
     */
313
    public function getMetadata($path)
314
    {
315
        return $this->normalizeResponse(
316
            Cosapi::stat($this->getBucket(), $path)
317
        );
318
    }
319
320
    /**
321
     * @param string $path
322
     *
323
     * @return array
324
     */
325
    public function getSize($path)
326
    {
327
        $stat = $this->getMetadata($path);
328
329
        if ($stat) {
330
            return ['size' => $stat['filesize']];
331
        }
332
333
        return ['size' => 0];
334
    }
335
336
    /**
337
     * @param string $path
338
     *
339
     * @return array
340
     */
341
    public function getMimetype($path)
342
    {
343
        $stat = $this->getMetadata($path);
344
345
        if ($stat && !empty($stat['custom_headers']) && !empty($stat['custom_headers']['Content-Type'])) {
346
            return ['mimetype' => $stat['custom_headers']['Content-Type']];
347
        }
348
349
        return ['mimetype' => ''];
350
    }
351
352
    /**
353
     * @param string $path
354
     *
355
     * @return array
356
     */
357
    public function getTimestamp($path)
358
    {
359
        $stat = $this->getMetadata($path);
360
361
        if ($stat) {
362
            return ['timestamp' => $stat['ctime']];
363
        }
364
365
        return ['timestamp' => 0];
366
    }
367
368
    /**
369
     * @param string $path
370
     *
371
     * @return array
372
     */
373
    public function getVisibility($path)
374
    {
375
        $stat = $this->getMetadata($path);
376
377
        $visibility = AdapterInterface::VISIBILITY_PRIVATE;
378
379
        if ($stat && $stat['authority'] === 'eWPrivateRPublic') {
380
            $visibility = AdapterInterface::VISIBILITY_PUBLIC;
381
        }
382
383
        return ['visibility' => $visibility];
384
    }
385
386
    /**
387
     * @param $content
388
     *
389
     * @return bool|string
390
     */
391
    private function writeTempFile($content)
392
    {
393
        $tmpfname = tempnam('/tmp', 'dir');
394
395
        chmod($tmpfname, 0777);
396
397
        file_put_contents($tmpfname, $content);
398
399
        return $tmpfname;
400
    }
401
402
    /**
403
     * @param $tmpfname
404
     */
405
    private function deleteTempFile($tmpfname)
406
    {
407
        return unlink($tmpfname);
408
    }
409
410
    /**
411
     * @param $path
412
     * @param $content
413
     *
414
     * @return bool
415
     */
416
    protected function setContentType($path, $content)
417
    {
418
        $custom_headers = [
419
            'Content-Type' => Util::guessMimeType($path, $content),
420
        ];
421
422
        return $this->normalizeResponse(
423
            Cosapi::update($this->getBucket(), $path, null, null, $custom_headers)
424
        );
425
    }
426
427
    /**
428
     * @param $response
429
     *
430
     * @throws RuntimeException
431
     *
432
     * @return mixed
433
     */
434
    protected function normalizeResponse($response)
435
    {
436
        $response = is_array($response) ? $response : json_decode($response, true);
437
438
        if ($response['code'] == 0) {
439
            return isset($response['data']) ? $response['data'] : true;
440
        }
441
442
        throw new RuntimeException($response['message'], $response['code']);
443
    }
444
}
445