Completed
Push — master ( 787bbd...13cd31 )
by Albin
02:42
created

GoogleCloudStorage::getBucket()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Gaufrette\Adapter;
4
5
use Gaufrette\Adapter;
6
use GuzzleHttp;
7
8
/**
9
 * Google Cloud Storage adapter using the Google APIs Client Library for PHP.
10
 *
11
 * @author  Patrik Karisch <[email protected]>
12
 */
13
class GoogleCloudStorage implements Adapter,
0 ignored issues
show
Coding Style introduced by
The first item in a multi-line implements list must be on the line following the implements keyword
Loading history...
14
                                    MetadataSupporter,
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces before interface name; 36 found
Loading history...
15
                                    ListKeysAware
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces before interface name; 36 found
Loading history...
16
{
17
    protected $service;
18
    protected $bucket;
19
    protected $options;
20
    protected $bucketExists;
21
    protected $metadata = array();
22
    protected $detectContentType;
23
24
    /**
25
     * @param \Google_Service_Storage $service           The storage service class with authenticated
26
     *                                                   client and full access scope
27
     * @param string                  $bucket            The bucket name
28
     * @param array                   $options           Options can be directory and acl
29
     * @param bool                    $detectContentType Whether to detect the content type or not
30
     */
31 View Code Duplication
    public function __construct(
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...
32
        \Google_Service_Storage $service,
33
        $bucket,
34
        array $options = array(),
35
        $detectContentType = false
36
    ) {
37
        $this->service = $service;
38
        $this->bucket = $bucket;
39
        $this->options = array_replace(
40
            array(
41
                'directory' => '',
42
                'acl' => 'private',
43
            ),
44
            $options
45
        );
46
47
        $this->detectContentType = $detectContentType;
48
    }
49
50
    /**
51
     * @return array The actual options
52
     */
53
    public function getOptions()
54
    {
55
        return $this->options;
56
    }
57
58
    /**
59
     * @param array $options The new options
60
     */
61
    public function setOptions($options)
62
    {
63
        $this->options = array_replace($this->options, $options);
64
    }
65
66
    /**
67
     * @return string The current bucket name
68
     */
69
    public function getBucket()
70
    {
71
        return $this->bucket;
72
    }
73
74
    /**
75
     * Sets a new bucket name.
76
     *
77
     * @param string $bucket The new bucket name
78
     */
79
    public function setBucket($bucket)
80
    {
81
        $this->bucket = $bucket;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function read($key)
88
    {
89
        $this->ensureBucketExists();
90
        $path = $this->computePath($key);
91
92
        $object = $this->getObjectData($path);
93
        if ($object === false) {
94
            return false;
95
        }
96
97
        if (class_exists('Google_Http_Request')) {
98
            $request = new Google_Http_Request($object->getMediaLink());
0 ignored issues
show
Bug introduced by
The method getMediaLink() does not seem to exist on object<Google_Http_Request>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
99
            $this->service->getClient()->getAuth()->sign($request);
100
            $response = $this->service->getClient()->getIo()->executeRequest($request);
101
            if ($response[2] == 200) {
102
                $this->setMetadata($key, $object->getMetadata());
0 ignored issues
show
Bug introduced by
The method getMetadata() does not seem to exist on object<Google_Http_Request>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
103
104
                return $response[0];
105
            }
106
        } else {
107
            $httpClient = new GuzzleHttp\Client();
108
            $httpClient = $this->service->getClient()->authorize($httpClient);
0 ignored issues
show
Bug introduced by
The method authorize() does not seem to exist on object<Google_Client>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
109
            $response = $httpClient->request('GET', $object->getMediaLink());
0 ignored issues
show
Bug introduced by
The method getMediaLink() does not seem to exist on object<Google_Http_Request>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
110
            if ($response->getStatusCode() == 200) {
111
                $this->setMetadata($key, $object->getMetadata());
0 ignored issues
show
Bug introduced by
The method getMetadata() does not seem to exist on object<Google_Http_Request>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
112
113
                return $response->getBody();
114
            }
115
        }
116
117
        return false;
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123
    public function write($key, $content)
124
    {
125
        $this->ensureBucketExists();
126
        $path = $this->computePath($key);
127
128
        $metadata = $this->getMetadata($key);
129
        $options = array(
130
            'uploadType' => 'multipart',
131
            'data' => $content,
132
        );
133
134
        /*
135
         * If the ContentType was not already set in the metadata, then we autodetect
136
         * it to prevent everything being served up as application/octet-stream.
137
         */
138
        if (!isset($metadata['ContentType']) && $this->detectContentType) {
139
            $finfo = new \finfo(FILEINFO_MIME_TYPE);
140
            $options['mimeType'] = $finfo->buffer($content);
141
            unset($metadata['ContentType']);
142
        } elseif (isset($metadata['ContentType'])) {
143
            $options['mimeType'] = $metadata['ContentType'];
144
            unset($metadata['ContentType']);
145
        }
146
147
        $object = new \Google_Service_Storage_StorageObject();
148
        $object->name = $path;
149
150
        if (isset($metadata['ContentDisposition'])) {
151
            $object->setContentDisposition($metadata['ContentDisposition']);
152
            unset($metadata['ContentDisposition']);
153
        }
154
155
        if (isset($metadata['CacheControl'])) {
156
            $object->setCacheControl($metadata['CacheControl']);
157
            unset($metadata['CacheControl']);
158
        }
159
160
        if (isset($metadata['ContentLanguage'])) {
161
            $object->setContentLanguage($metadata['ContentLanguage']);
162
            unset($metadata['ContentLanguage']);
163
        }
164
165
        if (isset($metadata['ContentEncoding'])) {
166
            $object->setContentEncoding($metadata['ContentEncoding']);
167
            unset($metadata['ContentEncoding']);
168
        }
169
170
        $object->setMetadata($metadata);
171
172
        try {
173
            $object = $this->service->objects->insert($this->bucket, $object, $options);
174
175
            if ($this->options['acl'] == 'public') {
176
                $acl = new \Google_Service_Storage_ObjectAccessControl();
177
                $acl->setEntity('allUsers');
178
                $acl->setRole('READER');
179
180
                $this->service->objectAccessControls->insert($this->bucket, $path, $acl);
181
            }
182
183
            return $object->getSize();
0 ignored issues
show
Bug introduced by
The method getSize() does not seem to exist on object<Google_Http_Request>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
184
        } catch (\Google_Service_Exception $e) {
185
            return false;
186
        }
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192 View Code Duplication
    public function exists($key)
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...
193
    {
194
        $this->ensureBucketExists();
195
        $path = $this->computePath($key);
196
197
        try {
198
            $this->service->objects->get($this->bucket, $path);
199
        } catch (\Google_Service_Exception $e) {
200
            return false;
201
        }
202
203
        return true;
204
    }
205
206
    /**
207
     * {@inheritdoc}
208
     */
209
    public function keys()
210
    {
211
        return $this->listKeys();
212
    }
213
214
    /**
215
     * {@inheritdoc}
216
     */
217
    public function mtime($key)
218
    {
219
        $this->ensureBucketExists();
220
        $path = $this->computePath($key);
221
222
        $object = $this->getObjectData($path);
223
224
        return $object ? strtotime($object->getUpdated()) : false;
0 ignored issues
show
Bug introduced by
The method getUpdated() does not seem to exist on object<Google_Http_Request>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230 View Code Duplication
    public function delete($key)
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...
231
    {
232
        $this->ensureBucketExists();
233
        $path = $this->computePath($key);
234
235
        try {
236
            $this->service->objects->delete($this->bucket, $path);
237
        } catch (\Google_Service_Exception $e) {
238
            return false;
239
        }
240
241
        return true;
242
    }
243
244
    /**
245
     * {@inheritdoc}
246
     */
247
    public function rename($sourceKey, $targetKey)
248
    {
249
        $this->ensureBucketExists();
250
        $sourcePath = $this->computePath($sourceKey);
251
        $targetPath = $this->computePath($targetKey);
252
253
        $object = $this->getObjectData($sourcePath);
254
        if ($object === false) {
255
            return false;
256
        }
257
258
        try {
259
            $this->service->objects->copy($this->bucket, $sourcePath, $this->bucket, $targetPath, $object);
0 ignored issues
show
Bug introduced by
It seems like $object defined by $this->getObjectData($sourcePath) on line 253 can also be of type object<Google_Http_Request>; however, Google_Service_Storage_Objects_Resource::copy() does only seem to accept object<Google_Service_Storage_StorageObject>, 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...
260
            $this->delete($sourcePath);
261
        } catch (\Google_Service_Exception $e) {
262
            return false;
263
        }
264
265
        return true;
266
    }
267
268
    /**
269
     * {@inheritdoc}
270
     */
271
    public function isDirectory($key)
272
    {
273
        if ($this->exists($key.'/')) {
274
            return true;
275
        }
276
277
        return false;
278
    }
279
280
    /**
281
     * {@inheritdoc}
282
     */
283
    public function listKeys($prefix = '')
284
    {
285
        $this->ensureBucketExists();
286
287
        $options = array();
288 View Code Duplication
        if ((string) $prefix != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
289
            $options['prefix'] = $this->computePath($prefix);
290
        } elseif (!empty($this->options['directory'])) {
291
            $options['prefix'] = $this->options['directory'];
292
        }
293
294
        $list = $this->service->objects->listObjects($this->bucket, $options);
295
        $keys = array();
296
297
        // FIXME: Temporary workaround for google/google-api-php-client#375
298
        $reflectionClass = new \ReflectionClass('Google_Service_Storage_Objects');
299
        $reflectionProperty = $reflectionClass->getProperty('collection_key');
300
        $reflectionProperty->setAccessible(true);
301
        $reflectionProperty->setValue($list, 'items');
302
303
        /** @var \Google_Service_Storage_StorageObject $object */
304
        foreach ($list as $object) {
0 ignored issues
show
Bug introduced by
The expression $list of type object<Google_Http_Reque...|object<expected_class> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
305
            $keys[] = $object->name;
306
        }
307
308
        sort($keys);
309
310
        return $keys;
311
    }
312
313
    /**
314
     * {@inheritdoc}
315
     */
316
    public function setMetadata($key, $content)
317
    {
318
        $path = $this->computePath($key);
319
320
        $this->metadata[$path] = $content;
321
    }
322
323
    /**
324
     * {@inheritdoc}
325
     */
326
    public function getMetadata($key)
327
    {
328
        $path = $this->computePath($key);
329
330
        return isset($this->metadata[$path]) ? $this->metadata[$path] : array();
331
    }
332
333
    /**
334
     * Ensures the specified bucket exists.
335
     *
336
     * @throws \RuntimeException if the bucket does not exists
337
     */
338
    protected function ensureBucketExists()
339
    {
340
        if ($this->bucketExists) {
341
            return;
342
        }
343
344
        try {
345
            $this->service->buckets->get($this->bucket);
346
            $this->bucketExists = true;
347
348
            return;
349
        } catch (\Google_Service_Exception $e) {
350
            $this->bucketExists = false;
351
352
            throw new \RuntimeException(
353
                sprintf(
354
                    'The configured bucket "%s" does not exist.',
355
                    $this->bucket
356
                )
357
            );
358
        }
359
    }
360
361 View Code Duplication
    protected function computePath($key)
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...
362
    {
363
        if (empty($this->options['directory'])) {
364
            return $key;
365
        }
366
367
        return sprintf('%s/%s', $this->options['directory'], $key);
368
    }
369
370
    /**
371
     * @param string $path
372
     * @param array  $options
373
     *
374
     * @return bool|\Google_Service_Storage_StorageObject
375
     */
376
    private function getObjectData($path, $options = array())
377
    {
378
        try {
379
            return $this->service->objects->get($this->bucket, $path, $options);
380
        } catch (\Google_Service_Exception $e) {
381
            return false;
382
        }
383
    }
384
}
385