Passed
Push — master ( 876ab1...afad4e )
by f
39:27 queued 24:31
created

UnifiedArchive::extract()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 9
c 0
b 0
f 0
nc 9
nop 3
dl 0
loc 15
ccs 0
cts 2
cp 0
crap 30
rs 9.6111
1
<?php
2
namespace wapmorgan\UnifiedArchive;
3
4
use ArrayAccess;
5
use Countable;
6
use InvalidArgumentException;
7
use Iterator;
8
use wapmorgan\UnifiedArchive\Drivers\BasicDriver;
9
use wapmorgan\UnifiedArchive\Exceptions\ArchiveExtractionException;
10
use wapmorgan\UnifiedArchive\Exceptions\ArchiveModificationException;
11
use wapmorgan\UnifiedArchive\Exceptions\EmptyFileListException;
12
use wapmorgan\UnifiedArchive\Exceptions\FileAlreadyExistsException;
13
use wapmorgan\UnifiedArchive\Exceptions\NonExistentArchiveFileException;
14
use wapmorgan\UnifiedArchive\Exceptions\UnsupportedArchiveException;
15
use wapmorgan\UnifiedArchive\Exceptions\UnsupportedOperationException;
16
17
/**
18
 * Class which represents archive in one of supported formats.
19
 */
