Completed
Push — master ( 8b76b3...681930 )
by Cedric
02:06
created

FlysystemDriver   C

Complexity

Total Complexity 77

Size/Duplication

Total Lines 778
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 7

Test Coverage

Coverage 44.92%

Importance

Changes 21
Bugs 2 Features 4
Metric Value
wmc 77
c 21
b 2
f 4
lcom 2
cbo 7
dl 0
loc 778
ccs 115
cts 256
cp 0.4492
rs 5

40 Methods

Rating   Name   Duplication   Size   Complexity  
A renameFolder() 0 10 2
A __construct() 0 9 1
A processConfiguration() 0 4 1
A mergeConfigurationCapabilities() 0 4 1
A getRootLevelFolder() 0 4 1
A getDefaultFolder() 0 9 2
A folderExists() 0 8 3
A createFolder() 0 16 2
A getPublicUrl() 0 4 1
A deleteFolder() 0 12 2
A fileExists() 0 7 3
A isFolderEmpty() 0 4 1
B addFile() 0 26 6
A createFile() 0 23 3
A copyFileWithinStorage() 0 9 1
A renameFile() 0 8 1
A replaceFile() 0 8 1
A deleteFile() 0 4 1
A hash() 0 4 1
A moveFileWithinStorage() 0 4 1
A moveFolderWithinStorage() 0 4 1
A copyFolderWithinStorage() 0 4 1
A getFileContents() 0 4 1
A setFileContents() 0 6 1
A fileExistsInFolder() 0 6 1
A folderExistsInFolder() 0 6 1
A getFileForLocalProcessing() 0 8 1
A getPermissions() 0 7 1
A dumpFileContents() 0 7 1
A isWithin() 0 14 3
A getFileInfoByIdentifier() 0 10 3
A getFolderInfoByIdentifier() 0 10 1
A getFileInFolder() 0 4 1
B getFilesInFolder() 0 24 3
A getFolderInFolder() 0 5 1
B getFoldersInFolder() 0 25 3
A countFilesInFolder() 0 5 1
A countFoldersInFolder() 0 13 3
A extractFileInformation() 0 23 3
C getSpecificFileInformation() 0 30 11

How to fix   Complexity   

Complex Class

Complex classes like FlysystemDriver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FlysystemDriver, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace CedricZiel\FalFlysystem\Fal;
4
5
use League\Flysystem\Adapter\Local;
6
use League\Flysystem\AdapterInterface;
7
use League\Flysystem\Config;
8
use League\Flysystem\FilesystemInterface;
9
use TYPO3\CMS\Core\Resource\Driver\AbstractHierarchicalFilesystemDriver;
10
use TYPO3\CMS\Core\Resource\Exception\FileOperationErrorException;
11
use TYPO3\CMS\Core\Resource\Exception\InvalidFileNameException;
12
use TYPO3\CMS\Core\Resource\ResourceStorage;
13
use TYPO3\CMS\Core\Type\File\FileInfo;
14
use TYPO3\CMS\Core\Utility\GeneralUtility;
15
use TYPO3\CMS\Core\Utility\PathUtility;
16
use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
17
18
/**
19
 * Class FlysystemDriver
20
 * @package CedricZiel\FalFlysystem\Fal
21
 */
