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