Completed
Push — master ( f60877...dec506 )
by frey
03:16
created

Adapter::getTemporaryUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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