Passed
Push — 1.1.x ( 3d3019...6d4a67 )
by f
02:55 queued 01:04
created

Zip   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 344
Duplicated Lines 0 %

Test Coverage

Coverage 64.29%

Importance

Changes 0
Metric Value
wmc 55
eloc 107
c 0
b 0
f 0
dl 0
loc 344
ccs 81
cts 126
cp 0.6429
rs 6

26 Methods

Rating   Name   Duplication   Size   Complexity  
A checkFormatSupport() 0 5 2
A canCreateArchive() 0 3 1
A __destruct() 0 3 1
A canAddFiles() 0 3 1
A __construct() 0 5 2
A getFileData() 0 5 1
A isFileExists() 0 3 1
A getFileStream() 0 3 1
A setComment() 0 3 1
A getFileNames() 0 11 3
A open() 0 6 2
A canEncrypt() 0 3 1
A addFiles() 0 20 5
A getSupportedFormats() 0 4 1
A getDescription() 0 3 3
A getFileContent() 0 6 2
A getInstallationInstruction() 0 5 2
A getComment() 0 3 1
A canDeleteFiles() 0 3 1
A addFileFromString() 0 3 1
B createArchive() 0 41 11
A extractArchive() 0 6 2
A deleteFiles() 0 15 3
A canStream() 0 3 1
A extractFiles() 0 6 2
A getArchiveInformation() 0 13 3

How to fix   Complexity   

Complex Class

Complex classes like Zip 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 Zip, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace wapmorgan\UnifiedArchive\Drivers;
3
4
use Exception;
5
use wapmorgan\UnifiedArchive\ArchiveEntry;
6
use wapmorgan\UnifiedArchive\ArchiveInformation;
7
use wapmorgan\UnifiedArchive\Drivers\BasicDriver;
8
use wapmorgan\UnifiedArchive\Exceptions\ArchiveCreationException;
9
use wapmorgan\UnifiedArchive\Exceptions\ArchiveExtractionException;
10
use wapmorgan\UnifiedArchive\Exceptions\ArchiveModificationException;
11
use wapmorgan\UnifiedArchive\Exceptions\UnsupportedOperationException;
12
use wapmorgan\UnifiedArchive\Formats;
13
use wapmorgan\UnifiedArchive\PclzipZipInterface;
14
use ZipArchive;
15
16
/**
17
 * Class Zip
18
 *
19
 * @package wapmorgan\UnifiedArchive\Formats
20
 * @requires ext-zip
21
 */
