Completed
Push — master ( df6565...80b7ba )
by Cedric
01:46
created

FlysystemDriver::copyFileToTemporaryPath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 17
ccs 0
cts 12
cp 0
rs 9.4285
cc 2
eloc 11
nc 2
nop 1
crap 6
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
            $result = $this->filesystem->put($targetPath, $content);
270 2
            unlink($localFilePath);
271
        } else {
272 3
            $result = $this->filesystem->put($targetPath, $content);
273
        }
274 3
        if ($result === false || !$this->filesystem->has($targetPath)) {
275
            throw new \RuntimeException('Adding file ' . $localFilePath . ' at ' . $newFileIdentifier . ' failed.');
276
        }
277 3
        clearstatcache();
278 3
        return $newFileIdentifier;
279
    }
280
281
    /**
282
     * Creates a new (empty) file and returns the identifier.
283
     *
284
     * @param string $fileName
285
     * @param string $parentFolderIdentifier
286
     * @return string
287
     * @throws InvalidFileNameException
288
     */
289 3
    public function createFile($fileName, $parentFolderIdentifier)
290
    {
291 3
        if (!$this->isValidFilename($fileName)) {
292
            throw new InvalidFileNameException(
293
                'Invalid characters in fileName "' . $fileName . '"',
294
                1320572272
295
            );
296
        }
297
298 3
        $parentFolderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($parentFolderIdentifier);
299 3
        $fileIdentifier = $this->canonicalizeAndCheckFileIdentifier(
300 3
            $parentFolderIdentifier . $this->sanitizeFileName(ltrim($fileName, '/'))
301 2
        );
302
303 3
        $path = ltrim($parentFolderIdentifier . $fileName, '/');
304 3
        $result = $this->filesystem->put($path, '');
305
306 3
        if ($result !== true) {
307
            throw new \RuntimeException('Creating file ' . $fileIdentifier . ' failed.', 1320569854);
308
        }
309
310 3
        return $fileIdentifier;
311
    }
312
313
    /**
314
     * Copies a file *within* the current storage.
315
     * Note that this is only about an inner storage copy action,
316
     * where a file is just copied to another folder in the same storage.
317
     *
318
     * @param string $fileIdentifier
319
     * @param string $targetFolderIdentifier
320
     * @param string $fileName
321
     * @return string the Identifier of the new file
322
     */
323
    public function copyFileWithinStorage($fileIdentifier, $targetFolderIdentifier, $fileName)
324
    {
325
        // TODO: Implement copyFileWithinStorage() method.
326
        DebuggerUtility::var_dump([
327
            '$fileIdentifier' => $fileIdentifier,
328
            '$targetFolderIdentifier' => $targetFolderIdentifier,
329
            '$fileName' => $fileName
330
        ], 'copyFileWithinStorage');
331
    }
332
333
    /**
334
     * Renames a file in this storage.
335
     *
336
     * @param string $fileIdentifier
337
     * @param string $newName The target path (including the file name!)
338
     * @return string The identifier of the file after renaming
339
     * @throws ExistingTargetFileNameException
340
     */
341 6
    public function renameFile($fileIdentifier, $newName)
342
    {
343
        // Makes sure the Path given as parameter is valid
344 6
        $newName = $this->sanitizeFileName($newName);
345
346 6
        $newIdentifier = $this->canonicalizeAndCheckFileIdentifier($newName);
347
        // The target should not exist already
348 6
        if ($this->fileExists($newIdentifier)) {
349 3
            throw new ExistingTargetFileNameException(
350 3
                'The target file "' . $newIdentifier . '" already exists.',
351 1
                1320291063
352 2
            );
353
        }
354
355 3
        $sourcePath = ltrim($fileIdentifier, '/');
356 3
        $targetPath = ltrim($newIdentifier, '/');
357 3
        $result = $this->filesystem->rename($sourcePath, $targetPath);
358 3
        if ($result === false) {
359
            throw new \RuntimeException('Renaming file ' . $sourcePath . ' to ' . $targetPath . ' failed.', 1320375115);
360
        }
361 3
        return $newIdentifier;
362 2
    }
