Passed
Push — 1.1.x ( dfe7d6...7e326b )
by f
02:43
created

SevenZip   F

Complexity

Total Complexity 69

Size/Duplication

Total Lines 394
Duplicated Lines 0 %

Test Coverage

Coverage 27.21%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 69
eloc 160
dl 0
loc 394
ccs 40
cts 147
cp 0.2721
rs 2.88
c 1
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getSupportedFormats() 0 22 1
A addFiles() 0 14 4
A deleteFiles() 0 11 3
A canDeleteFiles() 0 3 1
A canAddFiles() 0 3 1
D checkFormatSupport() 0 30 20
A extractArchive() 0 8 2
A getArchiveInformation() 0 19 5
A getDescription() 0 6 3
A getInstallationInstruction() 0 9 3
A getFileNames() 0 9 3
A __construct() 0 9 3
A addFileFromString() 0 12 2
A createArchive() 0 26 5
A extractFiles() 0 13 3
A isFileExists() 0 3 1
A canCreateArchive() 0 13 2
A canRenameFiles() 0 4 2
A getFileData() 0 5 1
A getFileContent() 0 4 1
A getFileStream() 0 4 1
A canEncrypt() 0 3 2

How to fix   Complexity   

Complex Class

Complex classes like SevenZip often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SevenZip, and based on these observations, apply Extract Interface, too.

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\Formats;
13
14
class SevenZip extends BasicDriver
15
{
16
    /** @var Archive7z */
17
    protected $sevenZip;
18
19
    /**
20
     * @var string
21
     */
22
    protected $format;
23
24
    /**
25
     * @return array
26
     */
27 1
    public static function getSupportedFormats()
28
    {
29
        return [
30 1
            Formats::SEVEN_ZIP,
31
            Formats::ZIP,
32
            Formats::RAR,
33
            Formats::TAR,
34
            // disabled
35
//            Formats::TAR_GZIP,
36
//            Formats::TAR_BZIP,
37
            Formats::CAB,
38
            Formats::ISO,
39
            Formats::ARJ,
40
            Formats::LZMA,
41
            Formats::UEFI,
42
            Formats::GPT,
43
            Formats::MBR,
44
            Formats::MSI,
45
            Formats::DMG,
46
            Formats::RPM,
47
            Formats::DEB,
48
            Formats::UDF,
49
        ];
50
    }
51
52
    /**
53
     * @param string $format
54
     * @return bool
55
     * @throws \Archive7z\Exception
56
     */
57 3
    public static function checkFormatSupport($format)
58
    {
59 3
        $available = class_exists('\Archive7z\Archive7z') && Archive7z::getBinaryVersion() !== false;
60 3
        if (!$available)
61
            return false;
62
63
        // in 4.0.0 version it was supporting only 7z
64 3
        if (!Archive7z::supportsAllFormats())
65
            return $format === Formats::SEVEN_ZIP;
66
67
        switch ($format) {
68 3
            case Formats::SEVEN_ZIP:
69 3
            case Formats::ZIP:
70 2
            case Formats::RAR:
71 2
            case Formats::TAR:
72
//            case Formats::TAR_GZIP:
73
//            case Formats::TAR_BZIP:
74 1
            case Formats::CAB:
75 1
            case Formats::ISO:
76
            case Formats::ARJ:
77
            case Formats::LZMA:
78
            case Formats::UEFI:
79
            case Formats::GPT:
80
            case Formats::MBR:
81
            case Formats::MSI:
82
            case Formats::DMG:
83
            case Formats::RPM:
84
            case Formats::DEB:
85
            case Formats::UDF:
86 3
                return $available;
87
        }
88
    }
89
90
    /**
91
     * @inheritDoc
92
     */
93
    public static function getDescription()
94
    {
95
        return 'php-library and console program'
96
            .(class_exists('\Archive7z\Archive7z') && ($version = Archive7z::getBinaryVersion()) !== false
97
                ? ' ('.$version.')'
98
                : null);
99
    }
100
101
    /**
102
     * @inheritDoc
103
     */
104
    public static function getInstallationInstruction()
105
    {
106
        if (!class_exists('\Archive7z\Archive7z'))
107
            return 'install library `gemorroj/archive7z` and console program p7zip (7za)';
108
109
        if (Archive7z::getBinaryVersion() === false)
110
            return 'install console program p7zip (7za)';
111
112
        return null;
113
    }
114
115
    /**
116
     * @inheritDoc
117
     */
118 3
    public function __construct($archiveFileName, $format, $password = null)
119
    {
120
        try {
121 3
            $this->format = $format;
122 3
            $this->sevenZip = new Archive7z($archiveFileName, null, null);
123 3
            if ($password !== null)
124 3
                $this->sevenZip->setPassword($password);
125
        } 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...
126
            throw new Exception('Could not open 7Zip archive: '.$e->getMessage(), $e->getCode(), $e);
127
        }
128 3
    }