20
class UnifiedArchive implements ArrayAccess, Iterator, Countable
21
{
22
    const VERSION = '1.1.7';
23
24
    /** @var string Type of current archive */
25
    protected $format;
26
27
    /** @var BasicDriver Adapter for current archive */
28
    protected $archive;
29
30
    /** @var array List of files in current archive */
31
    protected $files;
32
33
    /**
34
     * @var int
35
     */
36
    protected $filesIterator = 0;
37
38
    /** @var int Number of files in archive */
39
    protected $filesQuantity;
40
41
    /** @var int Cumulative size of uncompressed files */
42
    protected $uncompressedFilesSize;
43
44
    /** @var int Cumulative size of compressed files */
45
    protected $compressedFilesSize;
46
47
    /** @var int Total size of archive file */
48
    protected $archiveSize;
49
50
    /** @var BasicDriver */
51
    protected $driver;
52
53
    /** @var string|null */
54
    private $password;
55
56
    /**
57
     * Creates a UnifiedArchive instance for passed archive
58
     *
59
     * @param string $fileName Archive filename
60
     * @param array|null|string $abilities List of supported abilities by driver. If passed string, used as password.
61
     * @param string|null $password Password to open archive
62 23
     * @return UnifiedArchive|null Returns UnifiedArchive in case of successful reading of the file
63
     */
64 23
    public static function open($fileName, $abilities = [], $password = null)
65
    {
66
        if (!file_exists($fileName) || !is_readable($fileName)) {
67
            throw new InvalidArgumentException('Could not open file: ' . $fileName . ' is not readable');
68 23
        }
69 23
70
        $format = Formats::detectArchiveFormat($fileName);
71
        if ($format === false) {
72
            return null;
73 23
        }
74
75
        if (!empty($abilities) && is_string($abilities)) {
76
            $password = $abilities;
77
            $abilities = [];
78
        }
79
80
        if (empty($abilities)) {
81
            $abilities = [BasicDriver::OPEN];
82 21
            if (!empty($password)) {
83
                $abilities[] = [BasicDriver::OPEN_ENCRYPTED];
84 21
            }
85 21
        }
86
        $driver = Formats::getFormatDriver($format, $abilities);
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type true; however, parameter $format of wapmorgan\UnifiedArchive...mats::getFormatDriver() 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

86
        $driver = Formats::getFormatDriver(/** @scrutinizer ignore-type */ $format, $abilities);
Loading history...
Bug introduced by
It seems like $abilities can also be of type null; however, parameter $abilities of wapmorgan\UnifiedArchive...mats::getFormatDriver() 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

86
        $driver = Formats::getFormatDriver($format, /** @scrutinizer ignore-type */ $abilities);
Loading history...
87
        if ($driver === null) {
88
            return null;
89
        }
90
91
        return new static($fileName, $format, $driver, $password);
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type true; however, parameter $format 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

91
        return new static($fileName, /** @scrutinizer ignore-type */ $format, $driver, $password);
Loading history...
92
    }
93
94
    /**
95 23
     * Checks whether archive can be opened with current system configuration
96
     *
97 23
     * @param string $fileName Archive filename
98 23
     * @return bool
99
     */
100
    public static function canOpen($fileName)
101
    {
102 23
        $format = Formats::detectArchiveFormat($fileName);
103 23
        return $format !== false && Formats::canOpen($format);
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type true; however, parameter $format of wapmorgan\UnifiedArchive\Formats::canOpen() 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

103
        return $format !== false && Formats::canOpen(/** @scrutinizer ignore-type */ $format);
Loading history...
104 23
    }
105
106
    /**
107 23
     * Opens the file as one of supported formats
108 23
     *
109 23
     * @param string $fileName Archive filename
110
     * @param string $format Archive type
111
     * @param string|BasicDriver $driver
112
     * @param string|null $password
113
     */
114 23
    public function __construct($fileName, $format, $driver, $password = null)
115
    {
116 23
        $this->format = $format;
117 23
        $this->driver = $driver;
0 ignored issues
show
Documentation Bug introduced by
It seems like $driver can also be of type string. However, the property $driver is declared as type wapmorgan\UnifiedArchive\Drivers\BasicDriver. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
118 23
        $this->password = $password;
119 23
        $this->archiveSize = filesize($fileName);
120 23
121 23
        /** @var BasicDriver */
122
        $this->archive = new $driver($fileName, $format, $password);
123
        $this->scanArchive();
124
    }
125
126 23
    /**
127
     * Rescans array after modification
128 23
     */
129 23
    protected function scanArchive()
130
    {
131
        $information = $this->archive->getArchiveInformation();
132
        $this->files = $information->files;
133
        $this->compressedFilesSize = $information->compressedFilesSize;
134
        $this->uncompressedFilesSize = $information->uncompressedFilesSize;
135
        $this->filesQuantity = count($information->files);
136
    }
137
138
    /**
139
     * Closes archive
140
     */
141
    public function __destruct()
142
    {
143
        unset($this->archive);
144
    }
145 2
146
    /**
147 2
     * Returns an instance of class implementing PclZipOriginalInterface
148
     * interface.
149
     *
150
     * @return PclZipInterface Returns an instance of a class implementing PclZip-like interface
151
     */
152
    public function getPclZipInterface()
153
    {
154
        return new PclZipInterface($this);
155
    }
156
157
    /**
158
     * @return string
159
     */
160
    public function getDriverType()
161
    {
162
        return get_class($this->archive);
163
    }
164
165
    /**
166
     * @return BasicDriver
167
     */
168
    public function getDriver()
169
    {
170
        return $this->archive;
171
    }
172
173
    /**
174
     * Returns size of archive file in bytes
175
     *
176
     * @return int
177
     */
178
    public function getSize()
179
    {
180
        return $this->archiveSize;
181
    }
182
183
    /**
184
     * Returns type of archive
185
     *
186
     * @return string One of Format class constants
187
     */
188
    public function getFormat()
189
    {
190
        return $this->format;
191
    }
192
193
    /**
194
     * Returns mime type of archive
195
     *
196
     * @return string|false Mime Type
197
     */
198
    public function getMimeType()
199
    {
200
        return Formats::getFormatMimeType($this->format);
201
    }
202
203
    /**
204
     * @return string|null
205
     */
206
    public function getComment()
207
    {
208
        return $this->archive->getComment();
209
    }
210 7
211
    /**
212 7
     * @param string|null $comment
213
     * @return string|null
214
     */
215
    public function setComment($comment)
216
    {
217
        return $this->archive->setComment($comment);
218
    }
219 7
220
    /**
221 7
     * Counts number of files
222
     *
223
     * @return int
224
     */
225
    public function countFiles()
226
    {
227
        return $this->filesQuantity;
228
    }
229
230
    /**
231
     * * Counts cumulative size of all uncompressed data (bytes)
232
     * @return int
233
     */
234
    public function getOriginalSize()
235
    {
236
        return $this->uncompressedFilesSize;
237
    }
238
239 8
    /**
240
     * Counts cumulative size of all compressed data (in bytes)
241 8
     * @return int
242
     */
243
    public function getCompressedSize()
244
    {
245
        return $this->compressedFilesSize;
246
    }
247
248
    /**
249
     * Checks that file exists in archive
250
     *
251
     * @param string $fileName File name in archive
252 8
     * @return bool
253
     */
254 8
    public function hasFile($fileName)
255 8
    {
256
        return in_array($fileName, $this->files, true);
257
    }
258
259
    /**
260
     * Returns list of files, excluding folders.
261
     *
262
     * Paths is present in unix-style (with forward slash - /).
263
     *
264
     * @param string|null $filter
265
     * @return array List of files
266
     */
267
    public function getFiles($filter = null)
268
    {
269
        if ($filter === null)
270
            return $this->files;
271
272 6
        $result = [];
273
        foreach ($this->files as $file) {
274 6
            if (fnmatch($filter, $file))
275
                $result[] = $file;
276
        }
277
        return $result;
278 6
    }
279
280
    /**
281
     * Returns file metadata of file in archive
282
     *
283
     * @param string $fileName File name in archive
284
     * @return ArchiveEntry
285
     * @throws NonExistentArchiveFileException
286
     */
287
    public function getFileData($fileName)
288 8
    {
289
        if (!in_array($fileName, $this->files, true)) {
290 8
            throw new NonExistentArchiveFileException('File ' . $fileName . ' does not exist in archive');
291
        }
292
293
        return $this->archive->getFileData($fileName);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->archive->getFileData($fileName) could also return false which is incompatible with the documented return type wapmorgan\UnifiedArchive\ArchiveEntry. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
294 8
    }
295
296
    /**
297
     * Returns full file content as string
298
     *
299
     * @param string $fileName File name in archive
300
     * @return string
301
     * @throws NonExistentArchiveFileException
302
     */
303
    public function getFileContent($fileName)
304 6
    {
305
        if (!in_array($fileName, $this->files, true)) {
306 6
            throw new NonExistentArchiveFileException('File ' . $fileName . ' does not exist in archive');
307
        }
308
309
        return $this->archive->getFileContent($fileName);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->archive->getFileContent($fileName) could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
310 6
    }
311
312
    /**
313
     * Returns a resource for reading file from archive
314
     *
315
     * @param string $fileName File name in archive
316
     * @return resource
317
     * @throws NonExistentArchiveFileException
318
     */
319
    public function getFileStream($fileName)
320
    {
321
        if (!in_array($fileName, $this->files, true)) {
322
            throw new NonExistentArchiveFileException('File ' . $fileName . ' does not exist in archive');
323
        }
324
325
        return $this->archive->getFileStream($fileName);
326
    }
327
328
    /**
329
     * Unpacks files to disk
330
     *
331
     * @param string $outputFolder Extraction output dir
332
     * @param string|array|null $files One file or files list or null to extract all content.
333
     * @param bool $expandFilesList Whether paths like 'src/' should be expanded to all files inside 'src/' dir or not.
334
     * @return int Number of extracted files
335
     * @throws EmptyFileListException
336
     * @throws ArchiveExtractionException
337
     */
338
    public function extract($outputFolder, &$files = null, $expandFilesList = false)
339
    {
340
        if ($files !== null) {
341
            if (is_string($files)) {
342
                $files = [$files];
343
            }
344
            if ($expandFilesList) {
345
                $files = static::expandFileList($this->files, $files);
346
            }
347
            if (empty($files)) {
348
                throw new EmptyFileListException('Files list is empty!');
349
            }
350
            return $this->archive->extractFiles($outputFolder, $files);
351
        }
352
        return $this->archive->extractArchive($outputFolder);
353
    }
354
355
    /**
356 2
     * Updates existing archive by removing files from it
357
     *
358 2
     * Only 7zip and zip types support deletion.
359
     * @param string|string[] $fileOrFiles
360 2
     * @param bool $expandFilesList
361
     *
362
     * @return bool|int
363
     * @throws EmptyFileListException
364 2
     * @throws UnsupportedOperationException
365
     * @throws ArchiveModificationException
366
     */
367
    public function delete($fileOrFiles, $expandFilesList = false)
368 2
    {
369 2
        $fileOrFiles = is_string($fileOrFiles) ? [$fileOrFiles] : $fileOrFiles;
370
371 2
        if ($expandFilesList && $fileOrFiles !== null) {
372
            $fileOrFiles = static::expandFileList($this->files, $fileOrFiles);
373
        }
374
375
        if (empty($fileOrFiles)) {
376
            throw new EmptyFileListException('Files list is empty!');
377
        }
378
379
        $result = $this->archive->deleteFiles($fileOrFiles);
380
        $this->scanArchive();
381
382
        return $result;
383 2
    }
384
385 2
    /**
386
     * Updates existing archive by adding new files
387 2
     *
388
     * @param string[] $fileOrFiles See [[archiveFiles]] method for file list format.
389
     * @return int|bool Number of added files
390 2
     * @throws ArchiveModificationException
391 2
     * @throws EmptyFileListException
392 2
     * @throws UnsupportedOperationException
393
     */
394
    public function add($fileOrFiles)
395
    {
396
        $files_list = static::createFilesList($fileOrFiles);
397
398
        if (empty($files_list))
399
            throw new EmptyFileListException('Files list is empty!');
400
401
        $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...BasicDriver::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

401
        $result = $this->archive->addFiles(/** @scrutinizer ignore-type */ $files_list);
Loading history...
402
        $this->scanArchive();
403
        return $result;
404
    }
405 2
406
    /**
407 2
     * Adds file into archive
408
     *
409
     * @param string $file File name to be added
410 2
     * @param string|null $inArchiveName If not passed, full path will be preserved.
411 2
     * @return bool
412 2
     * @throws ArchiveModificationException
413
     * @throws EmptyFileListException
414
     * @throws UnsupportedOperationException
415
     */
416
    public function addFile($file, $inArchiveName = null)
417
    {
418
        if (!is_file($file))
419
            throw new InvalidArgumentException($file . ' is not a valid file to add in archive');
420
421
        return ($inArchiveName !== null
422
                ? $this->add([$inArchiveName => $file])
423
                : $this->add([$file])) === 1;
424
    }
425
426
    /**
427
     * @param string $inArchiveName
428
     * @param string $content
429
     * @return bool
430
     * @throws ArchiveModificationException
431
     * @throws UnsupportedOperationException
432
     */
433
    public function addFileFromString($inArchiveName, $content)
434
    {
435
        $result = $this->archive->addFileFromString($inArchiveName, $content);
436
        $this->scanArchive();
437
        return $result;
438
    }
439
440
    /**
441
     * Adds directory contents to archive
442
     *
443
     * @param string $directory
444
     * @param string|null $inArchivePath If not passed, full paths will be preserved.
445
     * @return bool
446
     * @throws ArchiveModificationException
447
     * @throws EmptyFileListException
448
     * @throws UnsupportedOperationException
449
     */
450
    public function addDirectory($directory, $inArchivePath = null)
451
    {
452
        if (!is_dir($directory) || !is_readable($directory))
453
            throw new InvalidArgumentException($directory . ' is not a valid directory to add in archive');
454
455
        return ($inArchivePath !== null
456
                ? $this->add([$inArchivePath => $directory])
457
                : $this->add([$inArchivePath])) > 0;
458
    }
459
460
    /**
461
     * @param string|null $filter
462 2
     * @return true|string[]
463
     * @throws NonExistentArchiveFileException
464 2
     */
465
    public function test($filter = null)
466 2
    {
467
        $hash_exists = function_exists('hash_update_stream') && in_array('crc32b', hash_algos(), true);
468
        $failed = [];
469 2
        foreach ($this->getFiles($filter) as $fileName) {
470
            if ($hash_exists) {
471 2
                $ctx = hash_init('crc32b');
472
                hash_update_stream($ctx, $this->getFileStream($fileName));
473
                $actual_hash = hash_final($ctx);
474 2
            } else {
475 2
                $actual_hash = dechex(crc32($this->getFileContent($fileName)));
476 2
            }
477
            $expected_hash = strtolower($this->getFileData($fileName)->crc32);
0 ignored issues
show
Bug introduced by
It seems like $this->getFileData($fileName)->crc32 can also be of type null; however, parameter $string of strtolower() 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

477
            $expected_hash = strtolower(/** @scrutinizer ignore-type */ $this->getFileData($fileName)->crc32);
Loading history...
478
            if ($expected_hash !== $actual_hash) {
479
                $failed[] = $fileName;
480 2
            }
481 2
        }
482 2
        return !empty($failed) ? $failed : true;
483 2
    }
484
485
    /**
486
     * Prepare files list for archiving
487
     *
488
     * @param string|array $fileOrFiles File of list of files. See [[archiveFiles]] for details.
489
     * @param string $archiveName File name of archive. See [[archiveFiles]] for details.
490
     * @return array An array containing entries:
491
     * - totalSize (int) - size in bytes for all files
492
     * - numberOfFiles (int) - quantity of files
493
     * - files (array) - list of files prepared for archiving
494
     * - type (string) - prepared format for archive. One of class constants
495
     * @throws EmptyFileListException
496
     * @throws UnsupportedArchiveException
497
     */
498
    public static function prepareForArchiving($fileOrFiles, $archiveName)
499
    {
500
        $archiveType = Formats::detectArchiveFormat($archiveName, false);
501
502
        if ($archiveType === false)
503
            throw new UnsupportedArchiveException('Could not detect archive type for name "' . $archiveName . '"');
504
505
        $files_list = static::createFilesList($fileOrFiles);
506
507
        if (empty($files_list))
508
            throw new EmptyFileListException('Files list is empty!');
509
510
        $totalSize = 0;
511
        foreach ($files_list as $fn) {
512
            if ($fn !== null) {
513
                $totalSize += filesize($fn);
514
            }
515 2
        }
516
517 2
        return [
518
            'totalSize' => $totalSize,
519
            'numberOfFiles' => count($files_list),
0 ignored issues
show
Bug introduced by
It seems like $files_list can also be of type boolean; however, parameter $value 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

519
            'numberOfFiles' => count(/** @scrutinizer ignore-type */ $files_list),
Loading history...
520 2
            'files' => $files_list,
521
            'type' => $archiveType,
522 2
        ];
523
    }
524
525 2
    /**
526
     * Creates an archive with passed files list
527
     *
528
     * @param string|string[]|array<string,string> $fileOrFiles List of files. Can be one of three formats:
529 2
     *                             1. A string containing path to file or directory.
530
     *                                  File will have it's basename.
531 2
     *                                  `UnifiedArchive::archiveFiles('/etc/php.ini', 'archive.zip)` will store
532
     * file with 'php.ini' name.
533
     *                                  Directory contents will be stored in archive root.
534
     *                                  `UnifiedArchive::archiveFiles('/var/log/', 'archive.zip')` will store all
535
     * directory contents in archive root.
536
     *                             2. An array with strings containing pats to files or directories.
537
     *                                  Files and directories will be stored with full paths.
538
     *                                  `UnifiedArchive::archiveFiles(['/etc/php.ini', '/var/log/'], 'archive.zip)`
539
     * will preserve full paths.
540
     *                             3. An array with strings where keys are strings.
541
     *                                  Files will have name from key.
542
     *                                  Directories contents will have prefix from key.
543
     *                                  `UnifiedArchive::archiveFiles(['doc.txt' => 'very_long_name_of_document.txt',
544
     *  'static' => '/var/www/html/static/'], 'archive.zip')`
545
     *
546
     * @param string $archiveName File name of archive. Type of archive will be determined by it's name.
547
     * @param int $compressionLevel Level of compression
548
     * @param string|null $password
549
     * @param callable|null $fileProgressCallable
550
     * @return int Count of stored files is returned.
551
     * @throws FileAlreadyExistsException
552
     * @throws UnsupportedOperationException
553
     */
554
    public static function archive(
555
        $fileOrFiles,
556
        $archiveName,
557
        $compressionLevel = BasicDriver::COMPRESSION_AVERAGE,
558
        $password = null,
559
        $fileProgressCallable = null
560
    )
561
    {
562
        if (file_exists($archiveName))
563
            throw new FileAlreadyExistsException('Archive ' . $archiveName . ' already exists!');
564
565
        $info = static::prepareForArchiving($fileOrFiles, $archiveName);
566
567
        $abilities = [BasicDriver::CREATE];
568
569
        if (!Formats::canCreate($info['type']))
570
            throw new UnsupportedArchiveException('Unsupported archive type: ' . $info['type'] . ' of archive ' . $archiveName);
571
572
        if ($password !== null && !Formats::canEncrypt($info['type']))
573
            throw new UnsupportedOperationException('Archive type ' . $info['type'] . ' can not be encrypted');
574
        if ($password !== null) {
575
            $abilities[] = BasicDriver::CREATE_ENCRYPTED;
576
        }
577
578
        /** @var BasicDriver $driver */
579
        $driver = Formats::getFormatDriver($info['type'], $abilities);
580
        if ($driver === null) {
581
            throw new UnsupportedArchiveException('Unsupported archive type: ' . $info['type'] . ' of archive ');
582
        }
583
584
        return $driver::createArchive(
585
            $info['files'],
0 ignored issues
show
Bug introduced by
It seems like $info['files'] can also be of type boolean; however, parameter $files of wapmorgan\UnifiedArchive...Driver::createArchive() 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

585
            /** @scrutinizer ignore-type */ $info['files'],
Loading history...
586
            $archiveName,
587
            $compressionLevel,
588
            $compressionLevel,
589
            $password,
0 ignored issues
show
Bug introduced by
It seems like $password can also be of type string; however, parameter $password of wapmorgan\UnifiedArchive...Driver::createArchive() does only seem to accept null, 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

589
            /** @scrutinizer ignore-type */ $password,
Loading history...
590
            $fileProgressCallable
591
        );
592
    }
593
594
    /**
595
     * Creates an archive with one file
596 4
     *
597
     * @param string $file
598 4
     * @param string $archiveName
599
     * @param int $compressionLevel Level of compression
600
     * @param string|null $password
601 4
     * @return bool
602 2
     * @throws FileAlreadyExistsException
603
     * @throws UnsupportedOperationException
604 2
     */
605
    public static function archiveFile($file, $archiveName, $compressionLevel = BasicDriver::COMPRESSION_AVERAGE, $password = null)
606
    {
607
        if (!is_file($file)) {
608 2
            throw new InvalidArgumentException($file . ' is not a valid file to archive');
609
        }
610
611
        return static::archive($file, $archiveName, $compressionLevel, $password) === 1;
612
    }
613 2
614
    /**
615
     * Creates an archive with full directory contents
616 2
     *
617
     * @param string $directory
618
     * @param string $archiveName
619 2
     * @param int $compressionLevel Level of compression
620 2
     * @param string|null $password
621
     * @return bool
622
     * @throws FileAlreadyExistsException
623 2
     * @throws UnsupportedOperationException
624
     */
625 2
    public static function archiveDirectory($directory, $archiveName, $compressionLevel = BasicDriver::COMPRESSION_AVERAGE, $password = null)
626 2
    {
627 2
        if (!is_dir($directory) || !is_readable($directory))
628
            throw new InvalidArgumentException($directory . ' is not a valid directory to archive');
629
630
        return static::archive($directory, $archiveName, $compressionLevel, $password) > 0;
631
    }
632 4
633
    /**
634
     * Expands files list
635
     * @param $archiveFiles
636
     * @param $files
637
     * @return array
638
     */
639
    protected static function expandFileList($archiveFiles, $files)
640
    {
641 2
        $newFiles = [];
642
        foreach ($files as $file) {
643
            foreach ($archiveFiles as $archiveFile) {
644
                if (fnmatch($file . '*', $archiveFile)) {
645
                    $newFiles[] = $archiveFile;
646 2
                }
647 2
            }
648
        }
649 2
        return $newFiles;
650 2
    }
651 2
652 2
    /**
653 2
     * @param string|array $nodes
654 2
     * @return array|bool
655
     */
656
    protected static function createFilesList($nodes)
657 2
    {
658
        $files = [];
659
660
        // passed an extended list
661
        if (is_array($nodes)) {
662
            foreach ($nodes as $destination => $source) {
663
                // new format
664
                if (is_numeric($destination))
665
                    $destination = $source;
666
                else {
667
                    // old format
668
                    if (is_string($source) && !file_exists($source)) {
669
                        list($destination, $source) = [$source, $destination];
670
                    }
671
                }
672
673
                $destination = rtrim($destination, '/\\*');
674
675
                // few sources for directories
676
                if (is_array($source)) {
677
                    foreach ($source as $sourceItem) {
678
                        static::importFilesFromDir(
679
                            rtrim($sourceItem, '/\\*') . '/*',
680
                            !empty($destination) ? $destination . '/' : null,
681
                            true,
682
                            $files
683
                        );
684
                    }
685
                } else if (is_dir($source)) {
686
                    // one source for directories
687
                    static::importFilesFromDir(
688
                        rtrim($source, '/\\*') . '/*',
689
                        !empty($destination) ? $destination . '/' : null,
690
                        true,
691
                        $files
692
                    );
693
                } else if (is_file($source)) {
694
                    $files[$destination] = $source;
695
                }
696
            }
697
698
        } 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...
699
            // if is directory
700
            if (is_dir($nodes))
701
                static::importFilesFromDir(rtrim($nodes, '/\\*') . '/*', null, true,
702
                    $files);
703
            else if (is_file($nodes))
704
                $files[basename($nodes)] = $nodes;
705
        }
706
707
        return $files;
708
    }
