Completed
Branch 0.1.x (de528d)
by f
03:18 queued 01:28
created

UnifiedArchive::getFileResource()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 3
b 0
f 0
nc 2
nop 1
dl 0
loc 6
ccs 3
cts 4
cp 0.75
crap 2.0625
rs 10
1
<?php
2
namespace wapmorgan\UnifiedArchive;
3
4
use Exception;
5
use InvalidArgumentException;
6
use wapmorgan\UnifiedArchive\Formats\BasicFormat;
7
use wapmorgan\UnifiedArchive\Formats\Bzip;
8
use wapmorgan\UnifiedArchive\Formats\Cab;
9
use wapmorgan\UnifiedArchive\Formats\Gzip;
10
use wapmorgan\UnifiedArchive\Formats\Iso;
11
use wapmorgan\UnifiedArchive\Formats\Lzma;
12
use wapmorgan\UnifiedArchive\Formats\Rar;
13
use wapmorgan\UnifiedArchive\Formats\SevenZip;
14
use wapmorgan\UnifiedArchive\Formats\Tar;
15
use wapmorgan\UnifiedArchive\Formats\Zip;
16
17
/**
18
 * Class which represents archive in one of supported formats.
19
 */
20
class UnifiedArchive
21
{
22
    const VERSION = '0.1.2';
23
24
    const ZIP = 'zip';
25
    const SEVEN_ZIP = '7zip';
26
    const RAR = 'rar';
27
    const GZIP = 'gzip';
28
    const BZIP = 'bzip2';
29
    const LZMA = 'lzma2';
30
    const ISO = 'iso';
31
    const CAB = 'cab';
32
    const TAR = 'tar';
33
    const TAR_GZIP = 'tgz';
34
    const TAR_BZIP = 'tbz2';
35
    const TAR_LZMA = 'txz';
36
    const TAR_LZW = 'tar.z';
37
38
    /** @var array List of archive format handlers */
39
    protected static $formatHandlers = [
40
        self::ZIP => Zip::class,
41
        self::SEVEN_ZIP => SevenZip::class,
42
        self::RAR => Rar::class,
43
        self::GZIP => Gzip::class,
44
        self::BZIP => Bzip::class,
45
        self::LZMA => Lzma::class,
46
        self::ISO => Iso::class,
47
        self::CAB => Cab::class,
48
        self::TAR => Tar::class,
49
        self::TAR_GZIP => Tar::class,
50
        self::TAR_BZIP => Tar::class,
51
        self::TAR_LZMA => Tar::class,
52
        self::TAR_LZW => Tar::class,
53
    ];
54
55
    /** @var array List of archive formats with support state */
56
    static protected $enabledTypes = [];
57
58
    /** @var string Type of current archive */
59
    protected $type;
60
61
    /** @var BasicFormat Adapter for current archive */
62
    protected $archive;
63
64
    /** @var array List of files in current archive */
65
    protected $files;
66
67
    /** @var int Number of files */
68
    protected $filesQuantity;
69
70
    /** @var int Size of uncompressed files */
71
    protected $uncompressedFilesSize;
72
73
    /** @var int Size of compressed files */
74
    protected $compressedFilesSize;
75
76
    /** @var int Size of archive */
77
    protected $archiveSize;
78
79
    /**
80
     * Creates instance with right type
81
     *
82
     * @param  string $fileName Filename
83
     * @return UnifiedArchive|null Returns UnifiedArchive in case of successful reading of the file
84
     * @throws \Exception
85
     */
86 20
    public static function open($fileName)
87
    {
88 20
        self::checkRequirements();
89
90 20
        if (!file_exists($fileName) || !is_readable($fileName))
91
            throw new Exception('Could not open file: '.$fileName);
92
93 20
        $type = self::detectArchiveType($fileName);
94 20
        if (!self::canOpenType($type)) {
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type false; however, parameter $type of wapmorgan\UnifiedArchive...dArchive::canOpenType() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

94
        if (!self::canOpenType(/** @scrutinizer ignore-type */ $type)) {
Loading history...
95
            return null;
96
        }
97
98 20
        return new self($fileName, $type);
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type false; however, parameter $type of wapmorgan\UnifiedArchive...dArchive::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

98
        return new self($fileName, /** @scrutinizer ignore-type */ $type);
Loading history...
99
    }
100
101
    /**
102
     * Checks whether archive can be opened with current system configuration
103
     *
104
     * @param string $fileName
105
     * @return boolean
106
     */
107 18
    public static function canOpenArchive($fileName)
108
    {
109 18
        self::checkRequirements();
110
111 18
        $type = self::detectArchiveType($fileName);
112
113 18
        return $type !== false && self::canOpenType($type);
114
    }
115
116
    /**
117
     * Checks whether specific archive type can be opened with current system configuration
118
     *
119
     * @param string $type One of predefined archive types
120
     * @return boolean
121
     */
122 22
    public static function canOpenType($type)
123
    {
124 22
        self::checkRequirements();
125
126 22
        return isset(self::$enabledTypes[$type])
127 22
            ? self::$enabledTypes[$type]
128 22
            : false;
129
    }
130
131
    /**
132
     * @param string $type One of predefined archive types
133
     * @return bool
134
     */
135 2
    public static function canCreateType($type)
136
    {
137 2
        self::checkRequirements();
138 2
        return isset(self::$enabledTypes[$type])
139 2
            ? call_user_func([static::$formatHandlers[$type], 'canCreateArchive'])
140 2
            : false;
141
    }
142
143
    /**
144
     * Detect archive type by its filename or content
145
     *
146
     * @param string $fileName
147
     * @param bool $contentCheck
148
     * @return string|boolean One of UnifiedArchive type constants OR false if type is not detected
149
     */
150 33
    public static function detectArchiveType($fileName, $contentCheck = true)
151
    {
152
        // by file name
153 33
        $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
154
155
        // by file name
156 33
        if (stripos($fileName, '.tar.') !== false && preg_match('~\.(?<ext>tar\.(gz|bz2|xz|z))$~', strtolower($fileName), $match)) {
157 9
            switch ($match['ext']) {
158 9
                case 'tar.gz':
159 4
                    return self::TAR_GZIP;
160 5
                case 'tar.bz2':
161 4
                    return self::TAR_BZIP;
162 1
                case 'tar.xz':
163 1
                    return self::TAR_LZMA;
164
                case 'tar.z':
165
                    return self::TAR_LZW;
166
            }
167
        }
168
169
        switch ($ext) {
170 24
            case 'zip':
171 6
                return self::ZIP;
172 18
            case '7z':
173 4
                return self::SEVEN_ZIP;
174 14
            case 'rar':
175 1
                return self::RAR;
176 13
            case 'gz':
177
                return self::GZIP;
178 13
            case 'bz2':
179
                return self::BZIP;
180 13
            case 'xz':
181
                return self::LZMA;
182 13
            case 'iso':
183 4
                return self::ISO;
184 9
            case 'cab':
185
                return self::CAB;
186 9
            case 'tar':
187 6
                return self::TAR;
188 3
            case 'tgz':
189 1
                return self::TAR_GZIP;
190 2
            case 'tbz2':
191 1
                return self::TAR_BZIP;
192 1
            case 'txz':
193 1
                return self::TAR_LZMA;
194
195
        }
196
197
        // by content
198
        if ($contentCheck) {
199
            $mime_type = mime_content_type($fileName);
200
            switch ($mime_type) {
201
                case 'application/zip':
202
                    return self::ZIP;
203
                case 'application/x-7z-compressed':
204
                    return self::SEVEN_ZIP;
205
                case 'application/x-rar':
206
                    return self::RAR;
207
                case 'application/zlib':
208
                    return self::GZIP;
209
                case 'application/x-bzip2':
210
                    return self::BZIP;
211
                case 'application/x-lzma':
212
                    return self::LZMA;
213
                case 'application/x-iso9660-image':
214
                    return self::ISO;
215
                case 'application/vnd.ms-cab-compressed':
216
                    return self::CAB;
217
                case 'application/x-tar':
218
                    return self::TAR;
219
                case 'application/x-gtar':
220
                    return self::TAR_GZIP;
221
222
            }
223
        }
224
225
        return false;
226
    }
227
228
    /**
229
     * Opens the file as one of supported formats
230
     *
231
     * @param string $fileName Filename
232
     * @param string $type Archive type.
233
     * @throws Exception If archive can not be opened
234
     */
235 20
    public function __construct($fileName, $type)
236
    {
237 20
        self::checkRequirements();
238
239 20
        $this->type = $type;
240 20
        $this->archiveSize = filesize($fileName);
241
242 20
        if (!isset(static::$formatHandlers[$type]))
243
            throw new Exception('Unsupported archive type: '.$type.' of archive '.$fileName);
244
245 20
        $handler_class = static::$formatHandlers[$type];
246
247 20
        $this->archive = new $handler_class($fileName);
248 20
        $this->scanArchive();
249 20
    }
250
251
    /**
252
     * Rescans array after modification
253
     */
254 20
    protected function scanArchive()
255
    {
256 20
        $information = $this->archive->getArchiveInformation();
257 20
        $this->files = $information->files;
258 20
        $this->compressedFilesSize = $information->compressedFilesSize;
259 20
        $this->uncompressedFilesSize = $information->uncompressedFilesSize;
260 20
        $this->filesQuantity = count($information->files);
261 20
    }
262
263
    /**
264
     * Closes archive
265
     */
266 20
    public function __destruct()
267
    {
268 20
        unset($this->archive);
269 20
    }
270
271
    /**
272
     * Returns an instance of class implementing PclZipOriginalInterface
273
     * interface.
274
     *
275
     * @return PclzipZipInterface Returns an instance of a class implementing PclZipOriginalInterface
276
     * @throws Exception
277
     */
278
    public function getPclZipInterface()
279
    {
280
        if ($this->type !== self::ZIP)
281
            throw new UnsupportedOperationException('Format '.$this->type.' does not support PclZip-interface');
282
283
        return new $this->archive->getPclZip();
0 ignored issues
show
Bug introduced by
The property getPclZip does not seem to exist on wapmorgan\UnifiedArchive\Formats\BasicFormat.
Loading history...
284
    }
285
286
    /**
287
     * Counts number of files
288
     *
289
     * @return int
290
     */
291 6
    public function countFiles()
292
    {
293 6
        return $this->filesQuantity;
294
    }
295
296
    /**
297
     * Counts size of all uncompressed data (bytes)
298
     *
299
     * @return int
300
     */
301 6
    public function countUncompressedFilesSize()
302
    {
303 6
        return $this->uncompressedFilesSize;
304
    }
305
306
    /**
307
     * Returns size of archive
308
     *
309
     * @return int
310
     */
311
    public function getArchiveSize()
312
    {
313
        return $this->archiveSize;
314
    }
315
316
    /**
317
     * Returns type of archive
318
     *
319
     * @return string
320
     */
321
    public function getArchiveType()
322
    {
323
        return $this->type;
324
    }
325
326
    /**
327
     * Counts size of all compressed data (in bytes)
328
     *
329
     * @return int
330
     */
331
    public function countCompressedFilesSize()
332
    {
333
        return $this->compressedFilesSize;
334
    }
335
336
    /**
337
     * Returns list of files
338
     *
339
     * @return array List of files
340
     */
341
    public function getFileNames()
342
    {
343
        return array_values($this->files);
344
    }
345
346
    /**
347
     * Checks that file exists in archive
348
     *
349
     * @param string $fileName
350
     * @return bool
351
     */
352 2
    public function isFileExists($fileName)
353
    {
354 2
        return in_array($fileName, $this->files, true);
355
    }
356
357
    /**
358
     * Returns file metadata
359
     *
360
     * @param string $fileName
361
     * @return ArchiveEntry|bool
362
     */
363 6
    public function getFileData($fileName)
364
    {
365 6
        if (!in_array($fileName, $this->files, true))
366
            return false;
367
368 6
        return $this->archive->getFileData($fileName);
369
    }
370
371
    /**
372
     * Returns file content
373
     *
374
     * @param string $fileName
375
     * @return bool|string
376
     * @throws \Exception
377
     */
378 8
    public function getFileContent($fileName)
379
    {
380 8
        if (!in_array($fileName, $this->files, true))
381
            return false;
382
383 8
        return $this->archive->getFileContent($fileName);
384
    }
385
386
    /**
387
     * Returns a resource for reading file from archive
388
     *
389
     * @param string $fileName
390
     * @return bool|resource
391
     */
392 6
    public function getFileResource($fileName)
393
    {
394 6
        if (!in_array($fileName, $this->files, true))
395
            return false;
396
397 6
        return $this->archive->getFileResource($fileName);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->archive->getFileResource($fileName) also could return the type string which is incompatible with the documented return type boolean|resource.
Loading history...
398
    }
399
400
    /**
401
     * Unpacks files to disk
402
     *
403
     * @param string $outputFolder Extraction output dir.
404
     * @param string|array|null $files One files or list of files or null to extract all content.
405
     * @param bool $expandFilesList Should be expanded paths like 'src/' to all files inside 'src/' dir or not.
406
     * @return false|int
407
     * @throws Exception If files can not be extracted
408
     */
409
    public function extractFiles($outputFolder, $files = null, $expandFilesList = false)
410
    {
411
        if ($files !== null) {
412
            if (is_string($files)) $files = [$files];
413
414
            if ($expandFilesList)
415
                $files = self::expandFileList($this->files, $files);
416
417
            return $this->archive->extractFiles($outputFolder, $files);
418
        } else {
419
            return $this->archive->extractArchive($outputFolder);
420
        }
421
    }
422
423
    /**
424
     * Updates existing archive by removing files from it
425
     *
426
     * Only 7zip and zip types support deletion.
427
     * @param string|string[] $fileOrFiles
428
     * @param bool $expandFilesList
429
     *
430
     * @return bool|int
431
     * @throws Exception
432
     */
433 2
    public function deleteFiles($fileOrFiles, $expandFilesList = false)
434
    {
435 2
        $fileOrFiles = is_string($fileOrFiles) ? [$fileOrFiles] : $fileOrFiles;
436
437 2
        if ($expandFilesList && $fileOrFiles !== null)
438
            $fileOrFiles = self::expandFileList($this->files, $fileOrFiles);
439
440 2
        $result = $this->archive->deleteFiles($fileOrFiles);
441 2
        $this->scanArchive();
442 2
        return $result;
443
    }
444
445
    /**
446
     * Updates existing archive by adding new files
447
     *
448
     * @param string[] $fileOrFiles See [[archiveFiles]] method for file list format.
449
     * @return int|bool False if failed, number of added files if success
450
     * @throws Exception
451
     */
452 2
    public function addFiles($fileOrFiles)
453
    {
454 2
        $files_list = self::createFilesList($fileOrFiles);
455 2
        if (empty($files_list))
456
            throw new InvalidArgumentException('Files list is empty!');
457 2
        $result = $this->archive->addFiles($files_list);
0 ignored issues
show
Bug introduced by
It seems like $files_list can also be of type boolean; however, parameter $files of wapmorgan\UnifiedArchive...BasicFormat::addFiles() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

457
        $result = $this->archive->addFiles(/** @scrutinizer ignore-type */ $files_list);
Loading history...
458 2
        $this->scanArchive();
459 2
        return $result;
460
    }
461
462
    /**
463
     * Adds file into archive
464
     *
465
     * @param string $file
466
     * @param string|null $inArchiveName If not passed, full path will be preserved.
467
     * @return bool
468
     * @throws Exception
469
     */
470 2
    public function addFile($file, $inArchiveName = null)
471
    {
472 2
        if (!is_file($file))
473
            throw new InvalidArgumentException($file.' is not a valid file to add in archive');
474
475 2
        return ($inArchiveName !== null
476 2
                ? $this->addFiles([$file => $inArchiveName])
477 2
                : $this->addFiles([$file])) === 1;
478
    }
479
480
    /**
481
     * Adds directory contents to archive
482
     *
483
     * @param string $directory
484
     * @param string|null $inArchivePath If not passed, full paths will be preserved.
485
     * @return bool
486
     * @throws Exception
487
     */
488
    public function addDirectory($directory, $inArchivePath = null)
489
    {
490
        if (!is_dir($directory) || !is_readable($directory))
491
            throw new InvalidArgumentException($directory.' is not a valid directory to add in archive');
492
493
        return ($inArchivePath !== null
494
                ? $this->addFiles([$directory => $inArchivePath])
495
                : $this->addFiles([$inArchivePath])) > 0;
496
    }
497
498
    /**
499
     * Creates an archive with passed files list
500
     *
501
     * @param string|string[]|array<string,string> $fileOrFiles List of files. Can be one of three formats:
502
     *                             1. A string containing path to file or directory.
503
     *                                  File will have it's basename.
504
     *                                  `UnifiedArchive::archiveFiles('/etc/php.ini', 'archive.zip)` will store
505
     * file with 'php.ini' name.
506
     *                                  Directory contents will be stored in archive root.
507
     *                                  `UnifiedArchive::archiveFiles('/var/log/', 'archive.zip')` will store all
508
     * directory contents in archive root.
509
     *                             2. An array with strings containing pats to files or directories.
510
     *                                  Files and directories will be stored with full paths.
511
     *                                  `UnifiedArchive::archiveFiles(['/etc/php.ini', '/var/log/'], 'archive.zip)`
512
     * will preserve full paths.
513
     *                             3. An array with strings where keys are strings.
514
     *                                  Files will have name from key.
515
     *                                  Directories contents will have prefix from key.
516
     *                                  `UnifiedArchive::archiveFiles(['doc.txt' => 'very_long_name_of_document.txt',
517
     *  'static' => '/var/www/html/static/'], 'archive.zip')`
518
     *
519
     * @param string $archiveName File name of archive. Type of archive will be determined via it's name.
520
     * @param bool $emulate If true, emulation mode is performed instead of real archiving.
521
     *
522
     * @return array|bool|int Count of stored files is returned.
523
     * @throws Exception
524
     */
525 2
    public static function archiveFiles($fileOrFiles, $archiveName, $emulate = false)
526
    {
527 2
        if (file_exists($archiveName))
528
            throw new Exception('Archive '.$archiveName.' already exists!');
529
530 2
        self::checkRequirements();
531
532 2
        $archiveType = self::detectArchiveType($archiveName, false);
533
        //        if (in_array($archiveType, [TarArchive::TAR, TarArchive::TAR_GZIP, TarArchive::TAR_BZIP, TarArchive::TAR_LZMA, TarArchive::TAR_LZW], true))
534
        //            return TarArchive::archiveFiles($fileOrFiles, $archiveName, $emulate);
535 2
        if ($archiveType === false)
536
            return false;
537
538 2
        $files_list = self::createFilesList($fileOrFiles);
539 2
        if (empty($files_list))
540
            throw new InvalidArgumentException('Files list is empty!');
541
542
        // fake creation: return archive data
543 2
        if ($emulate) {
544
            $totalSize = 0;
545
            foreach ($files_list as $fn) $totalSize += filesize($fn);
546
547
            return array(
548
                'totalSize' => $totalSize,
549
                'numberOfFiles' => count($files_list),
0 ignored issues
show
Bug introduced by
It seems like $files_list can also be of type boolean; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

549
                'numberOfFiles' => count(/** @scrutinizer ignore-type */ $files_list),
Loading history...
550
                'files' => $files_list,
551
                'type' => $archiveType,
552
            );
553
        }
554
555 2
        if (!isset(static::$formatHandlers[$archiveType]))
556
            throw new Exception('Unsupported archive type: '.$archiveType.' of archive '.$archiveName);
557
558 2
        $handler_class = static::$formatHandlers[$archiveType];
559
560 2
        return $handler_class::createArchive($files_list, $archiveName);
561
    }
562
563
    /**
564
     * Creates an archive with one file
565
     *
566
     * @param string $file
567
     * @param string $archiveName
568
     * @return bool
569
     * @throws \Exception
570
     */
571
    public static function archiveFile($file, $archiveName)
572
    {
573
        if (!is_file($file))
574
            throw new InvalidArgumentException($file.' is not a valid file to archive');
575
576
        return static::archiveFiles($file, $archiveName) === 1;
577
    }
578
579
    /**
580
     * Creates an archive with full directory contents
581
     *
582
     * @param string $directory
583
     * @param string $archiveName
584
     * @return bool
585
     * @throws Exception
586
     */
587
    public static function archiveDirectory($directory, $archiveName)
588
    {
589
        if (!is_dir($directory) || !is_readable($directory))
590
            throw new InvalidArgumentException($directory.' is not a valid directory to archive');
591
592
        return static::archiveFiles($directory, $archiveName) > 0;
593
    }
594
595
    /**
596
     * Tests system configuration
597
     */
598 22
    protected static function checkRequirements()
599
    {
600 22
        if (empty(self::$enabledTypes)) {
601 1
            self::$enabledTypes[self::ZIP] = extension_loaded('zip');
602 1
            self::$enabledTypes[self::SEVEN_ZIP] = class_exists('\Archive7z\Archive7z');
603 1
            self::$enabledTypes[self::RAR] = extension_loaded('rar');
604 1
            self::$enabledTypes[self::GZIP] = extension_loaded('zlib');
605 1
            self::$enabledTypes[self::BZIP] = extension_loaded('bz2');
606 1
            self::$enabledTypes[self::LZMA] = extension_loaded('xz');
607 1
            self::$enabledTypes[self::ISO] = class_exists('\CISOFile');
608 1
            self::$enabledTypes[self::CAB] = class_exists('\CabArchive');
609 1
            self::$enabledTypes[self::TAR] = class_exists('\Archive_Tar') || class_exists('\PharData');
610 1
            self::$enabledTypes[self::TAR_GZIP] = (class_exists('\Archive_Tar') || class_exists('\PharData')) && extension_loaded('zlib');
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: self::enabledTypes[self:...tension_loaded('zlib')), Probably Intended Meaning: self::enabledTypes[self:...tension_loaded('zlib'))
Loading history...
611 1
            self::$enabledTypes[self::TAR_BZIP] = (class_exists('\Archive_Tar') || class_exists('\PharData')) && extension_loaded('bz2');
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: self::enabledTypes[self:...xtension_loaded('bz2')), Probably Intended Meaning: self::enabledTypes[self:...xtension_loaded('bz2'))
Loading history...
612 1
            self::$enabledTypes[self::TAR_LZMA] = class_exists('\Archive_Tar') && extension_loaded('lzma2');
613 1
            self::$enabledTypes[self::TAR_LZW] = class_exists('\Archive_Tar') && LzwStreamWrapper::isBinaryAvailable();
614
        }
615 22
    }
616
617
    /**
618
     * Deprecated method for extracting files
619
     *
620
     * @param string $outputFolder
621
     * @param string|array|null $files
622
     * @deprecated 0.1.0
623
     * @see extractFiles()
624
     * @return bool|int
625
     * @throws Exception
626
     */
627
    public function extractNode($outputFolder, $files = null)
628
    {
629
        return $this->extractFiles($outputFolder, $files);
630
    }
631
632
    /**
633
     * Deprecated method for archiving files
634
     *
635
     * @param string|array $filesOrFiles
636
     * @param string $archiveName
637
     * @deprecated 0.1.0
638
     * @see archiveFiles()
639
     * @return mixed
640
     * @throws Exception
641
     */
642
    public static function archiveNodes($filesOrFiles, $archiveName)
643
    {
644
        return static::archiveFiles($filesOrFiles, $archiveName);
645
    }
646
647
    /**
648
     * Expands files list
649
     *
650
     * @param $archiveFiles
651
     * @param $files
652
     * @return array
653
     */
654
    protected static function expandFileList($archiveFiles, $files)
655
    {
656
        $newFiles = [];
657
        foreach ($files as $file) {
658
            foreach ($archiveFiles as $archiveFile) {
659
                if (fnmatch($file.'*', $archiveFile))
660
                    $newFiles[] = $archiveFile;
661
            }
662
        }
663
        return $newFiles;
664
    }
665
666
    /**
667
     * @param string|array $nodes
668
     * @return array|bool
669
     */
670 4
    protected static function createFilesList($nodes)
671
    {
672 4
        $files = [];
673
674
        // passed an extended list
675 4
        if (is_array($nodes)) {
676 2
            foreach ($nodes as $source => $destination) {
677 2
                if (is_numeric($source))
678
                    $source = $destination;
679
680 2
                $destination = rtrim($destination, '/\\*');
681
682
                // if is directory
683 2
                if (is_dir($source))
684
                    self::importFilesFromDir(rtrim($source, '/\\*').'/*',
685
                        !empty($destination) ? $destination.'/' : null, true, $files);
686 2
                else if (is_file($source))
687 2
                    $files[$destination] = $source;
688
            }
689
690 2
        } else if (is_string($nodes)) { // passed one file or directory
0 ignored issues
show
introduced by
The condition is_string($nodes) is always true.
Loading history...
691
            // if is directory
692 2
            if (is_dir($nodes))
693 2
                self::importFilesFromDir(rtrim($nodes, '/\\*').'/*', null, true,
694 2
                    $files);
695
            else if (is_file($nodes))
696
                $files[basename($nodes)] = $nodes;
697
        }
698
699 4
        return $files;
700
    }
701
702
    /**
703
     * @param string $source
704
     * @param string|null $destination
705
     * @param bool $recursive
706
     * @param array $map
707
     */
708 2
    protected static function importFilesFromDir($source, $destination, $recursive, &$map)
709
    {
710
        // $map[$destination] = rtrim($source, '/*');
711
        // do not map root archive folder
712
713 2
        if ($destination !== null)
714 2
            $map[$destination] = null;
715
716 2
        foreach (glob($source, GLOB_MARK) as $node) {
717 2
            if (in_array(substr($node, -1), ['/', '\\'], true) && $recursive) {
718 2
                self::importFilesFromDir(str_replace('\\', '/', $node).'*',
719 2
                    $destination.basename($node).'/', $recursive, $map);
720 2
            } elseif (is_file($node) && is_readable($node)) {
721 2
                $map[$destination.basename($node)] = $node;
722
            }
723
        }
724 2
    }
725
726
    /**
727
     * @return bool
728
     */
729 2
    public function canAddFiles()
730
    {
731 2
        return call_user_func([static::$formatHandlers[$this->type], 'canAddFiles']);
732
    }
733
734
    /**
735
     * @return bool
736
     */
737 2
    public function canDeleteFiles()
738
    {
739 2
        return call_user_func([static::$formatHandlers[$this->type], 'canDeleteFiles']);
740
    }
741
}
742