363
364
    /**
365
     * Replaces a file with file in local file system.
366
     *
367
     * @param string $fileIdentifier
368
     * @param string $localFilePath
369
     * @return bool TRUE if the operation succeeded
370
     */
371
    public function replaceFile($fileIdentifier, $localFilePath)
372
    {
373
        // TODO: Implement replaceFile() method.
374
        DebuggerUtility::var_dump([
375
            '$fileIdentifier' => $fileIdentifier,
376
            '$localFilePath' => $localFilePath
377
        ], 'replaceFile');
378
    }
379
380
    /**
381
     * Removes a file from the filesystem. This does not check if the file is
382
     * still used or if it is a bad idea to delete it for some other reason
383
     * this has to be taken care of in the upper layers (e.g. the Storage)!
384
     *
385
     * @param string $fileIdentifier
386
     * @return bool TRUE if deleting the file succeeded
387
     */
388 3
    public function deleteFile($fileIdentifier)
389
    {
390 3
        return $this->filesystem->delete($fileIdentifier);
391
    }
392
393
    /**
394
     * Creates a hash for a file.
395
     *
396
     * @param string $fileIdentifier
397
     * @param string $hashAlgorithm The hash algorithm to use
398
     * @return string
399
     */
400 9
    public function hash($fileIdentifier, $hashAlgorithm)
401
    {
402 9
        if (!in_array($hashAlgorithm, ['sha1', 'md5'])) {
403 3
            throw new \InvalidArgumentException(
404 3
                'Hash algorithm "' . $hashAlgorithm . '" is not supported.',
405 1
                1304964032
406 2
            );
407
        }
408 6
        $propertiesToHash = ['name', 'size', 'mtime', 'identifier'];
409
        switch ($hashAlgorithm) {
410 6
            case 'sha1':
411 3
                $hash = sha1(implode('-', $this->getFileInfoByIdentifier($fileIdentifier, $propertiesToHash)));
412 3
                break;
413 2
            case 'md5':
414 3
                $hash = md5(implode('-', $this->getFileInfoByIdentifier($fileIdentifier, $propertiesToHash)));
415 3
                break;
416
            default:
417
                throw new \RuntimeException('Hash algorithm ' . $hashAlgorithm . ' is not implemented.', 1329644451);
418
        }
419 6
        return $hash;
420
    }
421
422
    /**
423
     * Moves a file *within* the current storage.
424
     * Note that this is only about an inner-storage move action,
425
     * where a file is just moved to another folder in the same storage.
426
     *
427
     * @param string $fileIdentifier
428
     * @param string $targetFolderIdentifier
429
     * @param string $newFileName
430
     * @return string
431
     */
432
    public function moveFileWithinStorage($fileIdentifier, $targetFolderIdentifier, $newFileName)
433
    {
434
        // TODO: Implement moveFileWithinStorage() method.
435
    }
436
437
    /**
438
     * Folder equivalent to moveFileWithinStorage().
439
     *
440
     * @param string $sourceFolderIdentifier
441
     * @param string $targetFolderIdentifier
442
     * @param string $newFolderName
443
     * @return array All files which are affected, map of old => new file identifiers
444
     */
445
    public function moveFolderWithinStorage($sourceFolderIdentifier, $targetFolderIdentifier, $newFolderName)
446
    {
447
        // TODO: Implement moveFolderWithinStorage() method.
448
    }
449
450
    /**
451
     * Folder equivalent to copyFileWithinStorage().
452
     *
453
     * @param string $sourceFolderIdentifier
454
     * @param string $targetFolderIdentifier
455
     * @param string $newFolderName
456
     * @return bool
457
     */
458
    public function copyFolderWithinStorage($sourceFolderIdentifier, $targetFolderIdentifier, $newFolderName)
