Completed
Pull Request — master (#486)
by Dalibor
02:24
created

AzureBlobStorage::setCreateContainerOptions()   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
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Gaufrette\Adapter;
4
5
use Gaufrette\Adapter;
6
use Gaufrette\Util;
7
use Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactoryInterface;
8
use MicrosoftAzure\Storage\Blob\Models\Blob;
9
use MicrosoftAzure\Storage\Blob\Models\CreateBlobOptions;
10
use MicrosoftAzure\Storage\Blob\Models\CreateContainerOptions;
11
use MicrosoftAzure\Storage\Blob\Models\DeleteContainerOptions;
12
use MicrosoftAzure\Storage\Blob\Models\ListBlobsOptions;
13
use MicrosoftAzure\Storage\Common\Exceptions\ServiceException;
14
15
/**
16
 * Microsoft Azure Blob Storage adapter.
17
 *
18
 * @author Luciano Mammino <[email protected]>
19
 * @author Paweł Czyżewski <[email protected]>
20
 */
21
class AzureBlobStorage 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...
22
                                  MetadataSupporter
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces before interface name; 34 found
Loading history...
23
{
24
    /**
25
     * Error constants.
26
     */
27
    const ERROR_CONTAINER_ALREADY_EXISTS = 'ContainerAlreadyExists';
28
    const ERROR_CONTAINER_NOT_FOUND = 'ContainerNotFound';
29
30
    /**
31
     * @var AzureBlobStorage\BlobProxyFactoryInterface
32
     */
33
    protected $blobProxyFactory;
34
35
    /**
36
     * @var string
37
     */
38
    protected $containerName;
39
40
    /**
41
     * @var bool
42
     */
43
    protected $detectContentType;
44
45
    /**
46
     * @var \MicrosoftAzure\Storage\Blob\Internal\IBlob
47
     */
48
    protected $blobProxy;
49
50
    /**
51
     * @var bool
52
     */
53
    protected $multiContainerMode = false;
54
55
    /**
56
     * @var CreateContainerOptions
57
     */
58
    protected $createContainerOptions;
59
60
    /**
61
     * @param AzureBlobStorage\BlobProxyFactoryInterface $blobProxyFactory
62
     * @param string|null                                $containerName
63
     * @param bool                                       $create
64
     * @param bool                                       $detectContentType
65
     *
66
     * @throws \RuntimeException
67
     */
68
    public function __construct(BlobProxyFactoryInterface $blobProxyFactory, $containerName = null, $create = false, $detectContentType = true)
69
    {
70
        $this->blobProxyFactory = $blobProxyFactory;
71
        $this->containerName = $containerName;
72
        $this->detectContentType = $detectContentType;
73
        if ($create) {
74
            $this->createContainer($containerName);
75
        }
76
    }
77
78
    /**
79
     * @return CreateContainerOptions
80
     */
81
    public function getCreateContainerOptions()
82
    {
83
        return $this->createContainerOptions;
84
    }
85
86
    /**
87
     * @param CreateContainerOptions $options
88
     */
89
    public function setCreateContainerOptions(CreateContainerOptions $options)
90
    {
91
        $this->createContainerOptions = $options;
92
    }
93
94
    /**
95
     * Creates a new container.
96
     *
97
     * @param string                                                     $containerName
98
     * @param \MicrosoftAzure\Storage\Blob\Models\CreateContainerOptions $options
99
     *
100
     * @throws \RuntimeException if cannot create the container
101
     */
102 View Code Duplication
    public function createContainer($containerName, CreateContainerOptions $options = null)
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...
103
    {
104
        $this->init();
105
106
        try {
107
            $this->blobProxy->createContainer($containerName, $options);
108
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
109
            $errorCode = $this->getErrorCodeFromServiceException($e);
110
111
            if ($errorCode !== self::ERROR_CONTAINER_ALREADY_EXISTS) {
112
                throw new \RuntimeException(sprintf(
113
                    'Failed to create the configured container "%s": %s (%s).',
114
                    $containerName,
115
                    $e->getErrorText(),
116
                    $errorCode
117
                ));
118
            }
119
        }
120
    }
121
122
    /**
123
     * Deletes a container.
124
     *
125
     * @param string                 $containerName
126
     * @param DeleteContainerOptions $options
127
     *
128
     * @throws \RuntimeException if cannot delete the container
129
     */
130 View Code Duplication
    public function deleteContainer($containerName, DeleteContainerOptions $options = null)
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...
131
    {
132
        $this->init();
133
134
        try {
135
            $this->blobProxy->deleteContainer($containerName, $options);
136
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
137
            $errorCode = $this->getErrorCodeFromServiceException($e);
138
139
            if ($errorCode !== self::ERROR_CONTAINER_NOT_FOUND) {
140
                throw new \RuntimeException(sprintf(
141
                    'Failed to delete the configured container "%s": %s (%s).',
142
                    $containerName,
143
                    $e->getErrorText(),
144
                    $errorCode
145
                ), $e->getCode());
146
            }
147
        }
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     * @throws \RuntimeException
153
     */
154 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...
155
    {
156
        $this->init();
157
158
        try {
159
            $blob = $this->blobProxy->getBlob($this->containerName, $key);
160
161
            return stream_get_contents($blob->getContentStream());
162
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
163
            $this->failIfContainerNotFound($e, sprintf('read key "%s"', $key));
164
165
            return false;
166
        }
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     * @throws \RuntimeException
172
     */
173
    public function write($key, $content)
174
    {
175
        $this->init();
176
177
        try {
178
            $options = new CreateBlobOptions();
179
180
            if ($this->detectContentType) {
181
                $contentType = $this->guessContentType($content);
182
183
                $options->setBlobContentType($contentType);
184
            }
185
186
            $this->blobProxy->createBlockBlob($this->containerName, $key, $content, $options);
187
188
            if (is_resource($content)) {
189
                return Util\Size::fromResource($content);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \Gaufrette\Util\S...fromResource($content); (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...
190
            }
191
192
            return Util\Size::fromContent($content);
193
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
194
            $this->failIfContainerNotFound($e, sprintf('write content for key "%s"', $key));
195
196
            return false;
197
        }
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     * @throws \RuntimeException
203
     */
204
    public function exists($key)
205
    {
206
        $this->init();
207
208
        $listBlobsOptions = new ListBlobsOptions();
209
        $listBlobsOptions->setPrefix($key);
210
211
        try {
212
            $blobsList = $this->blobProxy->listBlobs($this->containerName, $listBlobsOptions);
213
214
            foreach ($blobsList->getBlobs() as $blob) {
215
                if ($key === $blob->getName()) {
216
                    return true;
217
                }
218
            }
219
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
220
            $this->failIfContainerNotFound($e, 'check if key exists');
221
            $errorCode = $this->getErrorCodeFromServiceException($e);
222
223
            throw new \RuntimeException(sprintf(
224
                'Failed to check if key "%s" exists in container "%s": %s (%s).',
225
                $key,
226
                $this->containerName,
227
                $e->getErrorText(),
228
                $errorCode
229
            ), $e->getCode());
230
        }
231
232
        return false;
233
    }
234
235
    /**
236
     * {@inheritdoc}
237
     * @throws \RuntimeException
238
     */
239
    public function keys()
240
    {
241
        $this->init();
242
243
        try {
244
            $blobList = $this->blobProxy->listBlobs($this->containerName);
245
246
            return array_map(
247
                function (Blob $blob) {
248
                    return $blob->getName();
249
                },
250
                $blobList->getBlobs()
251
            );
252
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
253
            $this->failIfContainerNotFound($e, 'retrieve keys');
254
            $errorCode = $this->getErrorCodeFromServiceException($e);
255
256
            throw new \RuntimeException(sprintf(
257
                'Failed to list keys for the container "%s": %s (%s).',
258
                $this->containerName,
259
                $e->getErrorText(),
260
                $errorCode
261
            ), $e->getCode());
262
        }
263
    }
264
265
    /**
266
     * {@inheritdoc}
267
     * @throws \RuntimeException
268
     */
269
    public function mtime($key)
270
    {
271
        $this->init();
272
273
        try {
274
            $properties = $this->blobProxy->getBlobProperties($this->containerName, $key);
275
276
            return $properties->getProperties()->getLastModified()->getTimestamp();
277
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
278
            $this->failIfContainerNotFound($e, sprintf('read mtime for key "%s"', $key));
279
280
            return false;
281
        }
282
    }
283
284
    /**
285
     * {@inheritdoc}
286
     * @throws \RuntimeException
287
     */
288 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...
289
    {
290
        $this->init();
291
292
        try {
293
            $this->blobProxy->deleteBlob($this->containerName, $key);
294
295
            return true;
296
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
297
            $this->failIfContainerNotFound($e, sprintf('delete key "%s"', $key));
298
299
            return false;
300
        }
301
    }
302
303
    /**
304
     * {@inheritdoc}
305
     * @throws \RuntimeException
306
     */
307
    public function rename($sourceKey, $targetKey)
308
    {
309
        $this->init();
310
311
        try {
312
            $this->blobProxy->copyBlob($this->containerName, $targetKey, $this->containerName, $sourceKey);
313
            $this->blobProxy->deleteBlob($this->containerName, $sourceKey);
314
315
            return true;
316
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
317
            $this->failIfContainerNotFound($e, sprintf('rename key "%s"', $sourceKey));
318
319
            return false;
320
        }
321
    }
322
323
    /**
324
     * {@inheritdoc}
325
     */
326
    public function isDirectory($key)
327
    {
328
        // Windows Azure Blob Storage does not support directories
329
        return false;
330
    }
331
332
    /**
333
     * {@inheritdoc}
334
     * @throws \RuntimeException
335
     */
336 View Code Duplication
    public function setMetadata($key, $content)
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...
337
    {
338
        $this->init();
339
340
        try {
341
            $this->blobProxy->setBlobMetadata($this->containerName, $key, $content);
342
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
343
            $errorCode = $this->getErrorCodeFromServiceException($e);
344
345
            throw new \RuntimeException(sprintf(
346
                'Failed to set metadata for blob "%s" in container "%s": %s (%s).',
347
                $key,
348
                $this->containerName,
349
                $e->getErrorText(),
350
                $errorCode
351
            ), $e->getCode());
352
        }
353
    }
354
355
    /**
356
     * {@inheritdoc}
357
     * @throws \RuntimeException
358
     */
359 View Code Duplication
    public function getMetadata($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...
360
    {
361
        $this->init();
362
363
        try {
364
            $properties = $this->blobProxy->getBlobProperties($this->containerName, $key);
365
366
            return $properties->getMetadata();
367
        } catch (ServiceException $e) {
0 ignored issues
show
Bug introduced by
The class MicrosoftAzure\Storage\C...ptions\ServiceException 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...
368
            $errorCode = $this->getErrorCodeFromServiceException($e);
369
370
            throw new \RuntimeException(sprintf(
371
                'Failed to get metadata for blob "%s" in container "%s": %s (%s).',
372
                $key,
373
                $this->containerName,
374
                $e->getErrorText(),
375
                $errorCode
376
            ), $e->getCode());
377
        }
378
    }
379
380
    /**
381
     * Lazy initialization, automatically called when some method is called after construction.
382
     */
383
    protected function init()
384
    {
385
        if ($this->blobProxy === null) {
386
            $this->blobProxy = $this->blobProxyFactory->create();
387
        }
388
    }
389
390
    /**
391
     * Throws a runtime exception if a give ServiceException derived from a "container not found" error.
392
     *
393
     * @param ServiceException $exception
394
     * @param string           $action
395
     *
396
     * @throws \RuntimeException
397
     */
398
    protected function failIfContainerNotFound(ServiceException $exception, $action)
399
    {
400
        $errorCode = $this->getErrorCodeFromServiceException($exception);
401
402
        if ($errorCode === self::ERROR_CONTAINER_NOT_FOUND) {
403
            throw new \RuntimeException(sprintf(
404
                'Failed to %s: container "%s" not found.',
405
                $action,
406
                $this->containerName
407
            ), $exception->getCode());
408
        }
409
    }
410
411
    /**
412
     * Extracts the error code from a service exception.
413
     *
414
     * @param ServiceException $exception
415
     *
416
     * @return string
417
     */
418
    protected function getErrorCodeFromServiceException(ServiceException $exception)
419
    {
420
        return $exception->getCode();
421
    }
422
423
    /**
424
     * @param string|resource $content
425
     *
426
     * @return string
427
     */
428 View Code Duplication
    private function guessContentType($content)
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...
429
    {
430
        $fileInfo = new \finfo(FILEINFO_MIME_TYPE);
431
432
        if (is_resource($content)) {
433
            return $fileInfo->file(stream_get_meta_data($content)['uri']);
434
        }
435
436
        return $fileInfo->buffer($content);
437
    }
438
}
439