Completed
Push — master ( da4c49...86da91 )
by frey
03:22
created

Adapter::setVisibility()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 2
crap 2
1
<?php
2
3
namespace Freyo\Flysystem\QcloudCOSv5;
4
5
use Carbon\Carbon;
6
use DateTimeInterface;
7
use League\Flysystem\Adapter\AbstractAdapter;
8
use League\Flysystem\AdapterInterface;
9
use League\Flysystem\Config;
10
use Qcloud\Cos\Client;
11
use Qcloud\Cos\Exception\NoSuchKeyException;
12
13
/**
14
 * Class Adapter.
15
 */
16
class Adapter extends AbstractAdapter
17
{
18
    /**
19
     * @var Client
20
     */
21
    protected $client;
22
23
    /**
24
     * @var array
25
     */
26
    protected $config = [];
27
28
    /**
29
     * @var array
30
     */
31
    protected $regionMap = [
32
        'cn-east'      => 'ap-shanghai',
33
        'cn-sorth'     => 'ap-guangzhou',
34
        'cn-north'     => 'ap-beijing-1',
35
        'cn-south-2'   => 'ap-guangzhou-2',
36
        'cn-southwest' => 'ap-chengdu',
37
        'sg'           => 'ap-singapore',
38
        'tj'           => 'ap-beijing-1',
39
        'bj'           => 'ap-beijing',
40
        'sh'           => 'ap-shanghai',
41
        'gz'           => 'ap-guangzhou',
42
        'cd'           => 'ap-chengdu',
43
        'sgp'          => 'ap-singapore',
44
    ];
45
46
    /**
47
     * Adapter constructor.
48
     *
49
     * @param Client $client
50
     * @param array  $config
51
     */
52
    public function __construct(Client $client, array $config)
53
    {
54
        $this->client = $client;
55
        $this->config = $config;
56
57
        $this->setPathPrefix($config['cdn']);
58
    }
59
60
    /**
61
     * @return string
62
     */
63 18
    public function getBucket()
64
    {
65 18
        return $this->config['bucket'];
66
    }
67
68
    /**
69
     * @return string
70
     */
71 2
    public function getAppId()
72
    {
73 2
        return $this->config['credentials']['appId'];
74
    }
75
76
    /**
77
     * @return string
78
     */
79 2
    public function getRegion()
80
    {
81 2
        return $this->regionMap[$this->config['region']];
82
    }
83
84
    /**
85
     * @param $path
86
     *
87
     * @return string
88
     */
89 2
    public function getSourcePath($path)
90
    {
91 2
        return sprintf('%s-%s.cos.%s.myqcloud.com/%s',
92 2
            $this->getBucket(), $this->getAppId(), $this->getRegion(), $path
93 2
        );
94
    }
95
96
    /**
97
     * @param string $path
98
     *
99
     * @return string
100
     */
101 6
    public function getUrl($path)
102 1
    {
103 2
        if (!empty($this->config['cdn'])) {
104 6
            return $this->applyPathPrefix($path);
105
        }
106
107
        return urldecode(
108
            $this->client->getObjectUrl($this->getBucket(), $path)
109
        );
110 4
    }
111
112
    /**
113
     * @param  string             $path
114
     * @param  \DateTimeInterface $expiration
115
     * @param  array              $options
116
     *
117
     * @return string
118
     */
119
    public function getTemporaryUrl($path, DateTimeInterface $expiration, array $options = [])
120
    {
121
        return urldecode(
122
            $this->client->getObjectUrl(
123
                $this->getBucket(), $path, $expiration->format('Y-m-d H:i:s'), $options
124
            )
125
        );
126
    }
127
128
    /**
129
     * @param string $path
130
     * @param string $contents
131
     * @param Config $config
132
     *
133
     * @return array|bool
134
     */
135 2
    public function write($path, $contents, Config $config)
136
    {
137 2
        return $this->client->upload($this->getBucket(), $path, $contents);
138
    }
139
140
    /**
141
     * @param string   $path
142
     * @param resource $resource
143
     * @param Config   $config
144
     *
145
     * @return array|bool
146
     */
147 2
    public function writeStream($path, $resource, Config $config)
148
    {
149 2
        return $this->client->upload($this->getBucket(), $path, stream_get_contents($resource, -1, 0));
150
    }
151
152
    /**
153
     * @param string $path
154
     * @param string $contents
155
     * @param Config $config
156
     *
157
     * @return array|bool
158
     */
159 1
    public function update($path, $contents, Config $config)
160
    {
161 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 161 which is incompatible with the return type declared by the interface League\Flysystem\AdapterInterface::update of type array|false.
Loading history...
162
    }
163
164
    /**
165
     * @param string   $path
166
     * @param resource $resource
167
     * @param Config   $config
168
     *
169
     * @return array|bool
170
     */
171 1
    public function updateStream($path, $resource, Config $config)
172
    {
173 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 173 which is incompatible with the return type declared by the interface League\Flysystem\AdapterInterface::updateStream of type array|false.
Loading history...
174
    }
175
176
    /**
177
     * @param string $path
178
     * @param string $newpath
179
     *
180
     * @return bool
181
     */
182 1
    public function rename($path, $newpath)
183
    {
184 1
        $source = $this->getSourcePath($path);
185
186 1
        $response = $this->client->copyObject([
187 1
            'Bucket'     => $this->getBucket(),
188 1
            'Key'        => $newpath,
189 1
            'CopySource' => $source,
190 1
        ]);
191
192
        $this->delete($path);
193
194
        return (bool)$response;
195
    }
196
197
    /**
198
     * @param string $path
199
     * @param string $newpath
200
     *
201
     * @return bool
202
     */
203 1
    public function copy($path, $newpath)
204
    {
205 1
        $source = $this->getSourcePath($path);
206
207 1
        return (bool)$this->client->copyObject([
208 1
            'Bucket'     => $this->getBucket(),
209 1
            'Key'        => $newpath,
210 1
            'CopySource' => $source,
211 1
        ]);
212
    }
213
214
    /**
215
     * @param string $path
216
     *
217
     * @return bool
218
     */
219 1
    public function delete($path)
220
    {
221 1
        return (bool)$this->client->deleteObject([
222 1
            'Bucket' => $this->getBucket(),
223 1
            'Key'    => $path,
224 1
        ]);
225
    }
226
227
    /**
228
     * @param string $dirname
229
     *
230
     * @return bool
231
     */
232 1
    public function deleteDir($dirname)
233
    {
234 1
        $response = $this->listContents($dirname);
235
236
        $keys = array_map(function ($item) {
237
            return ['Key' => $item['Key']];
238
        }, (array)$response['Contents']);
239
240
        return (bool)$this->client->deleteObjects([
241
            'Bucket'  => $this->getBucket(),
242
            'Objects' => $keys,
243
        ]);
244
    }
245
246
    /**
247
     * @param string $dirname
248
     * @param Config $config
249
     *
250
     * @return array|bool
251
     */
252 1
    public function createDir($dirname, Config $config)
253
    {
254 1
        return $this->client->putObject([
255 1
            'Bucket' => $this->getBucket(),
256 1
            'Key'    => $dirname . '/_blank',
257 1
            'Body'   => '',
258 1
        ]);
259
    }
260
261
    /**
262
     * @param string $path
263
     * @param string $visibility
264
     *
265
     * @return bool
266
     */
267 1
    public function setVisibility($path, $visibility)
268
    {
269 1
        $visibility = ($visibility === AdapterInterface::VISIBILITY_PUBLIC)
270 1
            ? 'public-read' : 'private';
271
272 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...
273 1
            'Bucket' => $this->getBucket(),
274 1
            'Key'    => $path,
275 1
            'ACL'    => $visibility,
276 1
        ]);