459
    {
460
        // TODO: Implement copyFolderWithinStorage() method.
461
    }
462
463
    /**
464
     * Returns the contents of a file. Beware that this requires to load the
465
     * complete file into memory and also may require fetching the file from an
466
     * external location. So this might be an expensive operation (both in terms
467
     * of processing resources and money) for large files.
468
     *
469
     * @param string $fileIdentifier
470
     * @return string The file contents
471
     */
472 3
    public function getFileContents($fileIdentifier)
473
    {
474 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 474 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...
475
    }
476
477
    /**
478
     * Sets the contents of a file to the specified value.
479
     *
480
     * @param string $fileIdentifier
481
     * @param string $contents
482
     * @return int The number of bytes written to the file
483
     */
484 3
    public function setFileContents($fileIdentifier, $contents)
485
    {
486 3
        $this->filesystem->put($fileIdentifier, $contents);
487
488 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 488 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...
489
    }
490
491
    /**
492
     * Checks if a file inside a folder exists
493
     *
494
     * @param string $fileName
495
     * @param string $folderIdentifier
496
     * @return bool
497
     */
498 3
    public function fileExistsInFolder($fileName, $folderIdentifier)
499
    {
500 3
        $identifier = $folderIdentifier . '/' . $fileName;
501 3
        $identifier = $this->canonicalizeAndCheckFileIdentifier($identifier);
502 3
        return $this->fileExists($identifier);
503
    }
504
505
    /**
506
     * Checks if a folder inside a folder exists.
507
     *
508
     * @param string $folderName
509
     * @param string $folderIdentifier
510
     * @return bool
511
     */
512 3
    public function folderExistsInFolder($folderName, $folderIdentifier)
513
    {
514 3
        $identifier = $folderIdentifier . '/' . $folderName;
515 3
        $identifier = $this->canonicalizeAndCheckFolderIdentifier($identifier);
516 3
        return $this->folderExists($identifier);
517
    }
518
519
    /**
520
     * Returns a path to a local copy of a file for processing it. When changing the
521
     * file, you have to take care of replacing the current version yourself!
522
     *
523
     * @param string $fileIdentifier
524
     * @param bool $writable Set this to FALSE if you only need the file for read
525
     *                       operations. This might speed up things, e.g. by using
526
     *                       a cached local version. Never modify the file if you
527
     *                       have set this flag!
528
     * @return string The path to the file on the local disk
529
     */
530
    public function getFileForLocalProcessing($fileIdentifier, $writable = true)
531
    {
532
        return $this->copyFileToTemporaryPath($fileIdentifier);
533
    }
534
535
    /**
536
     * Returns the permissions of a file/folder as an array
537
     * (keys r, w) of boolean flags
538
     *
539
     * @param string $identifier
540
     * @return array
541
     */
542
    public function getPermissions($identifier)
543
    {
544
        return array(
545
            'r' => true,
546
            'w' => true,
547
        );
548
    }
549
550
    /**
551
     * Directly output the contents of the file to the output
552
     * buffer. Should not take care of header files or flushing
553
     * buffer before. Will be taken care of by the Storage.
554
     *
555
     * @param string $identifier
556
     * @return void
557
     */
558
    public function dumpFileContents($identifier)
559
    {
560
        // TODO: Implement dumpFileContents() method.
561
        DebuggerUtility::var_dump([
562
            '$identifier' => $identifier,
563
        ], 'dumpFileContents');
564
    }
565
566
    /**
567
     * Checks if a given identifier is within a container, e.g. if
568
     * a file or folder is within another folder.
569
     * This can e.g. be used to check for web-mounts.
570
     *
571
     * Hint: this also needs to return TRUE if the given identifier
572
     * matches the container identifier to allow access to the root
573
     * folder of a filemount.
574
     *
575
     * @param string $folderIdentifier
576
     * @param string $identifier identifier to be checked against $folderIdentifier
577
     * @return bool TRUE if $content is within or matches $folderIdentifier
578
     */