709
710
    /**
711
     * @param string $source
712
     * @param string|null $destination
713
     * @param bool $recursive
714
     * @param array $map
715
     */
716
    protected static function importFilesFromDir($source, $destination, $recursive, &$map)
717
    {
718
        // $map[$destination] = rtrim($source, '/*');
719
        // do not map root archive folder
720
721
        if ($destination !== null)
722
            $map[$destination] = null;
723
724
        foreach (glob($source, GLOB_MARK) as $node) {
725
            if (in_array(substr($node, -1), ['/', '\\'], true) && $recursive) {
726
                static::importFilesFromDir(str_replace('\\', '/', $node) . '*',
727
                    $destination . basename($node) . '/', $recursive, $map);
728
            } elseif (is_file($node) && is_readable($node)) {
729
                $map[$destination . basename($node)] = $node;
730
            }
731
        }
732
    }
733
734
    /**
735
     * @param mixed $offset
736
     * @return bool
737
     */
738
    #[\ReturnTypeWillChange]
739
    public function offsetExists($offset)
740
    {
741
        return $this->hasFile($offset);
742
    }
743
744
    /**
745
     * @param mixed $offset
746
     * @return mixed|string
747
     * @throws NonExistentArchiveFileException
748
     */
749
    #[\ReturnTypeWillChange]
