Completed
Push — master ( 3ac5c4...483436 )
by Anton
03:47
created

StorageBucket::rename()   B

Complexity

Conditions 3
Paths 6

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 15
nc 6
nop 2
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Spiral\Storage\Entities;
10
11
use Psr\Http\Message\StreamInterface;
12
use Psr\Http\Message\UploadedFileInterface;
13
use Psr\Log\LoggerAwareInterface;
14
use Spiral\Core\Component;
15
use Spiral\Core\Container\InjectableInterface;
16
use Spiral\Debug\Traits\BenchmarkTrait;
17
use Spiral\Debug\Traits\LoggerTrait;
18
use Spiral\Files\Streams\StreamableInterface;
19
use Spiral\Storage\BucketInterface;
20
use Spiral\Storage\Exceptions\BucketException;
21
use Spiral\Storage\Exceptions\ServerException;
22
use Spiral\Storage\ServerInterface;
23
use Spiral\Storage\StorageManager;
24
25
/**
26
 * Default implementation of storage bucket.
27
 */
28
class StorageBucket extends Component implements
29
    BucketInterface,
30
    LoggerAwareInterface,
31
    InjectableInterface
32
{
33
    use BenchmarkTrait, LoggerTrait;
34
35
    /**
36
     * To let IoC who need to inject us.
37
     */
38
    const INJECTOR = StorageManager::class;
39
40
    /**
41
     * @var string
42
     */
43
    private $name = '';
44
45
    /**
46
     * @var string
47
     */
48
    private $prefix = '';
49
50
    /**
51
     * @var ServerInterface
52
     */
53
    private $server = null;
54
55
    /**
56
     * @var array
57
     */
58
    private $options = [];
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function __construct(
64
        string $name,
65
        string $prefix,
66
        array $options,
67
        ServerInterface $server
68
    ) {
69
        $this->name = $name;
70
        $this->prefix = $prefix;
71
        $this->options = $options;
72
        $this->server = $server;
73
    }
74
75
    /**
76
     * {@inheritdoc}
77
     */
78
    public function getName(): string
79
    {
80
        return $this->name;
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86
    public function getServer(): ServerInterface
87
    {
88
        return $this->server;
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94
    public function withOption(string $name, $value): BucketInterface
95
    {
96
        $bucket = clone $this;
97
        $bucket->options[$name] = $value;
98
99
        return $bucket;
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function getOption(string $name, $default = null)
106
    {
107
        return isset($this->options[$name]) ? $this->options[$name] : $default;
108
    }
109
110
111
    /**
112
     * {@inheritdoc}
113
     */
114
    public function getPrefix(): string
115
    {
116
        return $this->prefix;
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122
    public function hasAddress(string $address)
123
    {
124
        if (strpos($address, $this->prefix) === 0) {
125
            return strlen($this->prefix);
126
        }
127
128
        return false;
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    public function buildAddress(string $name): string
135
    {
136
        return $this->prefix . $name;
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142
    public function exists(string $name): bool
143
    {
144
        $this->logger()->info(
145
            "Check existence of '{$this->buildAddress($name)}' in '{$this->getName()}' bucket."
146
        );
147
148
        $benchmark = $this->benchmark($this->getName(), "exists::{$this->buildAddress($name)}");
149
        try {
150
            return (bool)$this->server->exists($this, $name);
151
        } catch (ServerException$e) {
152
            throw new BucketException($e->getMessage(), $e->getCode(), $e);
153
        } finally {
154
            $this->benchmark($benchmark);
155
        }
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161
    public function size(string $name)
162
    {
163
        $this->logger()->info(
164
            "Get size of '{$this->buildAddress($name)}' in '{$this->getName()}' bucket."
165
        );
166
167
        $benchmark = $this->benchmark($this->getName(), "size::{$this->buildAddress($name)}");
168
        try {
169
            return $this->server->size($this, $name);
170
        } catch (ServerException$e) {
171
            throw new BucketException($e->getMessage(), $e->getCode(), $e);
172
        } finally {
173
            $this->benchmark($benchmark);
174
        }
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    public function put(string $name, $source): string
181
    {
182
        $this->logger()->info(
183
            "Put '{$this->buildAddress($name)}' in '{$this->getName()}' bucket."
184
        );
185
186
        if ($source instanceof UploadedFileInterface || $source instanceof StreamableInterface) {
187
            //Known simplification for UploadedFile
188
            $source = $source->getStream();
189
        }
190
191
        if (is_resource($source)) {
192
            $source = \GuzzleHttp\Psr7\stream_for($source);
193
        }
194
195
        $benchmark = $this->benchmark($this->getName(), "put::{$this->buildAddress($name)}");
196
        try {
197
            $this->server->put($this, $name, $source);
198
199
            //Reopening
200
            return $this->buildAddress($name);
201
        } catch (ServerException$e) {
202
            throw new BucketException($e->getMessage(), $e->getCode(), $e);
203
        } finally {
204
            $this->benchmark($benchmark);
205
        }
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211
    public function allocateFilename(string $name): string
212
    {
213
        $this->logger()->info(
214
            "Allocate filename of '{$this->buildAddress($name)}' in '{$this->getName()}' bucket."
215
        );
216
217
        $benchmark = $this->benchmark(
218
            $this->getName(), "filename::{$this->buildAddress($name)}"
219
        );
220
221
        try {
222
            return $this->getServer()->allocateFilename($this, $name);
223
        } catch (ServerException$e) {
224
            throw new BucketException($e->getMessage(), $e->getCode(), $e);
225
        } finally {
226
            $this->benchmark($benchmark);
227
        }
228
    }
229
230
    /**
231
     * {@inheritdoc}
232
     */
233
    public function allocateStream(string $name): StreamInterface
234
    {
235
        $this->logger()->info(
236
            "Get stream for '{$this->buildAddress($name)}' in '{$this->getName()}' bucket."
237
        );
238
239
        $benchmark = $this->benchmark(
240
            $this->getName(), "stream::{$this->buildAddress($name)}"
241
        );
242
243
        try {
244
            return $this->getServer()->allocateStream($this, $name);
245
        } catch (ServerException$e) {
246
            throw new BucketException($e->getMessage(), $e->getCode(), $e);
247
        } finally {
248
            $this->benchmark($benchmark);
249
        }
250
    }
251
252
    /**
253
     * {@inheritdoc}
254
     */
255
    public function delete(string $name)
256
    {
257
        $this->logger()->info(
258
            "Delete '{$this->buildAddress($name)}' in '{$this->getName()}' bucket."
259
        );
260
261
        $benchmark = $this->benchmark(
262
            $this->getName(), "delete::{$this->buildAddress($name)}"
263
        );
264
265
        try {
266
            $this->server->delete($this, $name);
267
        } catch (ServerException$e) {
268
            throw new BucketException($e->getMessage(), $e->getCode(), $e);
269
        } finally {
270
            $this->benchmark($benchmark);
271
        }
272
    }
273
274
    /**
275
     * {@inheritdoc}
276
     */
277
    public function rename(string $oldName, string $newName): string
278
    {
279
        if ($oldName == $newName) {
280
            return true;
281
        }
282
283
        $this->logger()->info(
284
            "Rename '{$this->buildAddress($oldName)}' to '{$this->buildAddress($newName)}' "
285
            . "in '{$this->getName()}' bucket."
286
        );
287
288
        $benchmark = $this->benchmark(
289
            $this->getName(), "rename::{$this->buildAddress($oldName)}"
290
        );
291
292
        try {
293
            $this->server->rename($this, $oldName, $newName);
294
295
            return $this->buildAddress($newName);
296
        } catch (ServerException$e) {
297
            throw new BucketException($e->getMessage(), $e->getCode(), $e);
298
        } finally {
299
            $this->benchmark($benchmark);
300
        }
301
    }
302
303
    /**
304
     * {@inheritdoc}
305
     */
306
    public function copy(BucketInterface $destination, string $name): string
307
    {
308
        if ($destination == $this) {
309
            return $this->buildAddress($name);
310
        }
311
312
        //Internal copying
313
        if ($this->getServer() === $destination->getServer()) {
314
            $this->logger()->info(
315
                "Internal copy of '{$this->buildAddress($name)}' "
316
                . "to '{$destination->buildAddress($name)}' in '{$this->getName()}' bucket."
317
            );
318
319
            $benchmark = $this->benchmark(
320
                $this->getName(), "copy::{$this->buildAddress($name)}"
321
            );
322
323
            try {
324
                $this->getServer()->copy($this, $destination, $name);
325
            } catch (ServerException$e) {
326
                throw new BucketException($e->getMessage(), $e->getCode(), $e);
327
            } finally {
328
                $this->benchmark($benchmark);
329
            }
330
        } else {
331
            $this->logger()->info(
332
                "External copy of '{$this->getName()}'.'{$this->buildAddress($name)}' "
333
                . "to '{$destination->getName()}'.'{$destination->buildAddress($name)}'."
334
            );
335
336
            $destination->put($name, $stream = $this->allocateStream($name));
337
            $stream->detach();
338
        }
339
340
        return $destination->buildAddress($name);
341
    }
342
343
    /**
344
     * {@inheritdoc}
345
     */
346
    public function replace(BucketInterface $destination, string $name): string
347
    {
348
        if ($destination == $this) {
349
            return $this->buildAddress($name);
350
        }
351
352
        //Internal copying
353
        if ($this->getServer() == $destination->getServer()) {
354
            $this->logger()->info(
355
                "Internal move '{$this->buildAddress($name)}' "
356
                . "to '{$destination->buildAddress($name)}' in '{$this->getName()}' bucket."
357
            );
358
359
            $benchmark = $this->benchmark(
360
                $this->getName(), "replace::{$this->buildAddress($name)}"
361
            );
362
363
            try {
364
                $this->getServer()->replace($this, $destination, $name);
365
            } catch (ServerException$e) {
366
                throw new BucketException($e->getMessage(), $e->getCode(), $e);
367
            } finally {
368
                $this->benchmark($benchmark);
369
            }
370
        } else {
371
            $this->logger()->info(
372
                "External move '{$this->getName()}'.'{$this->buildAddress($name)}'"
373
                . " to '{$destination->getName()}'.'{$destination->buildAddress($name)}'."
374
            );
375
376
            //Copying using temporary stream (buffer)
377
            $destination->put($name, $stream = $this->allocateStream($name));
378
379
            if ($stream->detach()) {
380
                //Dropping temporary stream
381
                $this->delete($name);
382
            }
383
        }
384
385
        return $destination->buildAddress($name);
386
    }
387
}
388