579
    public function isWithin($folderIdentifier, $identifier)
580
    {
581
        $folderIdentifier = $this->canonicalizeAndCheckFileIdentifier($folderIdentifier);
582
        $entryIdentifier = $this->canonicalizeAndCheckFileIdentifier($identifier);
583
        if ($folderIdentifier === $entryIdentifier) {
584
            return true;
585
        }
586
        // File identifier canonicalization will not modify a single slash so
587
        // we must not append another slash in that case.
588
        if ($folderIdentifier !== '/') {
589
            $folderIdentifier .= '/';
590
        }
591
        return GeneralUtility::isFirstPartOfStr($entryIdentifier, $folderIdentifier);
592
    }
593
594
    /**
595
     * Returns information about a file.
596
     *
597
     * @param string $fileIdentifier
598
     * @param array $propertiesToExtract Array of properties which are be extracted
599
     *                                   If empty all will be extracted
600
     * @return array
601
     * @throws FileDoesNotExistException
602
     */
603 6
    public function getFileInfoByIdentifier($fileIdentifier, array $propertiesToExtract = [])
604
    {
605 6
        $relativeDriverPath = ltrim($fileIdentifier, '/');
606 6
        if (!$this->filesystem->has($relativeDriverPath) || !$this->filesystem->get($relativeDriverPath)->isFile()) {
607
            throw new FileDoesNotExistException('File ' . $fileIdentifier . ' does not exist.', 1314516809);
608
        }
609 6
        $dirPath = PathUtility::dirname($fileIdentifier);
610 6
        $dirPath = $this->canonicalizeAndCheckFolderIdentifier($dirPath);
611 6
        return $this->extractFileInformation($relativeDriverPath, $dirPath, $propertiesToExtract);
612
    }
613
614
    /**
615
     * Returns information about a file.
616
     *
617
     * @param string $folderIdentifier
618
     * @return array
619
     * @throws FolderDoesNotExistException
620
     */
621 3
    public function getFolderInfoByIdentifier($folderIdentifier)
622
    {
623 3
        $folderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($folderIdentifier);
624
625 3
        if (!$this->folderExists($folderIdentifier)) {
626
            throw new FolderDoesNotExistException(
627
                'Folder "' . $folderIdentifier . '" does not exist.',
628
                1314516810
629
            );
630
        }
631
        return [
632 3
            'identifier' => $folderIdentifier,
633 3
            'name' => PathUtility::basename($folderIdentifier),
634 3
            'storage' => $this->storageUid
635 2
        ];
636
    }
637
638
    /**
639
     * Returns the identifier of a file inside the folder
640
     *
641
     * @param string $fileName
642
     * @param string $folderIdentifier
643
     * @return string file identifier
644
     */
645
    public function getFileInFolder($fileName, $folderIdentifier)
646
    {
647
        return $this->canonicalizeAndCheckFileIdentifier($folderIdentifier . '/' . $fileName);
648
    }
649
650
    /**
651
     * Returns a list of files inside the specified path
652
     *
653
     * @param string $folderIdentifier
654
     * @param int $start
655
     * @param int $numberOfItems
656
     * @param bool $recursive
657
     * @param array $filenameFilterCallbacks callbacks for filtering the items
658
     * @param string $sort Property name used to sort the items.
659
     *                     Among them may be: '' (empty, no sorting), name,
660
     *                     fileext, size, tstamp and rw.
661
     *                     If a driver does not support the given property, it
662
     *                     should fall back to "name".
663
     * @param bool $sortRev TRUE to indicate reverse sorting (last to first)
664
     * @return array of FileIdentifiers
665
     */