22
abstract class FlysystemDriver extends AbstractHierarchicalFilesystemDriver
23
{
24
    /**
25
     * @var FilesystemInterface
26
     */
27
    protected $filesystem;
28
29
    /**
30
     * @var AdapterInterface
31
     */
32
    protected $adapter;
33
34
    /**
35
     * @var string
36
     */
37
    protected $entryPath;
38
39
    /**
40
     * FlysystemDriver constructor.
41
     * @param array $configuration
42
     */
43 51
    public function __construct(array $configuration = [])
44
    {
45 51
        parent::__construct($configuration);
46
        // The capabilities default of this driver. See CAPABILITY_* constants for possible values
47 51
        $this->capabilities =
48 17
            ResourceStorage::CAPABILITY_BROWSABLE
49 51
            | ResourceStorage::CAPABILITY_PUBLIC
50 51
            | ResourceStorage::CAPABILITY_WRITABLE;
51 51
    }
52
53
    /**
54
     * Processes the configuration for this driver.
55
     * @return void
56
     */
57
    public function processConfiguration()
58
    {
59
        $this->entryPath = $this->configuration['path'];
60
    }
61
62
    /**
63
     * Merges the capabilities merged by the user at the storage
64
     * configuration into the actual capabilities of the driver
65
     * and returns the result.
66
     *
67
     * @param int $capabilities
68
     * @return int
69
     */
70
    public function mergeConfigurationCapabilities($capabilities)
71
    {
72
        // TODO: Implement mergeConfigurationCapabilities() method.
73
    }
74
75
    /**
76
     * Returns the identifier of the root level folder of the storage.
77
     *
78
     * @return string
79
     */
80
    public function getRootLevelFolder()
81
    {
82
        return '/';
83
    }
84
85
    /**
86
     * Returns the identifier of the default folder new files should be put into.
87
     *
88
     * @return string
89
     */
90 3
    public function getDefaultFolder()
91
    {
92 3
        $identifier = '/user_upload/';
93 3
        $createFolder = !$this->folderExists($identifier);
94 3
        if (true === $createFolder) {
95 3
            $identifier = $this->createFolder('user_upload');
96 2
        }
97 3
        return $identifier;
98
    }
99
100
    /**
101
     * Checks if a folder exists.
102
     *
103
     * @param string $folderIdentifier
104
     * @return bool
105
     */
106 6
    public function folderExists($folderIdentifier)
107
    {
108 6
        if ('/' === $folderIdentifier) {
109
            return true;
110
        } else {
111 6
            return ($this->filesystem->has('/' . $folderIdentifier) && $this->filesystem->get('/' . $folderIdentifier)->isDir());
112
        }
113
    }
114
115
    /**
116
     * Creates a folder, within a parent folder.
117
     * If no parent folder is given, a root level folder will be created
118
     *
119
     * @param string $newFolderName
120
     * @param string $parentFolderIdentifier
121
     * @param bool $recursive
122
     * @return string the Identifier of the new folder
123
     */
124 9
    public function createFolder($newFolderName, $parentFolderIdentifier = '', $recursive = false)
125
    {
126 9
        $parentFolderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($parentFolderIdentifier);
127 9
        $newFolderName = trim($newFolderName, '/');
128 9
        if (false === $recursive) {
129 9
            $newFolderName = $this->sanitizeFileName($newFolderName);
130 9
            $newIdentifier = $parentFolderIdentifier . $newFolderName . '/';
131 9
            $this->filesystem->createDir($newIdentifier);
132 6
        } else {
133
            $parts = GeneralUtility::trimExplode('/', $newFolderName);
134
            $parts = array_map(array($this, 'sanitizeFileName'), $parts);
135
            $newFolderName = implode('/', $parts);
136
            $newIdentifier = $parentFolderIdentifier . $newFolderName . '/';
137
        }
138 9
        return $newIdentifier;
139
    }
140
141
    /**
142
     * Returns the public URL to a file.
143
     * Either fully qualified URL or relative to PATH_site (rawurlencoded).
144
     *
145
     * @param string $identifier
146
     * @return string
147
     */
148
    public function getPublicUrl($identifier)
149
    {
150
        return '/';
151
    }
152
153
    /**
154
     * Renames a folder in this storage.
155
     *
156
     * @param string $folderIdentifier
157
     * @param string $newName
158
     * @return array A map of old to new file identifiers of all affected resources
159
     */
160 3
    public function renameFolder($folderIdentifier, $newName)
161
    {
162 3
        $renameResult = $this->filesystem->rename($folderIdentifier, $newName);
163
164 3
        if (true === $renameResult) {
165 3
            return [$folderIdentifier => $newName];
166
        } else {
167
            return [$folderIdentifier => $folderIdentifier];
168
        }
169
    }
170
171
    /**
172
     * Removes a folder in filesystem.
173
     *
174
     * @param string $folderIdentifier
175
     * @param bool $deleteRecursively
176
     * @return bool
177
     * @throws FileOperationErrorException
178
     */
179
    public function deleteFolder($folderIdentifier, $deleteRecursively = false)
180
    {
181
        $folderIdentifier = ltrim($folderIdentifier, '/');
182
        $result = $this->filesystem->deleteDir(rtrim($folderIdentifier, '/'));
183
        if (false === $result) {
184
            throw new FileOperationErrorException(
185
                'Deleting folder "' . $folderIdentifier . '" failed.',
186
                1330119451
187
            );
188
        }
189
        return $result;
190
    }
191
192
    /**
193
     * Checks if a file exists.
194
     *
195
     * @param string $fileIdentifier
196
     * @return bool
197
     */
198 12
    public function fileExists($fileIdentifier)
199
    {
200 12
        if ($this->filesystem->has($fileIdentifier) && !$this->filesystem->get($fileIdentifier)->isDir()) {
201 12
            return true;
202
        }
203 9
        return false;
204
    }
205
206
    /**
207
     * Checks if a folder contains files and (if supported) other folders.
208
     *
209
     * @param string $folderIdentifier
210
     * @return bool TRUE if there are no files and folders within $folder
211
     */
212 3
    public function isFolderEmpty($folderIdentifier)
213
    {
214 3
        return 0 === count($this->filesystem->listContents($folderIdentifier));
215
    }
216
217
    /**
218
     * Adds a file from the local server hard disk to a given path in TYPO3s
219
     * virtual file system. This assumes that the local file exists, so no
220
     * further check is done here! After a successful the original file must
221
     * not exist anymore.
222
     *
223
     * @param string $localFilePath (within PATH_site)
224
     * @param string $targetFolderIdentifier
225
     * @param string $newFileName optional, if not given original name is used
226
     * @param bool $removeOriginal if set the original file will be removed
227
     *                                after successful operation
228
     * @return string the identifier of the new file
229
     */
230 3
    public function addFile($localFilePath, $targetFolderIdentifier, $newFileName = '', $removeOriginal = true)
231
    {
232 3
        $localFilePath = $this->canonicalizeAndCheckFilePath($localFilePath);
233 3
        $newFileName = $this->sanitizeFileName($newFileName !== '' ? $newFileName : PathUtility::basename($localFilePath));
234 3
        $newFileIdentifier = $this->canonicalizeAndCheckFolderIdentifier($targetFolderIdentifier) . $newFileName;
235
236 3
        $targetPath = ltrim($newFileIdentifier, '/');
237
238 3
        $content = file_get_contents($localFilePath);
239
240 3
        if ($removeOriginal) {
241
            if (is_uploaded_file($localFilePath)) {
242
                $result = $this->filesystem->put($targetPath, $content);
243
            } else {
244
                $result = rename($localFilePath, $targetPath);
245
            }
246
            unlink($localFilePath);
247
        } else {
248 3
            $result = $this->filesystem->put($targetPath, $content);
249
        }
250 3
        if ($result === false || !$this->filesystem->has($targetPath)) {
251
            throw new \RuntimeException('Adding file ' . $localFilePath . ' at ' . $newFileIdentifier . ' failed.');
252
        }
253 3
        clearstatcache();
254 3
        return $newFileIdentifier;
255
    }
256
257
    /**
258
     * Creates a new (empty) file and returns the identifier.
259
     *
260
     * @param string $fileName
261
     * @param string $parentFolderIdentifier
262
     * @return string
263
     * @throws InvalidFileNameException
264
     */
265 3
    public function createFile($fileName, $parentFolderIdentifier)
266
    {
267 3
        if (!$this->isValidFilename($fileName)) {
268
            throw new InvalidFileNameException(
269
                'Invalid characters in fileName "' . $fileName . '"',
270 2
                1320572272
271
            );
272
        }
273
274 3
        $parentFolderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($parentFolderIdentifier);
275 3
        $fileIdentifier =  $this->canonicalizeAndCheckFileIdentifier(
276 3
            $parentFolderIdentifier . $this->sanitizeFileName(ltrim($fileName, '/'))
277 2
        );
278
279 3
        $path = ltrim($parentFolderIdentifier . $fileName, '/');
280 3
        $result = $this->filesystem->put($path, '');
281
282 3
        if ($result !== true) {
283
            throw new \RuntimeException('Creating file ' . $fileIdentifier . ' failed.', 1320569854);
284
        }
285
286 3
        return $fileIdentifier;
287
    }
288
289
    /**
290
     * Copies a file *within* the current storage.
291
     * Note that this is only about an inner storage copy action,
292
     * where a file is just copied to another folder in the same storage.
293
     *
294
     * @param string $fileIdentifier
295
     * @param string $targetFolderIdentifier
296
     * @param string $fileName
297
     * @return string the Identifier of the new file
298
     */
299
    public function copyFileWithinStorage($fileIdentifier, $targetFolderIdentifier, $fileName)
300
    {
301
        // TODO: Implement copyFileWithinStorage() method.
302
        DebuggerUtility::var_dump([
303
            '$fileIdentifier' => $fileIdentifier,
304
            '$targetFolderIdentifier' => $targetFolderIdentifier,
305
            '$fileName' => $fileName
306
        ], 'copyFileWithinStorage');
307
    }
308
309
    /**
310
     * Renames a file in this storage.
311
     *
312
     * @param string $fileIdentifier
313
     * @param string $newName The target path (including the file name!)
314
     * @return string The identifier of the file after renaming
315
     */
316
    public function renameFile($fileIdentifier, $newName)
317
    {
318
        // TODO: Implement renameFile() method.
319
        DebuggerUtility::var_dump([
320
            '$fileIdentifier' => $fileIdentifier,
321
            '$newName' => $newName
322
        ], 'renameFile');
323
    }
324
325
    /**
326
     * Replaces a file with file in local file system.
327
     *
328
     * @param string $fileIdentifier
329
     * @param string $localFilePath
330
     * @return bool TRUE if the operation succeeded
331
     */
332
    public function replaceFile($fileIdentifier, $localFilePath)
333
    {
334
        // TODO: Implement replaceFile() method.
335
        DebuggerUtility::var_dump([
336
            '$fileIdentifier' => $fileIdentifier,
337
            '$localFilePath' => $localFilePath
338
        ], 'replaceFile');
339
    }
340
341
    /**
342
     * Removes a file from the filesystem. This does not check if the file is
343
     * still used or if it is a bad idea to delete it for some other reason
344
     * this has to be taken care of in the upper layers (e.g. the Storage)!
345
     *
346
     * @param string $fileIdentifier
347
     * @return bool TRUE if deleting the file succeeded
348
     */
349 3
    public function deleteFile($fileIdentifier)
350
    {
351 3
        return $this->filesystem->delete($fileIdentifier);
352
    }
353
354
    /**
355
     * Creates a hash for a file.
356
     *
357
     * @param string $fileIdentifier
358
     * @param string $hashAlgorithm The hash algorithm to use
359
     * @return string
360
     */
361 2
    public function hash($fileIdentifier, $hashAlgorithm)
362 2
    {
363
        // TODO: Implement hash() method.
364
    }
365
366
    /**
367
     * Moves a file *within* the current storage.
368
     * Note that this is only about an inner-storage move action,
369
     * where a file is just moved to another folder in the same storage.
370
     *
371
     * @param string $fileIdentifier
372
     * @param string $targetFolderIdentifier
373
     * @param string $newFileName
374
     * @return string
375
     */
376
    public function moveFileWithinStorage($fileIdentifier, $targetFolderIdentifier, $newFileName)
377
    {
378
        // TODO: Implement moveFileWithinStorage() method.
379
    }
380
381
    /**
382
     * Folder equivalent to moveFileWithinStorage().
383
     *
384
     * @param string $sourceFolderIdentifier
385
     * @param string $targetFolderIdentifier
386
     * @param string $newFolderName
387
     * @return array All files which are affected, map of old => new file identifiers
388
     */
389
    public function moveFolderWithinStorage($sourceFolderIdentifier, $targetFolderIdentifier, $newFolderName)
390
    {
391
        // TODO: Implement moveFolderWithinStorage() method.
392
    }
393
394
    /**
395
     * Folder equivalent to copyFileWithinStorage().
396
     *
397
     * @param string $sourceFolderIdentifier
398
     * @param string $targetFolderIdentifier
399
     * @param string $newFolderName
400
     * @return bool
401
     */
402
    public function copyFolderWithinStorage($sourceFolderIdentifier, $targetFolderIdentifier, $newFolderName)
403
    {
404
        // TODO: Implement copyFolderWithinStorage() method.
405
    }
406
407
    /**
408
     * Returns the contents of a file. Beware that this requires to load the
409
     * complete file into memory and also may require fetching the file from an
410
     * external location. So this might be an expensive operation (both in terms
411
     * of processing resources and money) for large files.
412
     *
413
     * @param string $fileIdentifier
414
     * @return string The file contents
415
     */
416 3
    public function getFileContents($fileIdentifier)
417
    {
418 3
        return $this->filesystem->read($fileIdentifier);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->filesystem->read($fileIdentifier); of type string|false adds false to the return on line 418 which is incompatible with the return type declared by the interface TYPO3\CMS\Core\Resource\...erface::getFileContents of type string. It seems like you forgot to handle an error condition.
Loading history...
419
    }
420
421
    /**
422
     * Sets the contents of a file to the specified value.
423
     *
424
     * @param string $fileIdentifier
425
     * @param string $contents
426
     * @return int The number of bytes written to the file
427
     */
428 3
    public function setFileContents($fileIdentifier, $contents)
429
    {
430 3
        $this->filesystem->put($fileIdentifier, $contents);
431
432 3
        return $this->filesystem->getSize($fileIdentifier);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->filesystem->getSize($fileIdentifier); of type integer|false adds false to the return on line 432 which is incompatible with the return type declared by the interface TYPO3\CMS\Core\Resource\...erface::setFileContents of type integer. It seems like you forgot to handle an error condition.
Loading history...
433
    }
434
435
    /**
436
     * Checks if a file inside a folder exists
437
     *
438
     * @param string $fileName
439
     * @param string $folderIdentifier
440
     * @return bool
441
     */
442 3
    public function fileExistsInFolder($fileName, $folderIdentifier)
443
    {
444 3
        $identifier = $folderIdentifier . '/' . $fileName;
445 3
        $identifier = $this->canonicalizeAndCheckFileIdentifier($identifier);
446 3
        return $this->fileExists($identifier);
447
    }
448
449
    /**
450
     * Checks if a folder inside a folder exists.
451
     *
452
     * @param string $folderName
453
     * @param string $folderIdentifier
454
     * @return bool
455
     */
456 3
    public function folderExistsInFolder($folderName, $folderIdentifier)
457
    {
458 3
        $identifier = $folderIdentifier . '/' . $folderName;
459 3
        $identifier = $this->canonicalizeAndCheckFolderIdentifier($identifier);
460 3
        return $this->folderExists($identifier);
461
    }
462
463
    /**
464
     * Returns a path to a local copy of a file for processing it. When changing the
465
     * file, you have to take care of replacing the current version yourself!
466
     *
467
     * @param string $fileIdentifier
468
     * @param bool $writable Set this to FALSE if you only need the file for read
469
     *                       operations. This might speed up things, e.g. by using
470
     *                       a cached local version. Never modify the file if you
471
     *                       have set this flag!
472
     * @return string The path to the file on the local disk
473
     */
474
    public function getFileForLocalProcessing($fileIdentifier, $writable = true)
475
    {
476
        // TODO: Implement getFileForLocalProcessing() method.
477
        DebuggerUtility::var_dump([
478
            '$fileIdentifier' => $fileIdentifier,
479
            '$writable' => $writable,
480
        ], 'getFileForLocalProcessing');
481
    }
482
483
    /**
484
     * Returns the permissions of a file/folder as an array
485
     * (keys r, w) of boolean flags
486
     *
487
     * @param string $identifier
488
     * @return array
489
     */
490
    public function getPermissions($identifier)
491
    {
492
        return array(
493
            'r' => true,
494
            'w' => true,
495
        );
496
    }
497
498
    /**
499
     * Directly output the contents of the file to the output
500
     * buffer. Should not take care of header files or flushing
501
     * buffer before. Will be taken care of by the Storage.
502
     *
503
     * @param string $identifier
504
     * @return void
505
     */
506
    public function dumpFileContents($identifier)
507
    {
508
        // TODO: Implement dumpFileContents() method.
509
        DebuggerUtility::var_dump([
510
            '$identifier' => $identifier,
511
        ], 'dumpFileContents');
512
    }
513
514
    /**
515
     * Checks if a given identifier is within a container, e.g. if
516
     * a file or folder is within another folder.
517
     * This can e.g. be used to check for web-mounts.
518
     *
519
     * Hint: this also needs to return TRUE if the given identifier
520
     * matches the container identifier to allow access to the root
521
     * folder of a filemount.
522
     *
523
     * @param string $folderIdentifier
524
     * @param string $identifier identifier to be checked against $folderIdentifier
525
     * @return bool TRUE if $content is within or matches $folderIdentifier
526
     */
527
    public function isWithin($folderIdentifier, $identifier)
528
    {
529
        $folderIdentifier = $this->canonicalizeAndCheckFileIdentifier($folderIdentifier);
530
        $entryIdentifier = $this->canonicalizeAndCheckFileIdentifier($identifier);
531
        if ($folderIdentifier === $entryIdentifier) {
532
            return true;
533
        }
534
        // File identifier canonicalization will not modify a single slash so
535
        // we must not append another slash in that case.
536
        if ($folderIdentifier !== '/') {
537
            $folderIdentifier .= '/';
538
        }
539
        return GeneralUtility::isFirstPartOfStr($entryIdentifier, $folderIdentifier);
540
    }
541
542
    /**
543
     * Returns information about a file.
544
     *
545
     * @param string $fileIdentifier
546
     * @param array $propertiesToExtract Array of properties which are be extracted
547
     *                                   If empty all will be extracted
548
     * @return array
549
     */
550
    public function getFileInfoByIdentifier($fileIdentifier, array $propertiesToExtract = [])
551
    {
552
        $relativeDriverPath = ltrim($fileIdentifier, '/');
553
        if (!$this->filesystem->has($relativeDriverPath) || !$this->filesystem->get($relativeDriverPath)->isFile()) {
554
            throw new \InvalidArgumentException('File ' . $fileIdentifier . ' does not exist.', 1314516809);
555
        }
556
        $dirPath = PathUtility::dirname($fileIdentifier);
557
        $dirPath = $this->canonicalizeAndCheckFolderIdentifier($dirPath);
558
        return $this->extractFileInformation($relativeDriverPath, $dirPath, $propertiesToExtract);
559
    }
560
561
    /**
562
     * Returns information about a file.
563
     *
564
     * @param string $folderIdentifier
565
     * @return array
566
     */
567 3
    public function getFolderInfoByIdentifier($folderIdentifier)
568
    {
569 3
        $folderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($folderIdentifier);
570
571
        return [
572 3
            'identifier' => $folderIdentifier,
573 3
            'name' => PathUtility::basename($folderIdentifier),
574 3
            'storage' => $this->storageUid
575 2
        ];
576
    }
577
578
    /**
579
     * Returns the identifier of a file inside the folder
580
     *
581
     * @param string $fileName
582
     * @param string $folderIdentifier
583
     * @return string file identifier
584
     */
585
    public function getFileInFolder($fileName, $folderIdentifier)
586
    {
587
        return $this->canonicalizeAndCheckFileIdentifier($folderIdentifier . '/' . $fileName);
588
    }
589
590
    /**
591
     * Returns a list of files inside the specified path
592
     *
593
     * @param string $folderIdentifier
594
     * @param int $start
595
     * @param int $numberOfItems
596
     * @param bool $recursive
597
     * @param array $filenameFilterCallbacks callbacks for filtering the items
598
     * @param string $sort Property name used to sort the items.
599
     *                     Among them may be: '' (empty, no sorting), name,
600
     *                     fileext, size, tstamp and rw.
601
     *                     If a driver does not support the given property, it
602
     *                     should fall back to "name".
603
     * @param bool $sortRev TRUE to indicate reverse sorting (last to first)
604
     * @return array of FileIdentifiers
605
     */
606 3
    public function getFilesInFolder(
607
        $folderIdentifier,
608
        $start = 0,
609
        $numberOfItems = 0,
610
        $recursive = false,
611
        array $filenameFilterCallbacks = [],
612
        $sort = '',
613
        $sortRev = false
614
    ) {
615 3
        $calculatedFolderIdentifier = ltrim($this->canonicalizeAndCheckFolderIdentifier($folderIdentifier), '/');
616 3
        $contents = $this->filesystem->listContents($calculatedFolderIdentifier);
617 3
        $files = [];
618
619
        /*
620
         * Filter directories
621
         */
622 3
        foreach ($contents as $directoryItem) {
623 3
            if ('file' === $directoryItem['type']) {
624 3
                $files['/' . $directoryItem['path']] = '/' . $directoryItem['path'];
625 2
            }
626 2
        }
627
628 3
        return $files;
629
    }
630
631
    /**
632
     * Returns the identifier of a folder inside the folder
633
     *
634
     * @param string $folderName The name of the target folder
635
     * @param string $folderIdentifier
636
     * @return string folder identifier
637
     */
638 3
    public function getFolderInFolder($folderName, $folderIdentifier)
639
    {
640 3
        $folderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($folderIdentifier . '/' . $folderName);
641 3
        return $folderIdentifier;
642
    }
643
644
    /**
645
     * Returns a list of folders inside the specified path
646
     *
647
     * @param string $folderIdentifier
648
     * @param int $start
649
     * @param int $numberOfItems
650
     * @param bool $recursive
651
     * @param array $folderNameFilterCallbacks callbacks for filtering the items
652
     * @param string $sort Property name used to sort the items.
653
     *                     Among them may be: '' (empty, no sorting), name,
654
     *                     fileext, size, tstamp and rw.
655
     *                     If a driver does not support the given property, it
656
     *                     should fall back to "name".
657
     * @param bool $sortRev TRUE to indicate reverse sorting (last to first)
658
     * @return array of Folder Identifier
659
     * @TODO: Implement pagination with $start and $numberOfItems
660
     * @TODO: Implement directory filter callbacks
661
     * @TODO: Implement sorting
662
     */
663 3
    public function getFoldersInFolder(
664
        $folderIdentifier,
665
        $start = 0,
666
        $numberOfItems = 0,
667
        $recursive = false,
668
        array $folderNameFilterCallbacks = [],
669
        $sort = '',
670
        $sortRev = false
671
    ) {
672 3
        $calculatedFolderIdentifier = ltrim($this->canonicalizeAndCheckFolderIdentifier($folderIdentifier), '/');
673 3
        $contents = $this->filesystem->listContents($calculatedFolderIdentifier);
674 3
        $directories = [];
675
676
        /*
677
         * Filter directories
678
         */
679 3
        foreach ($contents as $directoryItem) {
680 3
            if ('dir' === $directoryItem['type']) {
681 3
                $directories['/' . $directoryItem['path']]
682 3
                    = '/' . $directoryItem['path'];
683 2
            }
684 2
        }
685
686 3
        return $directories;
687
    }
688
689
    /**
690
     * Returns the number of files inside the specified path
691
     *
692
     * @param string $folderIdentifier
693
     * @param bool $recursive
694
     * @param array $filenameFilterCallbacks callbacks for filtering the items
695
     * @return int Number of files in folder
696
     * @TODO: Implement recursive count
697
     * @TODO: Implement filename filtering
698
     */
699
    public function countFilesInFolder($folderIdentifier, $recursive = false, array $filenameFilterCallbacks = [])
700
    {
701
702
        return count($this->getFilesInFolder($folderIdentifier, 0, 0, $recursive, $filenameFilterCallbacks));
703
    }
704
705
    /**
706
     * Returns the number of folders inside the specified path
707
     *
708
     * @param string $folderIdentifier
709
     * @param bool $recursive
710
     * @param array $folderNameFilterCallbacks callbacks for filtering the items
711
     * @return int Number of folders in folder
712
     */
713 3
    public function countFoldersInFolder($folderIdentifier, $recursive = false, array $folderNameFilterCallbacks = [])
714
    {
715 3
        $count = 0;
716 3
        $filesystemRelativeIdentifier = ltrim($folderIdentifier, '/');
717 3
        $directoryListing = $this->filesystem->listContents($filesystemRelativeIdentifier);
718 3
        foreach ($directoryListing as $entry) {
719 3
            if ('dir' === $entry['type']) {
720 3
                $count++;
721 2
            }
722 2
        }
723
724 3
        return $count;
725
    }
726
727
    /**
728
     * Extracts information about a file from the filesystem.
729
     *
730
     * @param string $filePath The absolute path to the file
731
     * @param string $containerPath The relative path to the file's container
732
     * @param array $propertiesToExtract array of properties which should be returned, if empty all will be extracted
733
     * @return array
734
     */
735
    protected function extractFileInformation($filePath, $containerPath, array $propertiesToExtract = array())
736
    {
737
        if (empty($propertiesToExtract)) {
738
            $propertiesToExtract = array(
739
                'size',
740
                'atime',
741
                'atime',
742
                'mtime',
743
                'ctime',
744
                'mimetype',
745
                'name',
746
                'identifier',
747
                'identifier_hash',
748
                'storage',
749
                'folder_hash'
750
            );
751
        }
752
        $fileInformation = array();
753
        foreach ($propertiesToExtract as $property) {
754
            $fileInformation[$property] = $this->getSpecificFileInformation($filePath, $containerPath, $property);
755
        }
756
        return $fileInformation;
757
    }
758
759
    /**
760
     * Extracts a specific FileInformation from the FileSystems.
761
     *
762
     * @param string $fileIdentifier
763
     * @param string $containerPath
764
     * @param string $property
765
     *
766
     * @return bool|int|string
767
     * @throws \InvalidArgumentException
768
     */
769
    public function getSpecificFileInformation($fileIdentifier, $containerPath, $property)
770
    {
771
        $identifier = $this->canonicalizeAndCheckFileIdentifier($containerPath . PathUtility::basename($fileIdentifier));
772
        $file = $this->filesystem->getMetadata($fileIdentifier);
773
774
        switch ($property) {
775
            case 'size':
776
                return $file['size'];
777
            case 'atime':
778
                return $file['timestamp'];
779
            case 'mtime':
780
                return $file['timestamp'];
781
            case 'ctime':
782
                return $file['timestamp'];
783
            case 'name':
784
                return PathUtility::basename($fileIdentifier);
785
            case 'mimetype':
786
                return 'application/octet-stream';
787
            case 'identifier':
788
                return $identifier;
789
            case 'storage':
790
                return $this->storageUid;
791
            case 'identifier_hash':
792
                return $this->hashIdentifier($identifier);
793
            case 'folder_hash':
794
                return $this->hashIdentifier($this->getParentFolderIdentifierOfIdentifier($identifier));
795
            default:
796
                throw new \InvalidArgumentException(sprintf('The information "%s" is not available.', $property));
797
        }
798
    }
799
}
800