Passed
Push — master ( 96860e...392c1d )
by f
11:16
created

SevenZip::setComment()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 6
ccs 0
cts 0
cp 0
crap 6
rs 10
1
<?php
2
namespace wapmorgan\UnifiedArchive\Drivers;
3
4
use Exception;
5
use wapmorgan\UnifiedArchive\Archive7z;
6
use wapmorgan\UnifiedArchive\ArchiveEntry;
7
use wapmorgan\UnifiedArchive\ArchiveInformation;
8
use wapmorgan\UnifiedArchive\Drivers\BasicDriver;
9
use wapmorgan\UnifiedArchive\Exceptions\ArchiveCreationException;
10
use wapmorgan\UnifiedArchive\Exceptions\ArchiveExtractionException;
11
use wapmorgan\UnifiedArchive\Exceptions\ArchiveModificationException;
12
use wapmorgan\UnifiedArchive\Exceptions\NonExistentArchiveFileException;
13
use wapmorgan\UnifiedArchive\Exceptions\UnsupportedOperationException;
14
use wapmorgan\UnifiedArchive\Formats;
15
16
class SevenZip extends BasicDriver
17
{
18
    /** @var Archive7z */
19
    protected $sevenZip;
20
21
    /**
22
     * @var string
23
     */
24
    protected $format;
25
26
    protected const COMMENT_FILE = 'descript.ion';
27
28 1
    /**
29
     * @return array
30
     */
31 1
    public static function getSupportedFormats()
32
    {
33
        return [
34
            Formats::SEVEN_ZIP,
35
            Formats::ZIP,
36
//            Formats::RAR,
37
            Formats::TAR,
38
            // disabled
39
//            Formats::TAR_GZIP,
40
//            Formats::TAR_BZIP,
41
            Formats::CAB,
42
            Formats::ISO,
43
            Formats::ARJ,
44
            Formats::LZMA,
45
            Formats::UEFI,
46
            Formats::GPT,
47
            Formats::MBR,
48
            Formats::MSI,
49
            Formats::DMG,
50
            Formats::RPM,
51
            Formats::DEB,
52
            Formats::UDF,
53
        ];
54
    }
55
56
    /**
57
     * @param string $format
58 3
     * @return bool
59
     * @throws \Archive7z\Exception
60 3
     */
61 3
    public static function checkFormatSupport($format)
62
    {
63
        $available = class_exists('\Archive7z\Archive7z') && Archive7z::getBinaryVersion() !== false;
64
        if (!$available)
65 3
            return false;
66
67
        // in 4.0.0 version it was supporting only 7z
68
        if (!Archive7z::supportsAllFormats())
69 3
            return $format === Formats::SEVEN_ZIP;
70 3
71 2
        switch ($format) {
72 2
            case Formats::SEVEN_ZIP:
73
            case Formats::ZIP:
74
            case Formats::RAR:
75 1
            case Formats::TAR:
76 1
//            case Formats::TAR_GZIP:
77
//            case Formats::TAR_BZIP:
78
            case Formats::CAB:
79
            case Formats::ISO:
80
            case Formats::ARJ:
81
            case Formats::LZMA:
82
            case Formats::UEFI:
83
            case Formats::GPT:
84
            case Formats::MBR:
85
            case Formats::MSI:
86
            case Formats::DMG:
87 3
            case Formats::RPM:
88
            case Formats::DEB:
89
            case Formats::UDF:
90
                return $available;
91
        }
92
    }
93
94
    /**
95
     * @inheritDoc
96
     */
97
    public static function getDescription()
98
    {
99
        return 'php-library and console program'
100
            .(class_exists('\Archive7z\Archive7z') && ($version = Archive7z::getBinaryVersion()) !== false
101
                ? ' ('.$version.')'
102
                : null);
103
    }
104
105
    /**
106
     * @inheritDoc
107
     */
108
    public static function getInstallationInstruction()
109
    {
110
        if (!class_exists('\Archive7z\Archive7z'))
111
            return 'install library [gemorroj/archive7z]: `composer require gemorroj/archive7z`' . "\n"
112
                . ' and console program p7zip [7za]: `apt install p7zip-full` - depends on OS';
113
114
        if (Archive7z::getBinaryVersion() === false)
115
            return 'install console program p7zip [7za]: `apt install p7zip-full` - depends on OS';
116
117
        return null;
118
    }
119 6
120
    /**
121
     * @inheritDoc
122 6
     */
123 6
    public function __construct($archiveFileName, $format, $password = null)
124 6
    {
125 6
        try {
126
            $this->format = $format;
127
            $this->sevenZip = new Archive7z($archiveFileName, null, null);
128
            if ($password !== null)
129 6
                $this->sevenZip->setPassword($password);
130
        } catch (\Archive7z\Exception $e) {
0 ignored issues
show
Bug introduced by
The type Archive7z\Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
131
            throw new Exception('Could not open 7Zip archive: '.$e->getMessage(), $e->getCode(), $e);
132
        }
133
    }
134 6
135
    /**
136 6
     * @return ArchiveInformation
137
     */
138 6
    public function getArchiveInformation()
139 6
    {
140
        $information = new ArchiveInformation();
141
142
        foreach ($this->sevenZip->getEntries() as $entry) {
143 6
            if ($entry->isDirectory()) {
144 6
                continue;
145
            }
146 6
147 6
            if (!isset($can_get_unix_path))
148
                $can_get_unix_path = method_exists($entry, 'getUnixPath');
149 6
150 6
            $information->files[] = $can_get_unix_path
151
                ? $entry->getUnixPath()
152 6
                : str_replace('\\', '/', $entry->getPath());
153
            $information->compressedFilesSize += (int)$entry->getPackedSize();
154
            $information->uncompressedFilesSize += (int)$entry->getSize();
155
        }
156
        return $information;
157
    }
158
159
    /**
160
     * @return array
161
     */
162
    public function getFileNames()
163
    {
164
        $files = [];
165
        foreach ($this->sevenZip->getEntries() as $entry) {
166
            if ($entry->isDirectory())
167
                continue;
168
            $files[] = $entry->getPath();
169
        }
170
        return $files;
171
    }
172
173
    /**
174
     * @param string $fileName
175
     *
176
     * @return bool
177
     */
178
    public function isFileExists($fileName)
179
    {
180
        return $this->sevenZip->getEntry($fileName) !== null;
181
    }
182
183
    /**
184 1
     * @param string $fileName
185
     *
186 1
     * @return ArchiveEntry|false
187 1
     */
188 1
    public function getFileData($fileName)
189
    {
190
        $entry = $this->sevenZip->getEntry($fileName);
191
        return new ArchiveEntry(
192
            $fileName,
193
            $entry->getPackedSize(),
194
            $entry->getSize(),
195
            strtotime($entry->getModified()),
196 1
            $entry->getSize() !== $entry->getPackedSize(),
197
            $entry->getComment(),
198 1
            $entry->getCrc()
199 1
        );
200
    }
201
202
    /**
203
     * @param string $fileName
204
     *
205
     * @return string|false
206
     * @throws NonExistentArchiveFileException
207 1
     */
208
    public function getFileContent($fileName)
209 1
    {
210 1
        $entry = $this->sevenZip->getEntry($fileName);
211
        if ($entry === null) {
212
            throw new NonExistentArchiveFileException('File ' . $fileName . ' does not exist');
213
        }
214
        return $entry->getContent();
215
    }
216
217
    /**
218
     * @param string $fileName
219
     *
220
     * @return bool|resource|string
221
     */
222
    public function getFileStream($fileName)
223
    {
224
        $entry = $this->sevenZip->getEntry($fileName);
225
        return self::wrapStringInStream($entry->getContent());
226
    }
227
228
    /**
229
     * @param string $outputFolder
230
     * @param array $files
231
     * @return int
232
     * @throws ArchiveExtractionException
233
     */
234
    public function extractFiles($outputFolder, array $files)
235
    {
236
        $count = 0;
237
        try {
238
            $this->sevenZip->setOutputDirectory($outputFolder);
239
240
            foreach ($files as $file) {
241
                $this->sevenZip->extractEntry($file);
242
                $count++;
243
            }
244
            return $count;
245
        } catch (Exception $e) {
246
            throw new ArchiveExtractionException('Could not extract archive: '.$e->getMessage(), $e->getCode(), $e);
247
        }
248
    }
249
250
    /**
251
     * @param string $outputFolder
252
     *
253
     * @return bool
254
     * @throws ArchiveExtractionException
255
     */
256
    public function extractArchive($outputFolder)
257
    {
258
        try {
259
            $this->sevenZip->setOutputDirectory($outputFolder);
260
            $this->sevenZip->extract();
261
            return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the return type mandated by wapmorgan\UnifiedArchive...river::extractArchive() of integer.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
262
        } catch (Exception $e) {
263
            throw new ArchiveExtractionException('Could not extract archive: '.$e->getMessage(), $e->getCode(), $e);
264
        }
265
    }
266
267
    /**
268
     * @param array $files
269
     * @return int Number of deleted files
270
     * @throws ArchiveModificationException
271
     */
272
    public function deleteFiles(array $files)
273
    {
274
        $count = 0;
275
        try {
276
            foreach ($files as $file) {
277
                $this->sevenZip->delEntry($file);
278
                $count++;
279
            }
280
            return $count;
281
        } catch (Exception $e) {
282
            throw new ArchiveModificationException('Could not modify archive: '.$e->getMessage(), $e->getCode(), $e);
283
        }
284
    }
285
286
    /**
287
     * @param array $files
288
     *
289
     * @return int
290
     * @throws ArchiveModificationException
291
     */
292
    public function addFiles(array $files)
293
    {
294
        $added_files = 0;
295
        try {
296
            foreach ($files as $localName => $filename) {
297
                if (!is_null($filename)) {
298
                    $this->sevenZip->addEntry($filename);
299
                    $this->sevenZip->renameEntry($filename, $localName);
300
                    $added_files++;
301
                }
302
            }
303
            return $added_files;
304
        } catch (Exception $e) {
305
            throw new ArchiveModificationException('Could not modify archive: '.$e->getMessage(), $e->getCode(), $e);
306
        }
307
    }
308
309
    /**
310
     * @param string $inArchiveName
311
     * @param string $content
312
     * @return bool|void
313
     * @throws ArchiveModificationException
314
     * @throws \Archive7z\Exception
315
     */
316
    public function addFileFromString($inArchiveName, $content)
317
    {
318
        $tmp_file = tempnam(sys_get_temp_dir(), 'ua');
319
        if (!$tmp_file)
320
            throw new ArchiveModificationException('Could not create temporarily file');
321
322
        file_put_contents($tmp_file, $content);
323
        $this->sevenZip->addEntry($tmp_file, true);
324
        $this->sevenZip->renameEntry($tmp_file, $inArchiveName);
325
        unlink($tmp_file);
326
        return true;
327
    }
328
329
    /**
330
     * @param array $files
331
     * @param string $archiveFileName
332
     * @param int $archiveFormat
333
     * @param int $compressionLevel
334
     * @param null $password
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $password is correct as it would always require null to be passed?
Loading history...
335
     * @param $fileProgressCallable
336
     * @return int
337
     * @throws ArchiveCreationException
338
     * @throws UnsupportedOperationException
339
     */
340
    public static function createArchive(
341
        array $files,
342
        $archiveFileName,
343
        $archiveFormat,
344
        $compressionLevel = self::COMPRESSION_AVERAGE,
345
        $password = null,
346
        $fileProgressCallable = null
347
    )
348
    {
349
        static $compressionLevelMap = [
350
            self::COMPRESSION_NONE => 0,
351
            self::COMPRESSION_WEAK => 2,
352
            self::COMPRESSION_AVERAGE => 4,
353
            self::COMPRESSION_STRONG => 7,
354
            self::COMPRESSION_MAXIMUM => 9,
355
        ];
356
357
        if ($password !== null && !static::canEncrypt($archiveFormat)) {
0 ignored issues
show
introduced by
The condition $password !== null is always false.
Loading history...
358
            throw new UnsupportedOperationException('SevenZip could not encrypt an archive of '.$archiveFormat.' format');
359
        }
360
361
        if ($fileProgressCallable !== null && !is_callable($fileProgressCallable)) {
362
            throw new ArchiveCreationException('File progress callable is not callable');
363
        }
364
365
        try {
366
            $current_file = 0;
367
            $total_files = count($files);
368
369
            $seven_zip = new Archive7z($archiveFileName);
370
            if ($password !== null)
0 ignored issues
show
introduced by
The condition $password !== null is always false.
Loading history...
371
                $seven_zip->setPassword($password);
372
            $seven_zip->setCompressionLevel($compressionLevelMap[$compressionLevel]);
373
            foreach ($files as $localName => $filename) {
374
                if ($filename !== null) {
375
                    $seven_zip->addEntry($filename, true);
376
                    $seven_zip->renameEntry($filename, $localName);
377
                }
378
                if ($fileProgressCallable !== null) {
379
                    call_user_func_array($fileProgressCallable, [$current_file++, $total_files, $filename, $localName]);
380
                }
381
            }
382
            unset($seven_zip);
383
        } catch (Exception $e) {
384
            throw new ArchiveCreationException('Could not create archive: '.$e->getMessage(), $e->getCode(), $e);
385
        }
386
        return count($files);
387
    }
388
389
    /**
390
     * @param $format
391
     * @return bool
392
     * @throws \Archive7z\Exception
393
     */
394
    public static function canCreateArchive($format)
395
    {
396
        if (in_array($format, [
397
            Formats::SEVEN_ZIP,
398
            Formats::BZIP,
399
            Formats::GZIP,
400
            Formats::TAR,
401
            Formats::LZMA,
402
            Formats::ZIP]
403
        ))
404
            return self::canRenameFiles();
405
406
        return false;
407
    }
408
409
    /**
410
     * @param $format
411
     * @return bool
412
     * @throws \Archive7z\Exception
413
     */
414
    public static function canAddFiles($format)
415
    {
416
        return self::canCreateArchive($format);
417
    }
418
419
    /**
420
     * @return bool
421
     * @throws \Archive7z\Exception
422
     */
423
    protected static function canRenameFiles()
424
    {
425
        $version = Archive7z::getBinaryVersion();
426
        return $version !== false && version_compare('9.30', $version, '<=');
427
    }
428
429
    /**
430
     * @param $format
431
     * @return bool
432
     * @throws \Archive7z\Exception
433
     */
434
    public static function canDeleteFiles($format)
435
    {
436
        return self::canCreateArchive($format);
437
    }
438
439
    /**
440
     * @param $format
441
     * @return bool
442
     * @throws \Archive7z\Exception
443
     */
444
    public static function canEncrypt($format)
445
    {
446
        return in_array($format, [Formats::ZIP, Formats::SEVEN_ZIP]) && self::canRenameFiles();
447
    }
448
449
    /**
450
     * @return string|null
451
     */
452
    public function getComment()
453
    {
454
        if ($this->format !== Formats::SEVEN_ZIP) {
455
            return parent::getComment();
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::getComment() targeting wapmorgan\UnifiedArchive...sicDriver::getComment() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
456
        }
457
        try {
458
            return $this->getFileContent(static::COMMENT_FILE);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getFileCon...t(static::COMMENT_FILE) could also return false which is incompatible with the documented return type null|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...
459
        } catch (NonExistentArchiveFileException $e) {
460
            return null;
461
        }
462
    }
463
464
    /**
465
     * @param string|null $comment
466
     * @return null
467
     * @throws ArchiveModificationException
468
     * @throws \Archive7z\Exception
469
     */
470
    public function setComment($comment)
471
    {
472
        if ($this->format !== Formats::SEVEN_ZIP) {
473
            return null;
474
        }
475
        $this->addFileFromString(static::COMMENT_FILE, $comment);
476
    }
477
}