666 3
    public function getFilesInFolder(
667
        $folderIdentifier,
668
        $start = 0,
669
        $numberOfItems = 0,
670
        $recursive = false,
671
        array $filenameFilterCallbacks = [],
672
        $sort = '',
673
        $sortRev = false
674
    ) {
675 3
        $calculatedFolderIdentifier = ltrim($this->canonicalizeAndCheckFolderIdentifier($folderIdentifier), '/');
676 3
        $contents = $this->filesystem->listContents($calculatedFolderIdentifier);
677 3
        $files = [];
678
679
        /*
680
         * Filter directories
681
         */
682 3
        foreach ($contents as $directoryItem) {
683 3
            if ('file' === $directoryItem['type']) {
684 3
                $files['/' . $directoryItem['path']] = '/' . $directoryItem['path'];
685 2
            }
686 2
        }
687
688 3
        return $files;
689
    }
690
691
    /**
692
     * Returns the identifier of a folder inside the folder
693
     *
694
     * @param string $folderName The name of the target folder
695
     * @param string $folderIdentifier
696
     * @return string folder identifier
697
     */
698 3
    public function getFolderInFolder($folderName, $folderIdentifier)
699
    {
700 3
        $folderIdentifier = $this->canonicalizeAndCheckFolderIdentifier($folderIdentifier . '/' . $folderName);
701 3
        return $folderIdentifier;
702
    }
703
704
    /**
705
     * Returns a list of folders inside the specified path
706
     *
707
     * @param string $folderIdentifier
708
     * @param int $start
709
     * @param int $numberOfItems
710
     * @param bool $recursive
711
     * @param array $folderNameFilterCallbacks callbacks for filtering the items
712
     * @param string $sort Property name used to sort the items.
713
     *                     Among them may be: '' (empty, no sorting), name,
714
     *                     fileext, size, tstamp and rw.
715
     *                     If a driver does not support the given property, it
716
     *                     should fall back to "name".
717
     * @param bool $sortRev TRUE to indicate reverse sorting (last to first)
718
     * @return array of Folder Identifier
719
     * @TODO: Implement pagination with $start and $numberOfItems
720
     * @TODO: Implement directory filter callbacks
721
     * @TODO: Implement sorting
722
     */
723 3
    public function getFoldersInFolder(
724
        $folderIdentifier,
725
        $start = 0,
726
        $numberOfItems = 0,
727
        $recursive = false,
728
        array $folderNameFilterCallbacks = [],
729
        $sort = '',
730
        $sortRev = false
731
    ) {
732 3
        $calculatedFolderIdentifier = ltrim($this->canonicalizeAndCheckFolderIdentifier($folderIdentifier), '/');
733 3
        $contents = $this->filesystem->listContents($calculatedFolderIdentifier);
734 3
        $directories = [];
735
736
        /*
737
         * Filter directories
738
         */
739 3
        foreach ($contents as $directoryItem) {
740 3
            if ('dir' === $directoryItem['type']) {
741 3
                $directories['/' . $directoryItem['path']]
742 3
                    = '/' . $directoryItem['path'];
743 2
            }
744 2
        }
745
746 3
        return $directories;
747
    }
748
749
    /**
750
     * Returns the number of files inside the specified path
751
     *
752
     * @param string $folderIdentifier
753
     * @param bool $recursive
754
     * @param array $filenameFilterCallbacks callbacks for filtering the items
755
     * @return int Number of files in folder
756
     * @TODO: Implement recursive count
757
     * @TODO: Implement filename filtering
758
     */
759
    public function countFilesInFolder($folderIdentifier, $recursive = false, array $filenameFilterCallbacks = [])
760
    {
761
762
        return count($this->getFilesInFolder($folderIdentifier, 0, 0, $recursive, $filenameFilterCallbacks));
763
    }
764
765
    /**
766
     * Returns the number of folders inside the specified path
767
     *
768
     * @param string $folderIdentifier
769
     * @param bool $recursive
770
     * @param array $folderNameFilterCallbacks callbacks for filtering the items
771
     * @return int Number of folders in folder
772
     */
773 3
    public function countFoldersInFolder($folderIdentifier, $recursive = false, array $folderNameFilterCallbacks = [])