129
130
    /**
131
     * @return ArchiveInformation
132
     */
133 3
    public function getArchiveInformation()
134
    {
135 3
        $information = new ArchiveInformation();
136
137 3
        foreach ($this->sevenZip->getEntries() as $entry) {
138 3
            if ($entry->isDirectory()) {
139
                continue;
140
            }
141
142 3
            if (!isset($can_get_unix_path))
143 3
                $can_get_unix_path = method_exists($entry, 'getUnixPath');
144
145 3
            $information->files[] = $can_get_unix_path
146 3
                ? $entry->getUnixPath()
147
                : str_replace('\\', '/', $entry->getPath());
148 3
            $information->compressedFilesSize += (int)$entry->getPackedSize();
149 3
            $information->uncompressedFilesSize += (int)$entry->getSize();
150
        }
151 3
        return $information;
152
    }
153
154
    /**
155
     * @return array
156
     */
157
    public function getFileNames()
158
    {
159
        $files = [];
160
        foreach ($this->sevenZip->getEntries() as $entry) {
161
            if ($entry->isDirectory())
162
                continue;
163
            $files[] = $entry->getPath();
164
        }
165
        return $files;
166
    }
167
168
    /**
169
     * @param string $fileName
170
     *
171
     * @return bool
172
     */
173
    public function isFileExists($fileName)
174
    {
175
        return $this->sevenZip->getEntry($fileName) !== null;
176
    }
177
178
    /**
179
     * @param string $fileName
180
     *
181
     * @return ArchiveEntry|false
182
     */
183 1
    public function getFileData($fileName)
184
    {
185 1
        $entry = $this->sevenZip->getEntry($fileName);
186 1
        return new ArchiveEntry($fileName, $entry->getPackedSize(), $entry->getSize(),
187 1
            strtotime($entry->getModified()));
188
    }
189
190
    /**
191
     * @param string $fileName
192
     *
193
     * @return string|false
194
     */
195 1
    public function getFileContent($fileName)
196
    {
197 1
        $entry = $this->sevenZip->getEntry($fileName);
198 1
        return $entry->getContent();
199
    }
200
201
    /**
202
     * @param string $fileName
203
     *
204
     * @return bool|resource|string
205
     */
206 1
    public function getFileStream($fileName)
207
    {
208 1
        $entry = $this->sevenZip->getEntry($fileName);
209 1
        return self::wrapStringInStream($entry->getContent());
210
    }
211
212
    /**
213
     * @param string $outputFolder
214
     * @param array $files
215
     * @return int
216
     * @throws ArchiveExtractionException
217
     */
218
    public function extractFiles($outputFolder, array $files)
219
    {
220
        $count = 0;
221
        try {
222
            $this->sevenZip->setOutputDirectory($outputFolder);
223
224
            foreach ($files as $file) {
225
                $this->sevenZip->extractEntry($file);
226
                $count++;
227
            }
228
            return $count;
229
        } catch (Exception $e) {
230
            throw new ArchiveExtractionException('Could not extract archive: '.$e->getMessage(), $e->getCode(), $e);
231
        }
232
    }
233
234
    /**
235
     * @param string $outputFolder
236
     *
237
     * @return bool
238
     * @throws ArchiveExtractionException
239
     */
240
    public function extractArchive($outputFolder)
241
    {
242
        try {
243
            $this->sevenZip->setOutputDirectory($outputFolder);
244
            $this->sevenZip->extract();
245
            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...
246
        } catch (Exception $e) {
247
            throw new ArchiveExtractionException('Could not extract archive: '.$e->getMessage(), $e->getCode(), $e);
248
        }
249
    }
250
251
    /**
252
     * @param array $files
253
     * @return int Number of deleted files
254
     * @throws ArchiveModificationException
255
     */
256
    public function deleteFiles(array $files)
257
    {
258
        $count = 0;
259
        try {
260
            foreach ($files as $file) {
261
                $this->sevenZip->delEntry($file);
262
                $count++;
263
            }
264
            return $count;
265
        } catch (Exception $e) {
266
            throw new ArchiveModificationException('Could not modify archive: '.$e->getMessage(), $e->getCode(), $e);
267
        }
268
    }
