Completed
Push — master ( 5a8a6a...202ca4 )
by Frank
01:59
created

AzureAdapter::getOptionsFromConfig()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 18
rs 9.2
cc 4
eloc 9
nc 6
nop 1
1
<?php
2
3
namespace League\Flysystem\Azure;
4
5
use League\Flysystem\Adapter\AbstractAdapter;
6
use League\Flysystem\Adapter\Polyfill\NotSupportingVisibilityTrait;
7
use League\Flysystem\Config;
8
use League\Flysystem\Util;
9
use WindowsAzure\Blob\Internal\IBlob;
10
use WindowsAzure\Blob\Models\BlobPrefix;
11
use WindowsAzure\Blob\Models\BlobProperties;
12
use WindowsAzure\Blob\Models\CopyBlobResult;
13
use WindowsAzure\Blob\Models\CreateBlobOptions;
14
use WindowsAzure\Blob\Models\ListBlobsOptions;
15
use WindowsAzure\Blob\Models\ListBlobsResult;
16
use WindowsAzure\Common\ServiceException;
17
18
class AzureAdapter extends AbstractAdapter
19
{
20
    use NotSupportingVisibilityTrait;
21
22
    /**
23
     * @var string
24
     */
25
    protected $container;
26
27
    /**
28
     * @var IBlob
29
     */
30
    protected $client;
31
32
    /**
33
     * @var string[]
34
     */
35
    protected static $metaOptions = [
36
        'CacheControl',
37
        'ContentType',
38
        'Metadata',
39
        'ContentLanguage',
40
        'ContentEncoding',
41
    ];
42
43
    /**
44
     * Constructor.
45
     *
46
     * @param IBlob  $azureClient
47
     * @param string $container
48
     */
49
    public function __construct(IBlob $azureClient, $container, $prefix = null)
50
    {
51
        $this->client = $azureClient;
52
        $this->container = $container;
53
        $this->setPathPrefix($prefix);
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    public function write($path, $contents, Config $config)
60
    {
61
        return $this->upload($path, $contents, $config);
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    public function writeStream($path, $resource, Config $config)
68
    {
69
        return $this->upload($path, $resource, $config);
70
    }
71
72
    /**
73
     * {@inheritdoc}
74
     */
75
    public function update($path, $contents, Config $config)
76
    {
77
        return $this->upload($path, $contents, $config);
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function updateStream($path, $resource, Config $config)
84
    {
85
        return $this->upload($path, $resource, $config);
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function rename($path, $newpath)
92
    {
93
        $this->copy($path, $newpath);
94
95
        return $this->delete($path);
96
    }
97
98
    public function copy($path, $newpath)
99
    {
100
        $path = $this->applyPathPrefix($path);
101
        $newpath = $this->applyPathPrefix($newpath);
102
103
        $this->client->copyBlob($this->container, $newpath, $this->container, $path);
104
105
        return true;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function delete($path)
112
    {
113
        $path = $this->applyPathPrefix($path);
114
115
        $this->client->deleteBlob($this->container, $path);
116
117
        return true;
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123
    public function deleteDir($dirname)
124
    {
125
        $dirname = $this->applyPathPrefix($dirname);
126
127
        $options = new ListBlobsOptions();
128
        $options->setPrefix($dirname . '/');
129
130
        /** @var ListBlobsResult $listResults */
131
        $listResults = $this->client->listBlobs($this->container, $options);
0 ignored issues
show
Documentation introduced by
$options is of type object<WindowsAzure\Blob\Models\ListBlobsOptions>, but the function expects a object<WindowsAzure\Blob...\ListBlobsOptions>|null.

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...
132
133
        foreach ($listResults->getBlobs() as $blob) {
134
            /** @var \WindowsAzure\Blob\Models\Blob $blob */
135
            $this->client->deleteBlob($this->container, $blob->getName());
136
        }
137
138
        return true;
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144
    public function createDir($dirname, Config $config)
145
    {
146
        $this->write(rtrim($dirname, '/').'/', ' ', $config);
147
148
        return ['path' => $dirname, 'type' => 'dir'];
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154
    public function has($path)
155
    {
156
        $path = $this->applyPathPrefix($path);
157
158
        try {
159
            $this->client->getBlobMetadata($this->container, $path);
160
        } catch (ServiceException $e) {
161
            if ($e->getCode() !== 404) {
162
                throw $e;
163
            }
164
165
            return false;
166
        }
167
168
        return true;
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174 View Code Duplication
    public function read($path)
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...
175
    {
176
        $path = $this->applyPathPrefix($path);
177
178
        /** @var \WindowsAzure\Blob\Models\GetBlobResult $blobResult */
179
        $blobResult = $this->client->getBlob($this->container, $path);
180
        $properties = $blobResult->getProperties();
181
        $content = $this->streamContentsToString($blobResult->getContentStream());
182
183
        return $this->normalizeBlobProperties($path, $properties) + ['contents' => $content];
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189 View Code Duplication
    public function readStream($path)
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...
190
    {
191
        $path = $this->applyPathPrefix($path);
192
193
        /** @var \WindowsAzure\Blob\Models\GetBlobResult $blobResult */
194
        $blobResult = $this->client->getBlob($this->container, $path);
195
        $properties = $blobResult->getProperties();
196
197
        return $this->normalizeBlobProperties($path, $properties) + ['stream' => $blobResult->getContentStream()];
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203
    public function listContents($directory = '', $recursive = false)
204
    {
205
        $directory = $this->applyPathPrefix($directory);
206
207
        // Append trailing slash only for directory other than root (which after normalization is an empty string).
208
        // Listing for / doesn't work properly otherwise.
209
        if (strlen($directory)) {
210
            $directory = rtrim($directory, '/') . '/';
211
        }
212
213
        $options = new ListBlobsOptions();
214
        $options->setPrefix($directory);
215
216
        if (!$recursive) {
217
            $options->setDelimiter('/');
218
        }
219
220
        /** @var ListBlobsResult $listResults */
221
        $listResults = $this->client->listBlobs($this->container, $options);
0 ignored issues
show
Documentation introduced by
$options is of type object<WindowsAzure\Blob\Models\ListBlobsOptions>, but the function expects a object<WindowsAzure\Blob...\ListBlobsOptions>|null.

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...
222
223
        $contents = [];
224
225
        foreach ($listResults->getBlobs() as $blob) {
226
            $contents[] = $this->normalizeBlobProperties($blob->getName(), $blob->getProperties());
227
        }
228
229
        if (!$recursive) {
230
            $contents = array_merge($contents, array_map([$this, 'normalizeBlobPrefix'], $listResults->getBlobPrefixes()));
231
        }
232
233
        return Util::emulateDirectories($contents);
234
    }
235
236
    /**
237
     * {@inheritdoc}
238
     */
239
    public function getMetadata($path)
240
    {
241
        $path = $this->applyPathPrefix($path);
242
243
        /** @var \WindowsAzure\Blob\Models\GetBlobPropertiesResult $result */
244
        $result = $this->client->getBlobProperties($this->container, $path);
245
246
        return $this->normalizeBlobProperties($path, $result->getProperties());
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252
    public function getSize($path)
253
    {
254
        return $this->getMetadata($path);
255
    }
256
257
    /**
258
     * {@inheritdoc}
259
     */
260
    public function getMimetype($path)
261
    {
262
        return $this->getMetadata($path);
263
    }
264
265
    /**
266
     * {@inheritdoc}
267
     */
268
    public function getTimestamp($path)
269
    {
270
        return $this->getMetadata($path);
271
    }
272
273
    /**
274
     * Builds the normalized output array.
275
     *
276
     * @param string $path
277
     * @param int    $timestamp
278
     * @param mixed  $content
279
     *
280
     * @return array
281
     */
282
    protected function normalize($path, $timestamp, $content = null)
283
    {
284
        $data = [
285
            'path' => $path,
286
            'timestamp' => (int) $timestamp,
287
            'dirname' => Util::dirname($path),
288
            'type' => 'file',
289
        ];
290
291
        if (is_string($content)) {
292
            $data['contents'] = $content;
293
        }
294
295
        return $data;
296
    }
297
298
    /**
299
     * Builds the normalized output array from a Blob object.
300
     *
301
     * @param string         $path
302
     * @param BlobProperties $properties
303
     *
304
     * @return array
305
     */
306
    protected function normalizeBlobProperties($path, BlobProperties $properties)
307
    {
308
        if (substr($path, -1) === '/') {
309
            return ['type' => 'dir', 'path' => $this->removePathPrefix(rtrim($path, '/'))];
310
        }
311
312
        $path = $this->removePathPrefix($path);
313
314
        return [
315
            'path' => $path,
316
            'timestamp' => (int) $properties->getLastModified()->format('U'),
317
            'dirname' => Util::dirname($path),
318
            'mimetype' => $properties->getContentType(),
319
            'size' => $properties->getContentLength(),
320
            'type' => 'file',
321
        ];
322
    }
323
324
    /**
325
     * Builds the normalized output array from a BlobPrefix object.
326
     *
327
     * @param BlobPrefix $blobPrefix
328
     *
329
     * @return array
330
     */
331
    protected function normalizeBlobPrefix(BlobPrefix $blobPrefix)
332
    {
333
        return ['type' => 'dir', 'path' => $this->removePathPrefix(rtrim($blobPrefix->getName(), '/'))];
334
    }
335
336
    /**
337
     * Retrieves content streamed by Azure into a string.
338
     *
339
     * @param resource $resource
340
     *
341
     * @return string
342
     */
343
    protected function streamContentsToString($resource)
344
    {
345
        return stream_get_contents($resource);
346
    }
347
348
    /**
349
     * Upload a file.
350
     *
351
     * @param string           $path     Path
352
     * @param string|resource  $contents Either a string or a stream.
353
     * @param Config           $config   Config
354
     *
355
     * @return array
356
     */
357
    protected function upload($path, $contents, Config $config)
358
    {
359
        $path = $this->applyPathPrefix($path);
360
361
        /** @var CopyBlobResult $result */
362
        $result = $this->client->createBlockBlob($this->container, $path, $contents, $this->getOptionsFromConfig($config));
0 ignored issues
show
Documentation introduced by
$this->getOptionsFromConfig($config) is of type object<WindowsAzure\Blob...dels\CreateBlobOptions>, but the function expects a object<WindowsAzure\Blob...CreateBlobOptions>|null.

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...
Bug introduced by
It seems like $contents defined by parameter $contents on line 357 can also be of type resource; however, WindowsAzure\Blob\Intern...Blob::createBlockBlob() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
363
364
        return $this->normalize($path, $result->getLastModified()->format('U'), $contents);
365
    }
366
367
    /**
368
     * Retrieve options from a Config instance.
369
     *
370
     * @param Config $config
371
     *
372
     * @return CreateBlobOptions
373
     */
374
    protected function getOptionsFromConfig(Config $config)
375
    {
376
        $options = new CreateBlobOptions();
377
378
        foreach (static::$metaOptions as $option) {
379
            if (!$config->has($option)) {
380
                continue;
381
            }
382
383
            call_user_func([$options, "set$option"], $config->get($option));
384
        }
385
386
        if ($mimetype = $config->get('mimetype')) {
387
            $options->setContentType($mimetype);
388
        }
389
390
        return $options;
391
    }
392
}
393