774
    {
775 3
        $count = 0;
776 3
        $filesystemRelativeIdentifier = ltrim($folderIdentifier, '/');
777 3
        $directoryListing = $this->filesystem->listContents($filesystemRelativeIdentifier);
778 3
        foreach ($directoryListing as $entry) {
779 3
            if ('dir' === $entry['type']) {
780 3
                $count++;
781 2
            }
782 2
        }
783
784 3
        return $count;
785
    }
786
787
    /**
788
     * Extracts information about a file from the filesystem.
789
     *
790
     * @param string $filePath The absolute path to the file
791
     * @param string $containerPath The relative path to the file's container
792
     * @param array $propertiesToExtract array of properties which should be returned, if empty all will be extracted
793
     * @return array
794
     */
795 6
    protected function extractFileInformation($filePath, $containerPath, array $propertiesToExtract = array())
796
    {
797 6
        if (empty($propertiesToExtract)) {
798
            $propertiesToExtract = array(
799
                'size',
800
                'atime',
801
                'atime',
802
                'mtime',
803
                'ctime',
804
                'mimetype',
805
                'name',
806
                'identifier',
807
                'identifier_hash',
808
                'storage',
809
                'folder_hash'
810
            );
811
        }
812 6
        $fileInformation = array();
813 6
        foreach ($propertiesToExtract as $property) {
814 6
            $fileInformation[$property] = $this->getSpecificFileInformation($filePath, $containerPath, $property);
815 4
        }
816 6
        return $fileInformation;
817
    }
818
819
    /**
820
     * Extracts a specific FileInformation from the FileSystems.
821
     *
822
     * @param string $fileIdentifier
823
     * @param string $containerPath
824
     * @param string $property
825
     *
826
     * @return bool|int|string
827
     * @throws \InvalidArgumentException
828
     */
829 6
    public function getSpecificFileInformation($fileIdentifier, $containerPath, $property)
830
    {
831 6
        $identifier = $this->canonicalizeAndCheckFileIdentifier($containerPath . PathUtility::basename($fileIdentifier));
832 6
        $file = $this->filesystem->getMetadata($fileIdentifier);
833
834
        switch ($property) {
835 6
            case 'size':
836 6
                return $file['size'];
837 4
            case 'atime':
838
                return $file['timestamp'];
839 4
            case 'mtime':
840 6
                return $file['timestamp'];
841 4
            case 'ctime':
842
                return $file['timestamp'];
843 4
            case 'name':
844 6
                return PathUtility::basename($fileIdentifier);
845 4
            case 'mimetype':
846
                return 'application/octet-stream';
847 4
            case 'identifier':
848 6
                return $identifier;
849
            case 'storage':
850
                return $this->storageUid;
851
            case 'identifier_hash':
852
                return $this->hashIdentifier($identifier);
853
            case 'folder_hash':
854
                return $this->hashIdentifier($this->getParentFolderIdentifierOfIdentifier($identifier));
855
            default:
856
                throw new \InvalidArgumentException(sprintf('The information "%s" is not available.', $property));
857
        }
858
    }
859
860
    /**
861
     * Copies a file to a temporary path and returns that path.
862
     *
863
     * @param string $fileIdentifier
864
     * @return string The temporary path
865
     * @throws \RuntimeException
866
     */
867
    protected function copyFileToTemporaryPath($fileIdentifier)
868
    {
869
        $temporaryPath = $this->getTemporaryPathForFile($fileIdentifier);
870
        $contents = $this->filesystem->read(ltrim($fileIdentifier, '/'));
871
872
        $res = fopen($temporaryPath, 'w');
873
        $result = fwrite($res, $contents);
874
        fclose($res);
875
876
        if (false === $result) {
877
            throw new \RuntimeException(
878
                'Copying file "' . $fileIdentifier . '" to temporary path "' . $temporaryPath . '" failed.',
879
                1320577649
880
            );
881
        }
882
        return $temporaryPath;
883
    }
884
}
885