269
270
    /**
271
     * @param array $files
272
     *
273
     * @return int
274
     * @throws ArchiveModificationException
275
     */
276
    public function addFiles(array $files)
277
    {
278
        $added_files = 0;
279
        try {
280
            foreach ($files as $localName => $filename) {
281
                if (!is_null($filename)) {
282
                    $this->sevenZip->addEntry($filename);
283
                    $this->sevenZip->renameEntry($filename, $localName);
284
                    $added_files++;
285
                }
286
            }
287
            return $added_files;
288
        } catch (Exception $e) {
289
            throw new ArchiveModificationException('Could not modify archive: '.$e->getMessage(), $e->getCode(), $e);
290
        }
291
    }
292
293
    /**
294
     * @param string $inArchiveName
295
     * @param string $content
296
     * @return bool|void
297
     * @throws ArchiveModificationException
298
     * @throws \Archive7z\Exception
299
     */
300
    public function addFileFromString($inArchiveName, $content)
301
    {
302
        $tmp_file = tempnam(sys_get_temp_dir(), 'ua');
303
        if (!$tmp_file)
304
            throw new ArchiveModificationException('Could not create temporarily file');
305
306
        file_put_contents($tmp_file, $content);
307
        unset($content);
308
        $this->sevenZip->addEntry($tmp_file, true);
309
        $this->sevenZip->renameEntry($tmp_file, $inArchiveName);
310
        unlink($tmp_file);
311
        return true;
312
    }
313
314
    /**
315
     * @param array $files
316
     * @param string $archiveFileName
317
     * @param int $compressionLevel
318
     * @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...
319
     * @return int
320
     * @throws ArchiveCreationException
321
     */
322
    public static function createArchive(array $files, $archiveFileName, $compressionLevel = self::COMPRESSION_AVERAGE, $password = null)
323
    {
324
        static $compressionLevelMap = [
325
            self::COMPRESSION_NONE => 0,
326
            self::COMPRESSION_WEAK => 2,
327
            self::COMPRESSION_AVERAGE => 4,
328
            self::COMPRESSION_STRONG => 7,
329
            self::COMPRESSION_MAXIMUM => 9,
330
        ];
331
332
        try {
333
            $seven_zip = new Archive7z($archiveFileName);
334
            if ($password !== null)
0 ignored issues
show
introduced by
The condition $password !== null is always false.
Loading history...
335
                $seven_zip->setPassword($password);
336
            $seven_zip->setCompressionLevel($compressionLevelMap[$compressionLevel]);
337
            foreach ($files as $localName => $filename) {
338
                if ($filename !== null) {
339
                    $seven_zip->addEntry($filename, true);
340
                    $seven_zip->renameEntry($filename, $localName);
341
                }
342
            }
343
            unset($seven_zip);
344
        } catch (Exception $e) {
345
            throw new ArchiveCreationException('Could not create archive: '.$e->getMessage(), $e->getCode(), $e);
346
        }
347
        return count($files);
348
    }
349
350
    /**
351
     * @param $format
352
     * @return bool
353
     * @throws \Archive7z\Exception
354
     */
355
    public static function canCreateArchive($format)
356
    {
357
        if (in_array($format, [
358
            Formats::SEVEN_ZIP,
359
            Formats::BZIP,
360
            Formats::GZIP,
361
            Formats::TAR,
362
            Formats::LZMA,
363
            Formats::ZIP]
364
        ))
365
            return self::canRenameFiles();
366
367
        return false;
368
    }
369
370
    /**
371
     * @param $format
372
     * @return bool
373
     * @throws \Archive7z\Exception
374
     */
375
    public static function canAddFiles($format)
376
    {
377
        return self::canCreateArchive($format);
378
    }
379
380
    /**
381
     * @return bool
382
     * @throws \Archive7z\Exception
383
     */
384
    protected static function canRenameFiles()
385
    {
386
        $version = Archive7z::getBinaryVersion();
387
        return $version !== false && version_compare('9.30', $version, '<=');
388
    }
389
390
    /**
391
     * @param $format
392
     * @return bool
393
     * @throws \Archive7z\Exception
394
     */
395
    public static function canDeleteFiles($format)
396
    {
397
        return self::canCreateArchive($format);
398
    }
399
400
    /**
401
     * @param $format
402
     * @return bool
403
     * @throws \Archive7z\Exception
404
     */
405
    public static function canEncrypt($format)
406
    {
407
        return in_array($format, [Formats::ZIP, Formats::SEVEN_ZIP]) && self::canRenameFiles();
408
    }
409
}