Completed
Pull Request — master (#27)
by
unknown
04:54
created

AzureAdapter::getClient()   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 0
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 MicrosoftAzure\Storage\Blob\Internal\IBlob;
10
use MicrosoftAzure\Storage\Blob\Models\BlobPrefix;
11
use MicrosoftAzure\Storage\Blob\Models\BlobProperties;
12
use MicrosoftAzure\Storage\Blob\Models\CopyBlobResult;
13
use MicrosoftAzure\Storage\Blob\Models\CreateBlobOptions;
14
use MicrosoftAzure\Storage\Blob\Models\ListBlobsOptions;
15
use MicrosoftAzure\Storage\Blob\Models\ListBlobsResult;
16
use MicrosoftAzure\Storage\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
     * @param string $prefix
49
     */
50
    public function __construct(IBlob $azureClient, $container, $prefix = null)
51
    {
52
        $this->client = $azureClient;
53
        $this->container = $container;
54
        $this->setPathPrefix($prefix);
55
    }
56
57
    /**
58
     * Get the Azure client instance.
59
     *
60
     * @return IBlob
61
     */
62
    public function getClient()
63
    {
64
        return $this->client;
65
    }
66
67
    /**
68
     * Get the Azure container.
69
     *
70
     * @return string
71
     */
72
    public function getContainer()
73
    {
74
        return $this->container;
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    public function write($path, $contents, Config $config)
81
    {
82
        return $this->upload($path, $contents, $config);
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88
    public function writeStream($path, $resource, Config $config)
89
    {
90
        return $this->upload($path, $resource, $config);
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    public function update($path, $contents, Config $config)
97
    {
98
        return $this->upload($path, $contents, $config);
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104
    public function updateStream($path, $resource, Config $config)
105
    {
106
        return $this->upload($path, $resource, $config);
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112
    public function rename($path, $newpath)
113
    {
114
        $this->copy($path, $newpath);
115
116
        return $this->delete($path);
117
    }
118
119
    public function copy($path, $newpath)
120
    {
121
        $path = $this->applyPathPrefix($path);
122
        $newpath = $this->applyPathPrefix($newpath);
123
124
        $this->client->copyBlob($this->container, $newpath, $this->container, $path);
125
126
        return true;
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132
    public function delete($path)
133
    {
134
        $path = $this->applyPathPrefix($path);
135
136
        $this->client->deleteBlob($this->container, $path);
137
138
        return true;
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144
    public function deleteDir($dirname)
145
    {
146
        $dirname = $this->applyPathPrefix($dirname);
147
148
        $options = new ListBlobsOptions();
149
        $options->setPrefix($dirname . '/');
150
151
        /** @var ListBlobsResult $listResults */
152
        $listResults = $this->client->listBlobs($this->container, $options);
0 ignored issues
show
Documentation introduced by
$options is of type object<MicrosoftAzure\St...odels\ListBlobsOptions>, but the function expects a object<MicrosoftAzure\St...\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...
153
154
        foreach ($listResults->getBlobs() as $blob) {
155
            /** @var \MicrosoftAzure\Storage\Blob\Models\Blob $blob */
156
            $this->client->deleteBlob($this->container, $blob->getName());
157
        }
158
159
        return true;
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function createDir($dirname, Config $config)
166
    {
167
        $this->write(rtrim($dirname, '/') . '/', ' ', $config);
168
169
        return ['path' => $dirname, 'type' => 'dir'];
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175
    public function has($path)
176
    {
177
        $path = $this->applyPathPrefix($path);
178
179
        try {
180
            $this->client->getBlobMetadata($this->container, $path);
181
        } catch (ServiceException $e) {
182
            if ($e->getCode() !== 404) {
183
                throw $e;
184
            }
185
186
            return false;
187
        }
188
189
        return true;
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195 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...
196
    {
197
        $path = $this->applyPathPrefix($path);
198
199
        /** @var \MicrosoftAzure\Storage\Blob\Models\GetBlobResult $blobResult */
200
        $blobResult = $this->client->getBlob($this->container, $path);
201
        $properties = $blobResult->getProperties();
202
        $content = $this->streamContentsToString($blobResult->getContentStream());
203
204
        return $this->normalizeBlobProperties($path, $properties) + ['contents' => $content];
205
    }
206
207
    /**
208
     * {@inheritdoc}
209
     */
210 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...
211
    {
212
        $path = $this->applyPathPrefix($path);
213
214
        /** @var \MicrosoftAzure\Storage\Blob\Models\GetBlobResult $blobResult */
215
        $blobResult = $this->client->getBlob($this->container, $path);
216
        $properties = $blobResult->getProperties();
217
218
        return $this->normalizeBlobProperties($path, $properties) + ['stream' => $blobResult->getContentStream()];
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224
    public function listContents($directory = '', $recursive = false)
225
    {
226
        $directory = $this->applyPathPrefix($directory);
227
228
        // Append trailing slash only for directory other than root (which after normalization is an empty string).
229
        // Listing for / doesn't work properly otherwise.
230
        if (strlen($directory)) {
231
            $directory = rtrim($directory, '/') . '/';
232
        }
233
234
        $options = new ListBlobsOptions();
235
        $options->setPrefix($directory);
236
237
        if ( ! $recursive) {
238
            $options->setDelimiter('/');
239
        }
240
241
        /** @var ListBlobsResult $listResults */
242
        $listResults = $this->client->listBlobs($this->container, $options);
0 ignored issues
show
Documentation introduced by
$options is of type object<MicrosoftAzure\St...odels\ListBlobsOptions>, but the function expects a object<MicrosoftAzure\St...\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...
243
244
        $contents = [];
245
246
        foreach ($listResults->getBlobs() as $blob) {
247
            $contents[] = $this->normalizeBlobProperties($blob->getName(), $blob->getProperties());
248
        }
249
250
        if ( ! $recursive) {
251
            $contents = array_merge(
252
                $contents,
253
                array_map([$this, 'normalizeBlobPrefix'], $listResults->getBlobPrefixes())
254
            );
255
        }
256
257
        return Util::emulateDirectories($contents);
258
    }
259
260
    /**
261
     * {@inheritdoc}
262
     */
263
    public function getMetadata($path)
264
    {
265
        $path = $this->applyPathPrefix($path);
266
267
        /** @var \MicrosoftAzure\Storage\Blob\Models\GetBlobPropertiesResult $result */
268
        $result = $this->client->getBlobProperties($this->container, $path);
269
270
        return $this->normalizeBlobProperties($path, $result->getProperties());
271
    }
272
273
    /**
274
     * {@inheritdoc}
275
     */
276
    public function getSize($path)
277
    {
278
        return $this->getMetadata($path);
279
    }
280
281
    /**
282
     * {@inheritdoc}
283
     */
284
    public function getMimetype($path)
285
    {
286
        return $this->getMetadata($path);
287
    }
288
289
    /**
290
     * {@inheritdoc}
291
     */
292
    public function getTimestamp($path)
293
    {
294
        return $this->getMetadata($path);
295
    }
296
297
    /**
298
     * Builds the normalized output array.
299
     *
300
     * @param string $path
301
     * @param int    $timestamp
302
     * @param mixed  $content
303
     *
304
     * @return array
305
     */
306
    protected function normalize($path, $timestamp, $content = null)
307
    {
308
        $data = [
309
            'path' => $path,
310
            'timestamp' => (int) $timestamp,
311
            'dirname' => Util::dirname($path),
312
            'type' => 'file',
313
        ];
314
315
        if (is_string($content)) {
316
            $data['contents'] = $content;
317
        }
318
319
        return $data;
320
    }
321
322
    /**
323
     * Builds the normalized output array from a Blob object.
324
     *
325
     * @param string         $path
326
     * @param BlobProperties $properties
327
     *
328
     * @return array
329
     */
330
    protected function normalizeBlobProperties($path, BlobProperties $properties)
331
    {
332
        if (substr($path, -1) === '/') {
333
            return ['type' => 'dir', 'path' => $this->removePathPrefix(rtrim($path, '/'))];
334
        }
335
336
        $path = $this->removePathPrefix($path);
337
338
        return [
339
            'path' => $path,
340
            'timestamp' => (int) $properties->getLastModified()->format('U'),
341
            'dirname' => Util::dirname($path),
342
            'mimetype' => $properties->getContentType(),
343
            'size' => $properties->getContentLength(),
344
            'type' => 'file',
345
        ];
346
    }
347
348
    /**
349
     * Builds the normalized output array from a BlobPrefix object.
350
     *
351
     * @param BlobPrefix $blobPrefix
352
     *
353
     * @return array
354
     */
355
    protected function normalizeBlobPrefix(BlobPrefix $blobPrefix)
356
    {
357
        return ['type' => 'dir', 'path' => $this->removePathPrefix(rtrim($blobPrefix->getName(), '/'))];
358
    }
359
360
    /**
361
     * Retrieves content streamed by Azure into a string.
362
     *
363
     * @param resource $resource
364
     *
365
     * @return string
366
     */
367
    protected function streamContentsToString($resource)
368
    {
369
        return stream_get_contents($resource);
370
    }
371
372
    /**
373
     * Upload a file.
374
     *
375
     * @param string          $path     Path
376
     * @param string|resource $contents Either a string or a stream.
377
     * @param Config          $config   Config
378
     *
379
     * @return array
380
     */
381
    protected function upload($path, $contents, Config $config)
382
    {
383
        $path = $this->applyPathPrefix($path);
384
385
        /** @var CopyBlobResult $result */
386
        $result = $this->client->createBlockBlob(
387
            $this->container,
388
            $path,
389
            $contents,
0 ignored issues
show
Bug introduced by
It seems like $contents defined by parameter $contents on line 381 can also be of type resource; however, MicrosoftAzure\Storage\B...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...
390
            $this->getOptionsFromConfig($config)
0 ignored issues
show
Documentation introduced by
$this->getOptionsFromConfig($config) is of type object<MicrosoftAzure\St...dels\CreateBlobOptions>, but the function expects a object<MicrosoftAzure\St...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...
391
        );
392
393
        return $this->normalize($path, $result->getLastModified()->format('U'), $contents);
394
    }
395
396
    /**
397
     * Retrieve options from a Config instance.
398
     *
399
     * @param Config $config
400
     *
401
     * @return CreateBlobOptions
402
     */
403
    protected function getOptionsFromConfig(Config $config)
404
    {
405
        $options = new CreateBlobOptions();
406
407
        foreach (static::$metaOptions as $option) {
408
            if ( ! $config->has($option)) {
409
                continue;
410
            }
411
412
            call_user_func([$options, "set$option"], $config->get($option));
413
        }
414
415
        if ($mimetype = $config->get('mimetype')) {
416
            $options->setContentType($mimetype);
417
        }
418
419
        return $options;
420
    }
421
}
422