22
class Zip extends BasicDriver
23
{
24
    /** @var ZipArchive */
25
    protected $zip;
26
27
    /**
28
     * @return array
29
     */
30 1
    public static function getSupportedFormats()
31
    {
32
        return [
33 1
            Formats::ZIP
34
        ];
35
    }
36
37
    /**
38
     * @param $format
39
     * @return bool
40
     */
41 1
    public static function checkFormatSupport($format)
42
    {
43
        switch ($format) {
44 1
            case Formats::ZIP:
45 1
                return extension_loaded('zip');
46
        }
47
    }
48
49
    public static function getDescription()
50
    {
51
        return 'adapter for ext-zip'.(extension_loaded('zip') && defined('\ZipArchive::LIBZIP_VERSION') ? ' ('. ZipArchive::LIBZIP_VERSION.')' : null);
52
    }
53
54
    public static function getInstallationInstruction()
55
    {
56
        return !extension_loaded('zip')
57
            ? 'install `zip` extension'
58
            : null;
59
    }
60
61
    /**
62
     * @inheritDoc
63
     */
64 4
    public function __construct($archiveFileName, $format, $password = null)
65
    {
66 4
        $this->open($archiveFileName);
67 4
        if ($password !== null)
68
            $this->zip->setPassword($password);
69 4
    }
70
71
    /**
72
     * @param string $archiveFileName
73
     * @throws UnsupportedOperationException
74
     */
75 4
    protected function open($archiveFileName)
76
    {
77 4
        $this->zip = new ZipArchive();
78 4
        $open_result = $this->zip->open($archiveFileName);
79 4
        if ($open_result !== true) {
80
            throw new UnsupportedOperationException('Could not open Zip archive: '.$open_result);
81
        }
82 4
    }
83
84
    /**
85
     * Zip format destructor
86
     */
87 4
    public function __destruct()
88
    {
89 4
        unset($this->zip);
90 4
    }
91
92
    /**
93
     * @return ArchiveInformation
94
     */
95 4
    public function getArchiveInformation()
96
    {
97 4
        $information = new ArchiveInformation();
98 4
        for ($i = 0; $i < $this->zip->numFiles; $i++) {
99 4
            $file = $this->zip->statIndex($i);
100
            // skip directories
101 4
            if (in_array(substr($file['name'], -1), ['/', '\\'], true))
102 4
                continue;
103 4
            $information->files[$i] = $file['name'];
104 4
            $information->compressedFilesSize += $file['comp_size'];
105 4
            $information->uncompressedFilesSize += $file['size'];
106
        }
107 4
        return $information;
108
    }
109
110
    /**
111
     * @return false|string|null
112
     */
113
    public function getComment()
114
    {
115
        return $this->zip->getArchiveComment();
116
    }
117
118
    /**
119
     * @param string|null $comment
120
     * @return bool|null
121
     */
122
    public function setComment($comment)
123
    {
124
        return $this->zip->setArchiveComment($comment);
125
    }
126
127
    /**
128
     * @return array
129
     */
130
    public function getFileNames()
131
    {
132
        $files = [];
133
        for ($i = 0; $i < $this->zip->numFiles; $i++) {
134
            $file_name = $this->zip->getNameIndex($i);
135
            // skip directories
136
            if (in_array(substr($file_name, -1), ['/', '\\'], true))
137
                continue;
138
            $files[] = $file_name;
139
        }
140
        return $files;
141
    }
142
143
    /**
144
     * @param string $fileName
145
     *
146
     * @return bool
147
     */
148
    public function isFileExists($fileName)
149
    {
150
        return $this->zip->statName($fileName) !== false;
151
    }
152
153
    /**
154
     * @param string $fileName
155
     *
156
     * @return ArchiveEntry
157
     */
158 1
    public function getFileData($fileName)
159
    {
160 1
        $stat = $this->zip->statName($fileName);
161 1
        return new ArchiveEntry($fileName, $stat['comp_size'], $stat['size'], $stat['mtime'],
162 1
            $stat['comp_method'] != 0, $this->zip->getCommentName($fileName));
163
    }
164
165
    /**
166
     * @param string $fileName
167
     *
168
     * @return string|false
169
     * @throws \Exception
170
     */
171 2
    public function getFileContent($fileName)
172
    {
173 2
        $result = $this->zip->getFromName($fileName);
174 2
        if ($result === false)
175
            throw new Exception('Could not get file information: '.$result.'. May use password?');
176 2
        return $result;
177
    }
178
179
    /**
180
     * @param $fileName
181
     * @return false|resource
182
     */
183 1
    public function getFileStream($fileName)
184
    {
185 1
        return $this->zip->getStream($fileName);
186
    }
187
188
    /**
189
     * @param string $outputFolder
190
     * @param array $files
191
     * @return int Number of extracted files
192
     * @throws ArchiveExtractionException
193
     */
194
    public function extractFiles($outputFolder, array $files)
195
    {
196
        if ($this->zip->extractTo($outputFolder, $files) === false)
197
            throw new ArchiveExtractionException($this->zip->getStatusString(), $this->zip->status);
198
199
        return count($files);
200
    }
201
202
    /**
203
     * @param string $outputFolder
204
     * @return int Number of extracted files
205
     *@throws ArchiveExtractionException
206
     */
207
    public function extractArchive($outputFolder)
208
    {
209
        if ($this->zip->extractTo($outputFolder) === false)
210
            throw new ArchiveExtractionException($this->zip->getStatusString(), $this->zip->status);
211
212
        return $this->zip->numFiles;
213
    }
214
215
    /**
216
     * @param array $files
217
     * @return int
218
     * @throws ArchiveModificationException
219
     * @throws UnsupportedOperationException
220
     */
221 1
    public function deleteFiles(array $files)
222
    {
223 1
        $count = 0;
224 1
        foreach ($files as $file) {
225 1
            if ($this->zip->deleteName($file) === false)
226
                throw new ArchiveModificationException($this->zip->getStatusString(), $this->zip->status);
227 1
            $count++;
228
        }
229
230
        // reopen archive to save changes
231 1
        $archive_filename = $this->zip->filename;
232 1
        $this->zip->close();
233 1
        $this->open($archive_filename);
234
235 1
        return $count;
236
    }
237
238
    /**
239
     * @param array $files
240
     * @return int
241
     * @throws ArchiveModificationException
242
     * @throws UnsupportedOperationException
243
     */
244 1
    public function addFiles(array $files)
245
    {
246 1
        $added_files = 0;
247 1
        foreach ($files as $localName => $fileName) {
248 1
            if (is_null($fileName)) {
249
                if ($this->zip->addEmptyDir($localName) === false)
250
                    throw new ArchiveModificationException($this->zip->getStatusString(), $this->zip->status);
251
            } else {
252 1
                if ($this->zip->addFile($fileName, $localName) === false)
253
                    throw new ArchiveModificationException($this->zip->getStatusString(), $this->zip->status);
254 1
                $added_files++;
255
            }
256
        }
257
258
        // reopen archive to save changes
259 1
        $archive_filename = $this->zip->filename;
260 1
        $this->zip->close();
261 1
        $this->open($archive_filename);
262
263 1
        return $added_files;
264
    }
265
266
    /**
267
     * @param string $inArchiveName
268
     * @param string $content
269
     * @return bool
270
     */
271
    public function addFileFromString($inArchiveName, $content)
272
    {
273
        return $this->zip->addFromString($inArchiveName, $content);
274
    }
275
276
    /**
277
     * @param array $files
278
     * @param string $archiveFileName
279
     * @param int $compressionLevel
280
     * @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...
281
     * @return int
282
     * @throws ArchiveCreationException
283
     * @throws UnsupportedOperationException
284
     */
285 1
    public static function createArchive(array $files, $archiveFileName, $compressionLevel = self::COMPRESSION_AVERAGE, $password = null)
286
    {
287 1
        static $compressionLevelMap = [
288
            self::COMPRESSION_NONE => ZipArchive::CM_STORE,
289
            self::COMPRESSION_WEAK => ZipArchive::CM_SHRINK,
290
            self::COMPRESSION_AVERAGE => ZipArchive::CM_IMPLODE,
291
            self::COMPRESSION_STRONG => ZipArchive::CM_DEFLATE,
292
            self::COMPRESSION_MAXIMUM => ZipArchive::CM_DEFLATE64,
293
        ];
294
295 1
        $zip = new ZipArchive();
296 1
        $result = $zip->open($archiveFileName, ZipArchive::CREATE);
297
298 1
        if ($result !== true)
299
            throw new ArchiveCreationException('ZipArchive error: '.$result);
300
301 1
        $can_set_compression_level = method_exists($zip, 'setCompressionName');
302 1
        $can_encrypt = method_exists($zip, 'setEncryptionName');
303
304 1
        if ($password !== null && !$can_encrypt) {
0 ignored issues
show
introduced by
The condition $password !== null is always false.
Loading history...
305
            throw new ArchiveCreationException('Encryption is not supported on current platform');
306
        }
307
308 1
        foreach ($files as $localName => $fileName) {
309 1
            if ($fileName === null) {
310 1
                if ($zip->addEmptyDir($localName) === false)
311 1
                    throw new ArchiveCreationException('Could not archive directory "'.$localName.'": '.$zip->getStatusString(), $zip->status);
312
            } else {
313 1
                if ($zip->addFile($fileName, $localName) === false)
314
                    throw new ArchiveCreationException('Could not archive file "'.$fileName.'": '.$zip->getStatusString(), $zip->status);
315 1
                if ($can_set_compression_level) {
316 1
                    $zip->setCompressionName($localName, $compressionLevelMap[$compressionLevel]);
317
                }
318 1
                if ($password !== null && $can_encrypt) {
319 1
                    $zip->setEncryptionName($localName, ZipArchive::EM_AES_256, $password);
320
                }
321
            }
322
        }
323 1
        $zip->close();
324
325 1
        return count($files);
326
    }
327
328
    /**
329
     * @inheritDoc
330
     */
331 1
    public static function canCreateArchive($format)
332
    {
333 1
        return true;
334
    }
335
336
    /**
337
     * @inheritDoc
338
     */
339 1
    public static function canAddFiles($format)
340
    {
341 1
        return true;
342
    }
343
344
    /**
345
     * @inheritDoc
346
     */
347 1
    public static function canDeleteFiles($format)
348
    {
349 1
        return true;
350
    }
351
352
    /**
353
     * @inheritDoc
354
     */
355
    public static function canEncrypt($format)
356
    {
357
        return true;
358
    }
359
360
    /**
361
     * @inheritDoc
362
     */
363
    public static function canStream($format)
364
    {
365
        return true;
366
    }
367
}
368