Completed
Push — master ( 1fad21...9cf1bc )
by Cedric
01:46
created

FlysystemDriver::renameFile()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3.0032

Importance

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