1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Gaufrette; |
4
|
|
|
|
5
|
|
|
use Gaufrette\Adapter\ListKeysAware; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* A filesystem is used to store and retrieve files. |
9
|
|
|
* |
10
|
|
|
* @author Antoine Hérault <[email protected]> |
11
|
|
|
* @author Leszek Prabucki <[email protected]> |
12
|
|
|
*/ |
13
|
|
|
class Filesystem |
14
|
|
|
{ |
15
|
|
|
protected $adapter; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Contains File objects created with $this->createFile() method. |
19
|
|
|
* |
20
|
|
|
* @var array |
21
|
|
|
*/ |
22
|
|
|
protected $fileRegister = array(); |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @param Adapter $adapter A configured Adapter instance |
26
|
|
|
*/ |
27
|
|
|
public function __construct(Adapter $adapter) |
28
|
|
|
{ |
29
|
|
|
$this->adapter = $adapter; |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Returns the adapter. |
34
|
|
|
* |
35
|
|
|
* @return Adapter |
36
|
|
|
*/ |
37
|
|
|
public function getAdapter() |
38
|
|
|
{ |
39
|
|
|
return $this->adapter; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Indicates whether the file matching the specified key exists. |
44
|
|
|
* |
45
|
|
|
* @param string $key |
46
|
|
|
* |
47
|
|
|
* @return bool TRUE if the file exists, FALSE otherwise |
48
|
|
|
*/ |
49
|
|
|
public function has($key) |
50
|
|
|
{ |
51
|
|
|
return $this->adapter->exists($key); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Renames a file. |
56
|
|
|
* |
57
|
|
|
* @param string $sourceKey |
58
|
|
|
* @param string $targetKey |
59
|
|
|
* |
60
|
|
|
* @return bool TRUE if the rename was successful |
61
|
|
|
* |
62
|
|
|
* @throws Exception\FileNotFound when sourceKey does not exist |
63
|
|
|
* @throws Exception\UnexpectedFile when targetKey exists |
64
|
|
|
* @throws \RuntimeException when cannot rename |
65
|
|
|
*/ |
66
|
|
|
public function rename($sourceKey, $targetKey) |
67
|
|
|
{ |
68
|
|
|
$this->assertHasFile($sourceKey); |
69
|
|
|
|
70
|
|
|
if ($this->has($targetKey)) { |
71
|
|
|
throw new Exception\UnexpectedFile($targetKey); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
if (!$this->adapter->rename($sourceKey, $targetKey)) { |
75
|
|
|
throw new \RuntimeException(sprintf('Could not rename the "%s" key to "%s".', $sourceKey, $targetKey)); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
if ($this->isFileInRegister($sourceKey)) { |
79
|
|
|
$this->fileRegister[$targetKey] = $this->fileRegister[$sourceKey]; |
80
|
|
|
unset($this->fileRegister[$sourceKey]); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
return true; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Returns the file matching the specified key. |
88
|
|
|
* |
89
|
|
|
* @param string $key Key of the file |
90
|
|
|
* @param bool $create Whether to create the file if it does not exist |
91
|
|
|
* |
92
|
|
|
* @throws Exception\FileNotFound |
93
|
|
|
* |
94
|
|
|
* @return File |
95
|
|
|
*/ |
96
|
|
|
public function get($key, $create = false) |
97
|
|
|
{ |
98
|
|
|
if (!$create) { |
99
|
|
|
$this->assertHasFile($key); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
return $this->createFile($key); |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Writes the given content into the file. |
107
|
|
|
* |
108
|
|
|
* @param string $key Key of the file |
109
|
|
|
* @param string $content Content to write in the file |
110
|
|
|
* @param bool $overwrite Whether to overwrite the file if exists |
111
|
|
|
* |
112
|
|
|
* @throws Exception\FileAlreadyExists When file already exists and overwrite is false |
113
|
|
|
* @throws \RuntimeException When for any reason content could not be written |
114
|
|
|
* |
115
|
|
|
* @return int The number of bytes that were written into the file |
116
|
|
|
*/ |
117
|
|
|
public function write($key, $content, $overwrite = false) |
118
|
|
|
{ |
119
|
|
|
if (!$overwrite && $this->has($key)) { |
120
|
|
|
throw new Exception\FileAlreadyExists($key); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
$numBytes = $this->adapter->write($key, $content); |
124
|
|
|
|
125
|
|
|
if (false === $numBytes) { |
126
|
|
|
throw new \RuntimeException(sprintf('Could not write the "%s" key content.', $key)); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
return $numBytes; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Reads the content from the file. |
134
|
|
|
* |
135
|
|
|
* @param string $key Key of the file |
136
|
|
|
* |
137
|
|
|
* @throws Exception\FileNotFound when file does not exist |
138
|
|
|
* @throws \RuntimeException when cannot read file |
139
|
|
|
* |
140
|
|
|
* @return string |
141
|
|
|
*/ |
142
|
|
View Code Duplication |
public function read($key) |
|
|
|
|
143
|
|
|
{ |
144
|
|
|
$this->assertHasFile($key); |
145
|
|
|
|
146
|
|
|
$content = $this->adapter->read($key); |
147
|
|
|
|
148
|
|
|
if (false === $content) { |
149
|
|
|
throw new \RuntimeException(sprintf('Could not read the "%s" key content.', $key)); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
return $content; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Deletes the file matching the specified key. |
157
|
|
|
* |
158
|
|
|
* @param string $key |
159
|
|
|
* |
160
|
|
|
* @throws \RuntimeException when cannot read file |
161
|
|
|
* |
162
|
|
|
* @return bool |
163
|
|
|
*/ |
164
|
|
View Code Duplication |
public function delete($key) |
|
|
|
|
165
|
|
|
{ |
166
|
|
|
$this->assertHasFile($key); |
167
|
|
|
|
168
|
|
|
if ($this->adapter->delete($key)) { |
169
|
|
|
$this->removeFromRegister($key); |
170
|
|
|
|
171
|
|
|
return true; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
throw new \RuntimeException(sprintf('Could not remove the "%s" key.', $key)); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Returns an array of all keys. |
179
|
|
|
* |
180
|
|
|
* @return array |
181
|
|
|
*/ |
182
|
|
|
public function keys() |
183
|
|
|
{ |
184
|
|
|
return $this->adapter->keys(); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* Lists keys beginning with given prefix |
189
|
|
|
* (no wildcard / regex matching). |
190
|
|
|
* |
191
|
|
|
* if adapter implements ListKeysAware interface, adapter's implementation will be used, |
192
|
|
|
* in not, ALL keys will be requested and iterated through. |
193
|
|
|
* |
194
|
|
|
* @param string $prefix |
195
|
|
|
* |
196
|
|
|
* @return array |
197
|
|
|
*/ |
198
|
|
|
public function listKeys($prefix = '') |
199
|
|
|
{ |
200
|
|
|
if ($this->adapter instanceof ListKeysAware) { |
201
|
|
|
return $this->adapter->listKeys($prefix); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
$dirs = array(); |
205
|
|
|
$keys = array(); |
206
|
|
|
|
207
|
|
|
foreach ($this->keys() as $key) { |
208
|
|
|
if (empty($prefix) || 0 === strpos($key, $prefix)) { |
209
|
|
|
if ($this->adapter->isDirectory($key)) { |
210
|
|
|
$dirs[] = $key; |
211
|
|
|
} else { |
212
|
|
|
$keys[] = $key; |
213
|
|
|
} |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
return array( |
218
|
|
|
'keys' => $keys, |
219
|
|
|
'dirs' => $dirs, |
220
|
|
|
); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* Lists files beginning with given prefix |
225
|
|
|
* (no wildcard / regex matching) |
226
|
|
|
* |
227
|
|
|
* if adapter implements ListFilesAware interface, adapter's implementation will be used, |
228
|
|
|
* otherwise return empty array |
229
|
|
|
* |
230
|
|
|
* @param string $prefix |
231
|
|
|
* @return array |
232
|
|
|
*/ |
233
|
|
|
public function listFiles($prefix = '') |
234
|
|
|
{ |
235
|
|
|
if ($this->adapter instanceof ListFilesAware) { |
|
|
|
|
236
|
|
|
return $this->adapter->listFiles($prefix); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
return []; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Returns the last modified time of the specified file. |
244
|
|
|
* |
245
|
|
|
* @param string $key |
246
|
|
|
* |
247
|
|
|
* @return int An UNIX like timestamp |
248
|
|
|
*/ |
249
|
|
|
public function mtime($key) |
250
|
|
|
{ |
251
|
|
|
$this->assertHasFile($key); |
252
|
|
|
|
253
|
|
|
return $this->adapter->mtime($key); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* Returns the checksum of the specified file's content. |
258
|
|
|
* |
259
|
|
|
* @param string $key |
260
|
|
|
* |
261
|
|
|
* @return string A MD5 hash |
262
|
|
|
*/ |
263
|
|
|
public function checksum($key) |
264
|
|
|
{ |
265
|
|
|
$this->assertHasFile($key); |
266
|
|
|
|
267
|
|
|
if ($this->adapter instanceof Adapter\ChecksumCalculator) { |
268
|
|
|
return $this->adapter->checksum($key); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
return Util\Checksum::fromContent($this->read($key)); |
|
|
|
|
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Returns the size of the specified file's content. |
276
|
|
|
* |
277
|
|
|
* @param string $key |
278
|
|
|
* |
279
|
|
|
* @return int File size in Bytes |
280
|
|
|
*/ |
281
|
|
|
public function size($key) |
282
|
|
|
{ |
283
|
|
|
$this->assertHasFile($key); |
284
|
|
|
|
285
|
|
|
if ($this->adapter instanceof Adapter\SizeCalculator) { |
286
|
|
|
return $this->adapter->size($key); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
return Util\Size::fromContent($this->read($key)); |
|
|
|
|
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Gets a new stream instance of the specified file. |
294
|
|
|
* |
295
|
|
|
* @param $key |
296
|
|
|
* |
297
|
|
|
* @return Stream|Stream\InMemoryBuffer |
298
|
|
|
*/ |
299
|
|
|
public function createStream($key) |
300
|
|
|
{ |
301
|
|
|
if ($this->adapter instanceof Adapter\StreamFactory) { |
302
|
|
|
return $this->adapter->createStream($key); |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
return new Stream\InMemoryBuffer($this, $key); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Creates a new file in a filesystem. |
310
|
|
|
* |
311
|
|
|
* @param $key |
312
|
|
|
* |
313
|
|
|
* @return File |
314
|
|
|
*/ |
315
|
|
|
public function createFile($key) |
316
|
|
|
{ |
317
|
|
|
if (false === $this->isFileInRegister($key)) { |
318
|
|
|
if ($this->adapter instanceof Adapter\FileFactory) { |
319
|
|
|
$this->fileRegister[$key] = $this->adapter->createFile($key, $this); |
320
|
|
|
} else { |
321
|
|
|
$this->fileRegister[$key] = new File($key, $this); |
322
|
|
|
} |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
return $this->fileRegister[$key]; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* Get the mime type of the provided key. |
330
|
|
|
* |
331
|
|
|
* @param string $key |
332
|
|
|
* |
333
|
|
|
* @return string |
334
|
|
|
*/ |
335
|
|
|
public function mimeType($key) |
336
|
|
|
{ |
337
|
|
|
$this->assertHasFile($key); |
338
|
|
|
|
339
|
|
|
if ($this->adapter instanceof Adapter\MimeTypeProvider) { |
340
|
|
|
return $this->adapter->mimeType($key); |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
throw new \LogicException(sprintf( |
344
|
|
|
'Adapter "%s" cannot provide MIME type', |
345
|
|
|
get_class($this->adapter) |
346
|
|
|
)); |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* Checks if matching file by given key exists in the filesystem. |
351
|
|
|
* |
352
|
|
|
* Key must be non empty string, otherwise it will throw Exception\FileNotFound |
353
|
|
|
* {@see http://php.net/manual/en/function.empty.php} |
354
|
|
|
* |
355
|
|
|
* @param string $key |
356
|
|
|
* |
357
|
|
|
* @throws Exception\FileNotFound when sourceKey does not exist |
358
|
|
|
*/ |
359
|
|
|
private function assertHasFile($key) |
360
|
|
|
{ |
361
|
|
|
if (!empty($key) && !$this->has($key)) { |
362
|
|
|
throw new Exception\FileNotFound($key); |
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Checks if matching File object by given key exists in the fileRegister. |
368
|
|
|
* |
369
|
|
|
* @param string $key |
370
|
|
|
* |
371
|
|
|
* @return bool |
372
|
|
|
*/ |
373
|
|
|
private function isFileInRegister($key) |
374
|
|
|
{ |
375
|
|
|
return array_key_exists($key, $this->fileRegister); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* Clear files register. |
380
|
|
|
*/ |
381
|
|
|
public function clearFileRegister() |
382
|
|
|
{ |
383
|
|
|
$this->fileRegister = array(); |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* Removes File object from register. |
388
|
|
|
* |
389
|
|
|
* @param string $key |
390
|
|
|
*/ |
391
|
|
|
public function removeFromRegister($key) |
392
|
|
|
{ |
393
|
|
|
if ($this->isFileInRegister($key)) { |
394
|
|
|
unset($this->fileRegister[$key]); |
395
|
|
|
} |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
/** |
399
|
|
|
* @param string $key |
400
|
|
|
* |
401
|
|
|
* @return bool |
402
|
|
|
*/ |
403
|
|
|
public function isDirectory($key) |
404
|
|
|
{ |
405
|
|
|
return $this->adapter->isDirectory($key); |
406
|
|
|
} |
407
|
|
|
} |
408
|
|
|
|
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.