750
    public function offsetGet($offset)
751
    {
752
        return $this->getFileData($offset);
753
    }
754
755
    /**
756
     * @param mixed $offset
757
     * @param mixed $value
758
     * @return bool|void
759
     * @throws ArchiveModificationException
760
     * @throws UnsupportedOperationException
761
     */
762
    #[\ReturnTypeWillChange]
763
    public function offsetSet($offset, $value)
764
    {
765
        return $this->addFileFromString($offset, $value);
766
    }
767
768
    /**
769
     * @param mixed $offset
770
     * @return bool|int|void
771
     * @throws ArchiveModificationException
772
     * @throws UnsupportedOperationException
773
     */
774
    #[\ReturnTypeWillChange]
775
    public function offsetUnset($offset)
776
    {
777
        return $this->delete($offset);
778
    }
779
780
    #[\ReturnTypeWillChange]
781
    public function key()
782
    {
783
        return $this->files[$this->filesIterator];
784
    }
785
786
    /**
787
     * @throws NonExistentArchiveFileException
788
     */
789
    #[\ReturnTypeWillChange]
790
    public function current()
791
    {
792
        return $this->getFileData($this->files[$this->filesIterator]);
793
    }
794
795
    #[\ReturnTypeWillChange]
796
    public function next()
797
    {
798
        $this->filesIterator++;
799
    }
