Completed
Pull Request — master (#427)
by
unknown
08:12
created

GoogleCloudClientStorage::setOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Gaufrette\Adapter;
4
5
use Gaufrette\Adapter;
6
use Gaufrette\Adapter\MetadataSupporter;
7
use Gaufrette\Adapter\ResourcesSupporter;
8
use Gaufrette\Adapter\ListKeysAware;
9
10
/**
11
 * Google Cloud Storage adapter using the Google Cloud Client Library for PHP
12
 * http://googlecloudplatform.github.io/google-cloud-php/
13
 *
14
 * @package Gaufrette
15
 * @author  Lech Buszczynski <[email protected]>
16
 */
17
class GoogleCloudClientStorage implements Adapter, MetadataSupporter, ResourcesSupporter, ListKeysAware {
18
19
    protected $storageClient;
20
    protected $bucket;
21
    protected $bucketValidated;
22
    protected $options      = array();
23
    protected $metadata     = array();
24
    protected $resources    = array();
25
26
    /**
27
     * @param Google\Cloud\Storage\StorageClient    $service    Authenticated storage client class
0 ignored issues
show
Bug introduced by
There is no parameter named $service. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
28
     * @param string                                $bucketName Name of the bucket
29
     * @param array                                 $options    Options are: "directory" and "acl" (see https://cloud.google.com/storage/docs/access-control/lists)
30
     */
31
    public function __construct(\Google\Cloud\Storage\StorageClient $storageClient, $bucketName, $options = array())
32
    {
33
        $this->storageClient = $storageClient;
34
        $this->setBucket($bucketName);
35
        $this->options = array_replace_recursive(
36
            array(
37
                'directory' => '',
38
                'acl'       => array()
39
            ),
40
            $options
41
        );
42
    }
43
    
44
    /**
45
     * Get adapter options
46
     * 
47
     * @return  array
48
     */
49
    public function getOptions()
50
    {
51
        return $this->options;
52
    }
53
54
    /**
55
     * Set adapter options
56
     * 
57
     * @param   array   $options
58
     */
59
    public function setOptions($options)
60
    {
61
        $this->options = array_replace($this->options, $options);
62
    }
63
    
64
    protected function computePath($key)
65
    {
66
        if (strlen($this->options['directory']))
67
        {
68
            if (strcmp(substr($this->options['directory'], -1), '/') == 0)
69
            {
70
                return $this->options['directory'].$key;
71
            }
72
            return $this->options['directory'].'/'.$key;
73
        }
74
        return $key;
75
    }
76
    
77
    protected function isBucket()
78
    {
79
        if ($this->bucketValidated === true)
80
        {
81
            return true;
82
        } elseif (!$this->bucket->exists()) {
83
            throw new \RuntimeException(sprintf('Bucket %s does not exist.', $this->bucket->name()));
84
        }
85
        $this->bucketValidated = true;
86
        return true;
87
    }
88
    
89
    public function setBucket($name)
90
    {
91
        $this->bucketValidated = null;
92
        $this->bucket = $this->storageClient->bucket($name);       
93
        $this->isBucket();
94
    }
95
    
96
    public function getBucket()
97
    {
98
        return $this->bucket;
99
    }
100
    
101
    /**
102
     * {@inheritdoc}
103
     */
104 View Code Duplication
    public function read($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...
105
    {   
106
        $this->isBucket();     
107
        $object = $this->bucket->object($this->computePath($key));
108
        $info = $object->info();
109
        $this->setResources($key, $info);
110
        return $object->downloadAsString();
111
    }
112
    
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public function write($key, $content)
117
    {
118
        $this->isBucket();
119
        
120
        $options = array(
121
            'resumable'     => true,
122
            'name'          => $this->computePath($key),
123
            'metadata'      => $this->getMetadata($key),
124
        );
125
126
        $this->bucket->upload(
127
            $content,
128
            $options
129
        );
130
        
131
        $this->updateKeyProperties($key,
132
            array(
133
                'acl'       => $this->options['acl'],
134
                'metadata'  => $this->getMetadata($key)
135
            )
136
        );
137
        
138
        $size = $this->getResourceByName($key, 'size');
139
        
140
        return $size === null ? false : $size;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $size === null ? false : $size; (array|string) is incompatible with the return type declared by the interface Gaufrette\Adapter::write of type integer|boolean.

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...
141
    }
142
    
143
    /**
144
     * {@inheritdoc}
145
     */
146
    public function exists($key)
147
    {
148
        $this->isBucket();
149
        $object = $this->bucket->object($this->computePath($key));
150
        if ($object->exists())
151
        {
152
            return true;
153
        }
154
        return false;
155
    }
156
    
157
    /**
158
     * {@inheritdoc}
159
     */
160
    public function isDirectory($key)
161
    {
162
        if ($this->exists($key . '/'))
163
        {
164
            return true;
165
        }
166
        return false;
167
    }
168
    
169
    /**
170
     * {@inheritdoc}
171
     */
172
    public function listKeys($prefix = null)
173
    {
174
        $this->isBucket();        
175
        $keys = array();        
176
        if ($prefix === null)
177
        {
178
            $prefix = $this->options['directory'];
179
        } else {
180
            $prefix = $this->computePath($prefix);
181
        }
182
        foreach ($this->bucket->objects(array('prefix' => $prefix)) as $e)
183
        {
184
            $keys[] = $e->name();
185
        }
186
        sort($keys);
187
        return $keys;
188
    }
189
    
190
    /**
191
     * {@inheritdoc}
192
     */
193
    public function keys()
194
    {
195
        return $this->listKeys();
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201 View Code Duplication
    public function mtime($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...
202
    {
203
        $this->isBucket();
204
        $object = $object = $this->bucket->object($this->computePath($key));
205
        $info = $object->info();
206
        $this->setResources($key, $info);
207
        return strtotime($info['updated']);
208
    }
209
    
210
    /**
211
     * {@inheritdoc}
212
     */
213
    public function delete($key)
214
    {
215
        $this->isBucket();
216
        $object = $this->bucket->object($this->computePath($key));
217
        $object->delete();
218
        return true;
219
    }
220
    
221
    /**
222
     * {@inheritdoc}
223
     */
224
    public function rename($sourceKey, $targetKey)
225
    {
226
        $this->isBucket();
227
        
228
        $metadata = $this->getMetadata($sourceKey);
0 ignored issues
show
Unused Code introduced by
$metadata is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
229
        
230
        $sourceKey = $this->computePath($sourceKey);
231
        $targetKey = $this->computePath($targetKey);
232
        
233
        $object = $this->bucket->object($sourceKey);
234
        
235
        $copiedObject = $object->copy($this->bucket,
236
            array(
237
                'name' => $targetKey
238
            )
239
        );
240
        
241
        if ($copiedObject->exists())
242
        {
243
            $this->updateKeyProperties($targetKey,
244
                array(
245
                    'acl'       => $this->options['acl'],
246
                    'metadata'  => $this->getMetadata($sourceKey)
247
                )
248
            );
249
            $this->setMetadata($targetKey, $this->getMetadata($sourceKey));
250
            $this->setMetadata($sourceKey, null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
251
            $object->delete();
252
            return true;
253
        }
254
        return false;
255
    }
256
    
257
    /**
258
     * {@inheritdoc}
259
     */
260
    public function setMetadata($key, $metadata)
261
    {
262
        $this->metadata[$key] = $metadata;
263
    }
264
265
    /**
266
     * {@inheritdoc}
267
     */
268
    public function getMetadata($key)
269
    {    
270
        return isset($this->metadata[$key]) ? $this->metadata[$key] : array();
271
    }
272
    
273
    /**
274
     * {@inheritdoc}
275
     */
276
    public function setResources($key, $data)
277
    {
278
        $this->resources[$key] = $data;
279
    }
280
    
281
    /**
282
     * {@inheritdoc}
283
     */
284
    public function getResources($key)
285
    {
286
        return isset($this->resources[$key]) ? $this->resources[$key] : array();
287
    }
288
    
289
    /**
290
     * {@inheritdoc}
291
     */
292
    public function getResourceByName($key, $resourceName)
293
    {
294
        return isset($this->resources[$key][$resourceName]) ? $this->resources[$key][$resourceName] : null;
295
    }
296
    
297
    /**
298
     * Sets ACL and metadata information.
299
     * 
300
     * @param   string  $key
301
     * @param   array   $options    Can contain "acl" and/or "metadata" arrays.
302
     * @return  boolean
303
     */
304
    protected function updateKeyProperties($key, $options = array())
305
    {
306
        if ($this->exists($key) === false)
307
        {
308
            return false;
309
        }
310
        
311
        $object = $this->bucket->object($this->computePath($key));
312
        
313
        $properties = array_replace_recursive(
314
            array(
315
                'acl'       => array(),
316
                'metadata'  => array()
317
            ), $options
318
        );
319
320
        $acl = $object->acl();
321
        foreach ($properties['acl'] as $k => $v)
322
        {
323
            $acl->add($k, $v);
324
        }
325
        $object->update(array('metadata' => $properties['metadata']));
326
327
        $info = $object->info();
328
329
        $this->setResources($key, $info);
330
        return true;
331
    }
332
}