Completed
Push — master ( c5bf72...b82ad7 )
by frey
03:46
created

Adapter::getVisibility()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 10.8265

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 5
cts 13
cp 0.3846
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 10
nc 3
nop 1
crap 10.8265
1
<?php
2
3
namespace Freyo\Flysystem\QcloudCOSv5;
4
5
use League\Flysystem\Adapter\AbstractAdapter;
6
use League\Flysystem\AdapterInterface;
7
use League\Flysystem\Config;
8
use Qcloud\Cos\Client;
9
use Qcloud\Cos\Exception\NoSuchKeyException;
10
11
/**
12
 * Class Adapter.
13
 */
14
class Adapter extends AbstractAdapter
15
{
16
    /**
17
     * @var Client
18
     */
19
    protected $client;
20
21
    /**
22
     * @var array
23
     */
24
    protected $config = [];
25
26
    /**
27
     * @var array
28
     */
29
    protected $regionMap = [
30
        'cn-east'      => 'ap-shanghai',
31
        'cn-sorth'     => 'ap-guangzhou',
32
        'cn-north'     => 'ap-beijing-1',
33
        'cn-south-2'   => 'ap-guangzhou-2',
34
        'cn-southwest' => 'ap-chengdu',
35
        'sg'           => 'ap-singapore',
36
        'tj'           => 'ap-beijing-1',
37
        'bj'           => 'ap-beijing',
38
        'sh'           => 'ap-shanghai',
39
        'gz'           => 'ap-guangzhou',
40
        'cd'           => 'ap-chengdu',
41
        'sgp'          => 'ap-singapore',
42
    ];
43
44
    /**
45
     * Adapter constructor.
46
     *
47
     * @param Client $client
48
     * @param array  $config
49
     */
50
    public function __construct(Client $client, array $config)
51
    {
52
        $this->client = $client;
53
        $this->config = $config;
54
55
        $this->setPathPrefix($config['cdn']);
56
    }
57
58
    /**
59
     * @return string
60
     */
61 18
    public function getBucket()
62
    {
63 18
        return $this->config['bucket'];
64
    }
65
66
    /**
67
     * @return string
68
     */
69 2
    public function getAppId()
70
    {
71 2
        return $this->config['credentials']['appId'];
72
    }
73
74
    /**
75
     * @return string
76
     */
77 2
    public function getRegion()
78
    {
79 2
        return $this->regionMap[$this->config['region']];
80
    }
81
82
    /**
83
     * @param $path
84
     *
85
     * @return string
86
     */
87 2
    public function getSourcePath($path)
88
    {
89 2
        return sprintf('%s-%s.cos.%s.myqcloud.com/%s',
90 2
            $this->getBucket(), $this->getAppId(), $this->getRegion(), ltrim($path, '/')
91 2
        );
92
    }
93
94
    /**
95
     * @param string $path
96
     *
97
     * @return string
98
     */
99 3
    public function getUrl($path)
100 1
    {
101 2
        if (!empty($this->config['cdn'])) {
102 3
            return $this->applyPathPrefix($path);
103
        }
104
105
        return urldecode(
106
            $this->client->getObjectUrl($this->getBucket(), $path)
107
        );
108
    }
109
110
    /**
111
     * @param string $path
112
     * @param string $contents
113
     * @param Config $config
114
     *
115
     * @return array|bool
116
     */
117 2
    public function write($path, $contents, Config $config)
118
    {
119 2
        return $this->client->upload($this->getBucket(), $path, $contents);
120
    }
121
122
    /**
123
     * @param string   $path
124
     * @param resource $resource
125
     * @param Config   $config
126
     *
127
     * @return array|bool
128
     */
129 2
    public function writeStream($path, $resource, Config $config)
130
    {
131 2
        return $this->client->upload($this->getBucket(), $path, stream_get_contents($resource, -1, 0));
132
    }
133
134
    /**
135
     * @param string $path
136
     * @param string $contents
137
     * @param Config $config
138
     *
139
     * @return array|bool
140
     */
141 1
    public function update($path, $contents, Config $config)
142
    {
143 1
        return $this->write($path, $contents, $config);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->write($path, $contents, $config); of type array|boolean adds the type boolean to the return on line 143 which is incompatible with the return type declared by the interface League\Flysystem\AdapterInterface::update of type array|false.
Loading history...
144
    }
145
146
    /**
147
     * @param string   $path
148
     * @param resource $resource
149
     * @param Config   $config
150
     *
151
     * @return array|bool
152
     */
153 1
    public function updateStream($path, $resource, Config $config)
154
    {
155 1
        return $this->writeStream($path, $resource, $config);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->writeStream($path, $resource, $config); of type array|boolean adds the type boolean to the return on line 155 which is incompatible with the return type declared by the interface League\Flysystem\AdapterInterface::updateStream of type array|false.
Loading history...
156
    }
157
158
    /**
159
     * @param string $path
160
     * @param string $newpath
161
     *
162
     * @return bool
163
     */
164 1
    public function rename($path, $newpath)
165
    {
166 1
        $source = $this->getSourcePath($path);
167
168 1
        $response = $this->client->copyObject([
169 1
            'Bucket'     => $this->getBucket(),
170 1
            'Key'        => $newpath,
171 1
            'CopySource' => $source,
172 1
        ]);
173
174
        $this->delete($path);
175
176
        return (bool) $response;
177
    }
178
179
    /**
180
     * @param string $path
181
     * @param string $newpath
182
     *
183
     * @return bool
184
     */
185 1
    public function copy($path, $newpath)
186
    {
187 1
        $source = $this->getSourcePath($path);
188
189 1
        return (bool) $this->client->copyObject([
190 1
            'Bucket'     => $this->getBucket(),
191 1
            'Key'        => $newpath,
192 1
            'CopySource' => $source,
193 1
        ]);
194
    }
195
196
    /**
197
     * @param string $path
198
     *
199
     * @return bool
200
     */
201 1
    public function delete($path)
202
    {
203 1
        return (bool) $this->client->deleteObject([
204 1
            'Bucket' => $this->getBucket(),
205 1
            'Key'    => $path,
206 1
        ]);
207
    }
208
209
    /**
210
     * @param string $dirname
211
     *
212
     * @return bool
213
     */
214 1
    public function deleteDir($dirname)
215
    {
216 1
        $model = $this->listContents($dirname);
217
218
        $keys = array_map(function ($item) {
219
            return ['Key' => $item['Key']];
220
        }, (array) $model->get('Contents'));
221
222
        return (bool) $this->client->deleteObjects([
223
            'Bucket'  => $this->getBucket(),
224
            'Objects' => $keys,
225
        ]);
226
    }
227
228
    /**
229
     * @param string $dirname
230
     * @param Config $config
231
     *
232
     * @return array|bool
233
     */
234 1
    public function createDir($dirname, Config $config)
235
    {
236 1
        return $this->client->putObject([
237 1
            'Bucket' => $this->getBucket(),
238 1
            'Key'    => rtrim($dirname, '/').'/_blank',
239 1
            'Body'   => '',
240 1
        ]);
241
    }
242
243
    /**
244
     * @param string $path
245
     * @param string $visibility
246
     *
247
     * @return bool
248
     */
249 1
    public function setVisibility($path, $visibility)
250
    {
251 1
        $visibility = ($visibility === AdapterInterface::VISIBILITY_PUBLIC)
252 1
            ? 'public-read' : 'private';
253
254 1
        return (bool) $this->client->PutObjectAcl([
0 ignored issues
show
Bug Best Practice introduced by
The return type of return (bool) $this->cli...'ACL' => $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...
255 1
            'Bucket' => $this->getBucket(),
256 1
            'Key'    => $path,
257 1
            'ACL'    => $visibility,
258 1
        ]);
259
    }
260
261
    /**
262
     * @param string $path
263
     *
264
     * @return bool
265
     */
266 1
    public function has($path)
267
    {
268
        try {
269 1
            return (bool) $this->getMetadata($path);
270 1
        } catch (NoSuchKeyException $e) {
271
            return false;
272
        }
273
    }
274
275
    /**
276
     * @param string $path
277
     *
278
     * @return array|bool
279
     */
280 1
    public function read($path)
281
    {
282
        try {
283 1
            $response = $this->client->getObject([
0 ignored issues
show
Bug introduced by
The method getObject() does not exist on Qcloud\Cos\Client. Did you maybe mean getObjectUrl()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
284 1
                'Bucket' => $this->getBucket(),
285 1
                'Key'    => $path,
286 1
            ]);
287
288
            return ['contents' => (string) $response->get('Body')];
289 1
        } catch (NoSuchKeyException $e) {
290
            return false;
291
        }
292
    }
293
294
    /**
295
     * @param string $path
296
     *
297
     * @return array|bool
298
     */
299 1
    public function readStream($path)
300
    {
301
        try {
302 1
            return ['stream' => fopen($this->getUrl($path), 'r')];
303
        } catch (NoSuchKeyException $e) {
304
            return false;
305
        }
306
    }
307
308
    /**
309
     * @param string $directory
310
     * @param bool   $recursive
311
     *
312
     * @return array|bool
313
     */
314 2
    public function listContents($directory = '', $recursive = false)
315
    {
316 2
        return $this->client->listObjects([
317 2
            'Bucket' => $this->getBucket(),
318 2
            'Prefix' => $directory,
319 2
        ]);
320
    }
321
322
    /**
323
     * @param string $path
324
     *
325
     * @return array|bool
326
     */
327 5
    public function getMetadata($path)
328
    {
329 5
        return $this->client->headObject([
330 5
            'Bucket' => $this->getBucket(),
331 5
            'Key'    => $path,
332 5
        ]);
333
    }
334
335
    /**
336
     * @param string $path
337
     *
338
     * @return array|bool
339
     */
340 1
    public function getSize($path)
341
    {
342 1
        $meta = $this->getMetadata($path);
343
344
        return $meta->hasKey('ContentLength')
0 ignored issues
show
Bug introduced by
The method hasKey cannot be called on $meta (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
345
            ? ['size' => $meta->get('ContentLength')] : false;
0 ignored issues
show
Bug introduced by
The method get cannot be called on $meta (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
346
    }
347
348
    /**
349
     * @param string $path
350
     *
351
     * @return array|bool
352
     */
353 1
    public function getMimetype($path)
354
    {
355 1
        $meta = $this->getMetadata($path);
356
357
        return $meta->hasKey('ContentType')
0 ignored issues
show
Bug introduced by
The method hasKey cannot be called on $meta (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
358
            ? ['mimetype' => $meta->get('ContentType')] : false;
0 ignored issues
show
Bug introduced by
The method get cannot be called on $meta (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
359
    }
360
361
    /**
362
     * @param string $path
363
     *
364
     * @return array|bool
365
     */
366 1
    public function getTimestamp($path)
367
    {
368 1
        $meta = $this->getMetadata($path);
369
370
        return $meta->hasKey('LastModified')
0 ignored issues
show
Bug introduced by
The method hasKey cannot be called on $meta (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
371
            ? ['timestamp' => strtotime($meta->get('LastModified'))] : false;
0 ignored issues
show
Bug introduced by
The method get cannot be called on $meta (of type array|boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
372
    }
373
374
    /**
375
     * @param string $path
376
     *
377
     * @return array|bool
378
     */
379 1
    public function getVisibility($path)
380
    {
381 1
        $meta = $this->client->getObjectAcl([
382 1
            'Bucket' => $this->getBucket(),
383 1
            'Key'    => $path,
384 1
        ]);
385
386
        foreach ($meta->get('Grants') as $grant) {
387
            if (isset($grant['Grantee']['URI'])
388
                && $grant['Permission'] === 'READ'
389
                && strpos($grant['Grantee']['URI'], 'global/AllUsers') !== false
390
            ) {
391
                return ['visibility' => AdapterInterface::VISIBILITY_PUBLIC];
392
            }
393
        }
394
395
        return ['visibility' => AdapterInterface::VISIBILITY_PRIVATE];
396
    }
397
}
398