800
801
    #[\ReturnTypeWillChange]
802
    public function valid()
803
    {
804
        return $this->filesIterator < $this->filesQuantity;
805
    }
806
807
    #[\ReturnTypeWillChange]
808
    public function rewind()
809
    {
810
        $this->filesIterator = 0;
811
    }
812
813
    #[\ReturnTypeWillChange]
814
    public function count()
815
    {
816
        return $this->filesQuantity;
817
    }
818
819
    // deprecated methods
820
821
    /**
822
     * Checks whether archive can be opened with current system configuration
823
     *
824
     * @param string $fileName Archive filename
825
     * @return bool
826
     * @deprecated See {UnifiedArchive::canOpen()}
827
     */
828
    public static function canOpenArchive($fileName)
829
    {
830
        return static::canOpen($fileName);
831
    }
832
833
    /**
834
     * Checks whether specific archive type can be opened with current system configuration
835
     *
836
     * @param string $type One of predefined archive types (class constants)
837
     * @return bool
838
     * @deprecated See {{Formats::canOpen()}}
839
     */
840
    public static function canOpenType($type)
841
    {
842
        return Formats::canOpen($type);
843
    }
844
845
    /**
846
     * Checks whether specified archive can be created
847
     *
848
     * @param string $type One of predefined archive types (class constants)
849
     * @return bool
850
     * @deprecated See {{Formats::canCreate()}}
851
     */
