Completed
Pull Request — master (#438)
by Albin
05:53
created

GCS   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 361
Duplicated Lines 100 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
wmc 44
lcom 1
cbo 0
dl 361
loc 361
rs 8.3396
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 18 18 1
A getOptions() 4 4 1
A setOptions() 4 4 1
A getBucket() 4 4 1
A setBucket() 4 4 1
A read() 23 23 3
D write() 65 65 10
A exists() 13 13 2
A keys() 4 4 1
A mtime() 9 9 2
A delete() 13 13 2
A rename() 20 20 3
A isDirectory() 8 8 2
B listKeys() 29 29 4
A setMetadata() 6 6 1
A getMetadata() 6 6 2
A ensureBucketExists() 22 22 3
A computePath() 8 8 2
A getObjectData() 8 8 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like GCS often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GCS, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Gaufrette\Adapter\GCS;
4
5
use Gaufrette\Adapter;
6
7
/**
8
 * Google Cloud Storage adapter using the Google APIs Client Library for PHP.
9
 *
10
 * @author  Patrik Karisch <[email protected]>
11
 */
12 View Code Duplication
class GCS implements Adapter, Adapter\MetadataSupporter, Adapter\ListKeysAware
0 ignored issues
show
Duplication introduced by
This class 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...
13
{
14
    protected $service;
15
    protected $bucket;
16
    protected $options;
17
    protected $bucketExists;
18
    protected $metadata = array();
19
    protected $detectContentType;
20
21
    /**
22
     * @param \Google_Service_Storage $service           The storage service class with authenticated
23
     *                                                   client and full access scope
24
     * @param string                  $bucket            The bucket name
25
     * @param array                   $options           Options can be directory and acl
26
     * @param bool                    $detectContentType Whether to detect the content type or not
27
     */
28
    public function __construct(
29
        \Google_Service_Storage $service,
30
        $bucket,
31
        array $options = array(),
32
        $detectContentType = false
33
    ) {
34
        $this->service = $service;
35
        $this->bucket = $bucket;
36
        $this->options = array_replace(
37
            array(
38
                'directory' => '',
39
                'acl' => 'private',
40
            ),
41
            $options
42
        );
43
44
        $this->detectContentType = $detectContentType;
45
    }
46
47
    /**
48
     * @return array The actual options
49
     */
50
    public function getOptions()
51
    {
52
        return $this->options;
53
    }
54
55
    /**
56
     * @param array $options The new options
57
     */
58
    public function setOptions($options)
59
    {
60
        $this->options = array_replace($this->options, $options);
61
    }
62
63
    /**
64
     * @return string The current bucket name
65
     */
66
    public function getBucket()
67
    {
68
        return $this->bucket;
69
    }
70
71
    /**
72
     * Sets a new bucket name.
73
     *
74
     * @param string $bucket The new bucket name
75
     */
76
    public function setBucket($bucket)
77
    {
78
        $this->bucket = $bucket;
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84
    public function read($key)
85
    {
86
        $this->ensureBucketExists();
87
        $path = $this->computePath($key);
88
89
        $object = $this->getObjectData($path);
90
        if ($object === false) {
91
            return false;
92
        }
93
94
        $request = new \Google_Http_Request($object->getMediaLink());
95
        $this->service->getClient()->getAuth()->sign($request);
96
97
        $response = $this->service->getClient()->getIo()->executeRequest($request);
98
99
        if ($response[2] == 200) {
100
            $this->setMetadata($key, $object->getMetadata());
101
102
            return $response[0];
103
        }
104
105
        return false;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function write($key, $content)
112
    {
113
        $this->ensureBucketExists();
114
        $path = $this->computePath($key);
115
116
        $metadata = $this->getMetadata($key);
117
        $options = array(
118
            'uploadType' => 'multipart',
119
            'data' => $content,
120
        );
121
122
        /*
123
         * If the ContentType was not already set in the metadata, then we autodetect
124
         * it to prevent everything being served up as application/octet-stream.
125
         */
126
        if (!isset($metadata['ContentType']) && $this->detectContentType) {
127
            $finfo = new \finfo(FILEINFO_MIME_TYPE);
128
            $options['mimeType'] = $finfo->buffer($content);
129
            unset($metadata['ContentType']);
130
        } elseif (isset($metadata['ContentType'])) {
131
            $options['mimeType'] = $metadata['ContentType'];
132
            unset($metadata['ContentType']);
133
        }
134
135
        $object = new \Google_Service_Storage_StorageObject();
136
        $object->name = $path;
137
138
        if (isset($metadata['ContentDisposition'])) {
139
            $object->setContentDisposition($metadata['ContentDisposition']);
140
            unset($metadata['ContentDisposition']);
141
        }
142
143
        if (isset($metadata['CacheControl'])) {
144
            $object->setCacheControl($metadata['CacheControl']);
145
            unset($metadata['CacheControl']);
146
        }
147
148
        if (isset($metadata['ContentLanguage'])) {
149
            $object->setContentLanguage($metadata['ContentLanguage']);
150
            unset($metadata['ContentLanguage']);
151
        }
152
153
        if (isset($metadata['ContentEncoding'])) {
154
            $object->setContentEncoding($metadata['ContentEncoding']);
155
            unset($metadata['ContentEncoding']);
156
        }
157
158
        $object->setMetadata($metadata);
159
160
        try {
161
            $object = $this->service->objects->insert($this->bucket, $object, $options);
162
163
            if ($this->options['acl'] == 'public') {
164
                $acl = new \Google_Service_Storage_ObjectAccessControl();
165
                $acl->setEntity('allUsers');
166
                $acl->setRole('READER');
167
168
                $this->service->objectAccessControls->insert($this->bucket, $path, $acl);
169
            }
170
171
            return $object->getSize();
172
        } catch (\Google_Service_Exception $e) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
173
            return false;
174
        }
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    public function exists($key)
181
    {
182
        $this->ensureBucketExists();
183
        $path = $this->computePath($key);
184
185
        try {
186
            $this->service->objects->get($this->bucket, $path);
187
        } catch (\Google_Service_Exception $e) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
188
            return false;
189
        }
190
191
        return true;
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197
    public function keys()
198
    {
199
        return $this->listKeys();
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function mtime($key)
206
    {
207
        $this->ensureBucketExists();
208
        $path = $this->computePath($key);
209
210
        $object = $this->getObjectData($path);
211
212
        return $object ? strtotime($object->getUpdated()) : false;
213
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218
    public function delete($key)
219
    {
220
        $this->ensureBucketExists();
221
        $path = $this->computePath($key);
222
223
        try {
224
            $this->service->objects->delete($this->bucket, $path);
225
        } catch (\Google_Service_Exception $e) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
226
            return false;
227
        }
228
229
        return true;
230
    }
231
232
    /**
233
     * {@inheritdoc}
234
     */
235
    public function rename($sourceKey, $targetKey)
236
    {
237
        $this->ensureBucketExists();
238
        $sourcePath = $this->computePath($sourceKey);
239
        $targetPath = $this->computePath($targetKey);
240
241
        $object = $this->getObjectData($sourcePath);
242
        if ($object === false) {
243
            return false;
244
        }
245
246
        try {
247
            $this->service->objects->copy($this->bucket, $sourcePath, $this->bucket, $targetPath, $object);
248
            $this->delete($sourcePath);
249
        } catch (\Google_Service_Exception $e) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
250
            return false;
251
        }
252
253
        return true;
254
    }
255
256
    /**
257
     * {@inheritdoc}
258
     */
259
    public function isDirectory($key)
260
    {
261
        if ($this->exists($key.'/')) {
262
            return true;
263
        }
264
265
        return false;
266
    }
267
268
    /**
269
     * {@inheritdoc}
270
     */
271
    public function listKeys($prefix = '')
272
    {
273
        $this->ensureBucketExists();
274
275
        $options = array();
276
        if ((string) $prefix != '') {
277
            $options['prefix'] = $this->computePath($prefix);
278
        } elseif (!empty($this->options['directory'])) {
279
            $options['prefix'] = $this->options['directory'];
280
        }
281
282
        $list = $this->service->objects->listObjects($this->bucket, $options);
283
        $keys = array();
284
285
        // FIXME: Temporary workaround for google/google-api-php-client#375
286
        $reflectionClass = new \ReflectionClass('Google_Service_Storage_Objects');
287
        $reflectionProperty = $reflectionClass->getProperty('collection_key');
288
        $reflectionProperty->setAccessible(true);
289
        $reflectionProperty->setValue($list, 'items');
290
291
        /** @var \Google_Service_Storage_StorageObject $object */
292
        foreach ($list as $object) {
293
            $keys[] = $object->name;
294
        }
295
296
        sort($keys);
297
298
        return $keys;
299
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304
    public function setMetadata($key, $content)
305
    {
306
        $path = $this->computePath($key);
307
308
        $this->metadata[$path] = $content;
309
    }
310
311
    /**
312
     * {@inheritdoc}
313
     */
314
    public function getMetadata($key)
315
    {
316
        $path = $this->computePath($key);
317
318
        return isset($this->metadata[$path]) ? $this->metadata[$path] : array();
319
    }
320
321
    /**
322
     * Ensures the specified bucket exists.
323
     *
324
     * @throws \RuntimeException if the bucket does not exists
325
     */
326
    protected function ensureBucketExists()
327
    {
328
        if ($this->bucketExists) {
329
            return;
330
        }
331
332
        try {
333
            $this->service->buckets->get($this->bucket);
334
            $this->bucketExists = true;
335
336
            return;
337
        } catch (\Google_Service_Exception $e) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
338
            $this->bucketExists = false;
339
340
            throw new \RuntimeException(
341
                sprintf(
342
                    'The configured bucket "%s" does not exist.',
343
                    $this->bucket
344
                )
345
            );
346
        }
347
    }
348
349
    protected function computePath($key)
350
    {
351
        if (empty($this->options['directory'])) {
352
            return $key;
353
        }
354
355
        return sprintf('%s/%s', $this->options['directory'], $key);
356
    }
357
358
    /**
359
     * @param string $path
360
     * @param array  $options
361
     *
362
     * @return bool|\Google_Service_Storage_StorageObject
363
     */
364
    private function getObjectData($path, $options = array())
365
    {
366
        try {
367
            return $this->service->objects->get($this->bucket, $path, $options);
368
        } catch (\Google_Service_Exception $e) {
0 ignored issues
show
Bug introduced by
The class Google_Service_Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
369
            return false;
370
        }
371
    }
372
}
373