277
    }
278
279
    /**
280
     * @param string $path
281
     *
282
     * @return bool
283
     */
284 1
    public function has($path)
285
    {
286
        try {
287 1
            return (bool)$this->getMetadata($path);
288 1
        } catch (NoSuchKeyException $e) {
289
            return false;
290
        }
291
    }
292
293
    /**
294
     * @param string $path
295
     *
296
     * @return array|bool
297
     */
298 1
    public function read($path)
299
    {
300
        try {
301 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...
302 1
                'Bucket' => $this->getBucket(),
303 1
                'Key'    => $path,
304 1
            ]);
305
306
            return ['contents' => (string)$response->get('Body')];
307 1
        } catch (NoSuchKeyException $e) {
308
            return false;
309
        }
310
    }
311
312
    /**
313
     * @param string $path
314
     *
315
     * @return array|bool
316
     */
317 1
    public function readStream($path)
318
    {
319
        try {
320 1
            return ['stream' => fopen($this->getUrl($path), 'rb', false)];
321
        } catch (NoSuchKeyException $e) {
322
            return false;
323
        }
324
    }
325
326
    /**
327
     * @param string $directory
328
     * @param bool   $recursive
329
     *
330
     * @return array|bool
331
     */
332 2
    public function listContents($directory = '', $recursive = false)