852
    public static function canCreateType($type)
853
    {
854
        return Formats::canCreate($type);
855
    }
856
857
    /**
858
     * Returns type of archive
859
     *
860
     * @return string One of class constants
861
     * @deprecated See {{UnifiedArchive::getArchiveFormat()}}
862
     */
863
    public function getArchiveType()
864
    {
865
        return $this->getFormat();
866
    }
867
868
    /**
869
     * Detect archive type by its filename or content
870
     *
871
     * @param string $fileName Archive filename
872
     * @param bool $contentCheck Whether archive type can be detected by content
873
     * @return string|bool One of UnifiedArchive type constants OR false if type is not detected
874
     * @deprecated See {{Formats::detectArchiveFormat()}}
875
     */
876
    public static function detectArchiveType($fileName, $contentCheck = true)
877
    {
878
        return Formats::detectArchiveFormat($fileName, $contentCheck);
879
    }
880
881
    /**
882
     * Returns a resource for reading file from archive
883
     *
884
     * @param string $fileName File name in archive
885
     * @return resource
886
     * @throws NonExistentArchiveFileException
887
     * @deprecated See {{UnifiedArchive::getFileStream}}
888
     */
889
    public function getFileResource($fileName)
890
    {
891
        return $this->getFileStream($fileName);
892
    }
893
894
    /**
895
     * Returns type of archive
896
     *
897
     * @return string One of class constants
898
     * @deprecated See {{UnifiedArchive::getFormat}}
899
     */
900
    public function getArchiveFormat()
901
    {
902
        return $this->getFormat();
903
    }
904
905
    /**
906
     * Checks that file exists in archive
907
     *
908
     * @param string $fileName File name in archive
909
     * @return bool
910
     * @deprecated See {{UnifiedArchive::hasFile}}
911
     */
912
    public function isFileExists($fileName)
913
    {
914
        return $this->hasFile($fileName);
915
    }
916
917
    /**
918
     * Returns size of archive file in bytes
919
     *
920
     * @return int
921
     * @deprecated See {{UnifiedArchive::getSize}}
922
     */
923
    public function getArchiveSize()
924
    {
925
        return $this->getSize();
926
    }
927
928
    /**
929
     * Counts cumulative size of all compressed data (in bytes)
930
     *
931
     * @return int
932
     * @deprecated See {{UnifiedArchive::getCompressedSize}}
933
     */
934
    public function countCompressedFilesSize()
935
    {
936
        return $this->getCompressedSize();
937
    }
938
939
    /**
940
     * Counts cumulative size of all uncompressed data (bytes)
941
     *
942
     * @return int
943
     * @deprecated See {{UnifiedArchive::getOriginalSize}}
944
     */