333
    {
334 2
        $list = [];
335
336 2
        $response = $this->client->listObjects([
337 2
            'Bucket'    => $this->getBucket(),
338 2
            'Prefix'    => $directory,
339 2
            'Delimiter' => $recursive ? '' : '/',
340 2
        ]);
341
342
        foreach ((array)$response->get('Contents') as $content) {
343
            $list[] = $this->normalizeFileInfo($content);
344
        }
345
346
        return $list;
347
    }
348
349
    /**
350
     * @param string $path
351
     *
352
     * @return array|bool
353
     */
354 5
    public function getMetadata($path)
355
    {
356 5
        return $this->client->headObject([
357 5
            'Bucket' => $this->getBucket(),
358 5
            'Key'    => $path,
359 5
        ])->toArray();
360
    }
361
362
    /**
363
     * @param string $path
364
     *
365
     * @return array|bool
366
     */
367 1
    public function getSize($path)
368
    {
369 1
        $meta = $this->getMetadata($path);
370
371
        return isset($meta['ContentLength'])
372
            ? ['size' => $meta['ContentLength']] : false;
373
    }
374
375
    /**
376
     * @param string $path
377
     *
378
     * @return array|bool
379
     */
380 1
    public function getMimetype($path)
381
    {
382 1
        $meta = $this->getMetadata($path);
383
384
        return isset($meta['ContentType'])
385
            ? ['mimetype' => $meta['ContentType']] : false;
386
    }
387
388
    /**
389
     * @param string $path
390
     *
391
     * @return array|bool
392
     */
393 1
    public function getTimestamp($path)
394
    {
395 1
        $meta = $this->getMetadata($path);
396
397
        return isset($meta['LastModified'])
398
            ? ['timestamp' => strtotime($meta['LastModified'])] : false;
399
    }
400
401
    /**
402
     * @param string $path
403
     *
404
     * @return array|bool
405
     */
406 1
    public function getVisibility($path)
407
    {
408 1
        $meta = $this->client->getObjectAcl([
409 1
            'Bucket' => $this->getBucket(),
410 1
            'Key'    => $path,
411 1
        ]);
412
413
        foreach ($meta->get('Grants') as $grant) {
414
            if (isset($grant['Grantee']['URI'])
415
                && $grant['Permission'] === 'READ'
416
                && strpos($grant['Grantee']['URI'], 'global/AllUsers') !== false
417
            ) {
418
                return ['visibility' => AdapterInterface::VISIBILITY_PUBLIC];
419
            }
420
        }
421
422
        return ['visibility' => AdapterInterface::VISIBILITY_PRIVATE];
423
    }
424
425
    /**
426
     * @param array $content
427
     *
428
     * @return array
429
     */
430
    private function normalizeFileInfo(array $content)
431
    {
432
        $path = pathinfo($content['Key']);
433
434
        return [
435
            "type"      => "file",
436
            "path"      => $content['Key'],
437
            "timestamp" => Carbon::parse($content['LastModified'])->getTimestamp(),
438
            "size"      => (int)$content['Size'],
439
            "dirname"   => (string)$path['dirname'],
440
            "basename"  => (string)$path['basename'],
441
            "extension" => (string)$path['extension'],
442
            "filename"  => (string)$path['filename'],
443
        ];
444
    }
445
}
446