945
    public function countUncompressedFilesSize()
946
    {
947
        return $this->getOriginalSize();
948
    }
949
950
    /**
951
     * Returns list of files, excluding folders.
952
     *
953
     * Paths is present in unix-style (with forward slash - /).
954
     *
955
     * @param string|null $filter
956
     * @return array List of files
957
     * @deprecated See {{UnifiedArchive::getFiles}}
958
     */
959
    public function getFileNames($filter = null)
960
    {
961
        return $this->getFiles($filter);
962
    }
963
964
    /**
965
     * Unpacks files to disk
966
     *
967
     * @param string $outputFolder Extraction output dir
968
     * @param string|array|null $files One file or files list or null to extract all content.
969
     * @param bool $expandFilesList Whether paths like 'src/' should be expanded to all files inside 'src/' dir or not.
970
     * @return int Number of extracted files
971
     * @throws EmptyFileListException
972
     * @throws ArchiveExtractionException
973
     * @deprecated See {{UnifiedArchive::extract}}
974
     */
975
    public function extractFiles($outputFolder, &$files = null, $expandFilesList = false)
976
    {
977
        return $this->extract($outputFolder, $files, $expandFilesList);
978
    }
979
980
    /**
981
     * Updates existing archive by removing files from it
982
     *
983
     * Only 7zip and zip types support deletion.
984
     * @param string|string[] $fileOrFiles
985
     * @param bool $expandFilesList
986
     *
987
     * @return bool|int
988
     * @throws EmptyFileListException
989
     * @throws UnsupportedOperationException
990
     * @throws ArchiveModificationException
991
     * @deprecated See {{UnifiedArchive::delete}}
992
     */
993
    public function deleteFiles($fileOrFiles, $expandFilesList = false)
994
    {
995
        return $this->delete($fileOrFiles, $expandFilesList);
996
    }
997
998
    /**
999
     * Updates existing archive by adding new files
1000
     *
1001
     * @param string[] $fileOrFiles See [[archiveFiles]] method for file list format.
1002
     * @return int|bool Number of added files
1003
     * @throws ArchiveModificationException
1004
     * @throws EmptyFileListException
1005
     * @throws UnsupportedOperationException
1006
     * @deprecated See {{UnifiedArchive::add}}
1007
     */
1008
    public function addFiles($fileOrFiles)
1009
    {
1010
        return $this->add($fileOrFiles);
1011
    }
1012
1013
    /**
1014
     * Creates an archive with passed files list
1015
     *
1016
     * @param string|string[]|array<string,string> $fileOrFiles List of files. Can be one of three formats:
1017
     *                             1. A string containing path to file or directory.
1018
     *                                  File will have it's basename.
1019
     *                                  `UnifiedArchive::archiveFiles('/etc/php.ini', 'archive.zip)` will store
1020
     * file with 'php.ini' name.
1021
     *                                  Directory contents will be stored in archive root.
1022
     *                                  `UnifiedArchive::archiveFiles('/var/log/', 'archive.zip')` will store all
1023
     * directory contents in archive root.
1024
     *                             2. An array with strings containing pats to files or directories.
1025
     *                                  Files and directories will be stored with full paths.
1026
     *                                  `UnifiedArchive::archiveFiles(['/etc/php.ini', '/var/log/'], 'archive.zip)`
1027
     * will preserve full paths.
1028
     *                             3. An array with strings where keys are strings.
1029
     *                                  Files will have name from key.
1030
     *                                  Directories contents will have prefix from key.
1031
     *                                  `UnifiedArchive::archiveFiles(['doc.txt' => 'very_long_name_of_document.txt',
1032
     *  'static' => '/var/www/html/static/'], 'archive.zip')`
1033
     *
1034
     * @param string $archiveName File name of archive. Type of archive will be determined by it's name.
1035
     * @param int $compressionLevel Level of compression
1036
     * @param string|null $password
1037
     * @param callable|null $fileProgressCallable
1038
     * @return int Count of stored files is returned.
1039
     * @throws FileAlreadyExistsException
1040
     * @throws UnsupportedOperationException
1041
     * @deprecated See {{UnifiedArchive::archive}}
1042
     */
1043
    public static function archiveFiles(
1044
        $fileOrFiles,
1045
        $archiveName,
1046
        $compressionLevel = BasicDriver::COMPRESSION_AVERAGE,
1047
        $password = null,
1048
        $fileProgressCallable = null
1049
    )
1050
    {
1051
        return static::archive($fileOrFiles, $archiveName, $compressionLevel, $password, $fileProgressCallable);
1052
    }
1053
}
1054