Passed
Push — master ( b1d4f6...7824cd )
by f
10:25
created

TarByPear   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 371
Duplicated Lines 0 %

Test Coverage

Coverage 36.24%

Importance

Changes 0
Metric Value
eloc 163
c 0
b 0
f 0
dl 0
loc 371
ccs 54
cts 149
cp 0.3624
rs 3.28
wmc 64

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getFileData() 0 17 3
A getFileStream() 0 6 2
A getFileContent() 0 6 2
A getFileNames() 0 14 3
A addFiles() 0 18 5
A extractArchive() 0 8 2
A getArchiveInformation() 0 25 6
B checkFormatSupport() 0 41 11
A extractFiles() 0 8 2
C createArchive() 0 60 16
A open() 0 23 5
A isFileExists() 0 3 1
A getDescription() 0 3 1
A __construct() 0 4 1
A getInstallationInstruction() 0 5 2
A getSupportedFormats() 0 8 1
A addFileFromString() 0 3 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
namespace wapmorgan\UnifiedArchive\Drivers;
3
4
use Archive_Tar;
0 ignored issues
show
Bug introduced by
The type Archive_Tar 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...
5
use Vtiful\Kernel\Format;
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
use wapmorgan\UnifiedArchive\LzwStreamWrapper;
16
17
class TarByPear extends BasicDriver
18
{
19
    /**
20
     * @var string Full path to archive
21
     */
22
    protected $archiveFileName;
23
24
    /**
25
     * @var Archive_Tar
26
     */
27
    protected $tar;
28
29
    /**
30
     * @var float Overall compression ratio of Tar archive when Archive_Tar is used
31
     */
32
    protected $pearCompressionRatio;
33
34
    /**
35
     * @var array<string, integer> List of files and their index in listContent() result
36
     */
37
    protected $pearFilesIndex;
38
39
    protected $pureFilesNumber;
40
41 1
    /**
42
     * @return array
43
     */
44 1
    public static function getSupportedFormats()
45
    {
46
        return [
47
            Formats::TAR,
48
            Formats::TAR_GZIP,
49
            Formats::TAR_BZIP,
50
            Formats::TAR_LZMA,
51
            Formats::TAR_LZW,
52
        ];
53
    }
54
55
    /**
56
     * @param $format
57 4
     * @return array
58
     * @throws \Exception
59 4
     */
60 4
    public static function checkFormatSupport($format)
61
    {
62 4
        $availability = class_exists('\Archive_Tar');
63 1
        if (!$availability) {
64
            return [];
65 3
        }
66 1
67
        $abilities = [
68 2
            BasicDriver::OPEN,
69 1
            BasicDriver::EXTRACT_CONTENT,
70
            BasicDriver::APPEND,
71 1
            BasicDriver::CREATE,
72 1
        ];
73
74
        switch ($format) {
75
            case Formats::TAR:
76
                return $abilities;
77
78
            case Formats::TAR_GZIP:
79
                if (!extension_loaded('zlib')) {
80
                    return [];
81
                }
82
                return $abilities;
83
84
            case Formats::TAR_BZIP:
85
                if (!extension_loaded('bz2')) {
86
                    return [];
87
                }
88
                return $abilities;
89
90
            case Formats::TAR_LZMA:
91
                if (!extension_loaded('xz')) {
92
                    return [];
93
                }
94
                return $abilities;
95
96
            case Formats::TAR_LZW:
97
                if (!LzwStreamWrapper::isBinaryAvailable()) {
98
                    return [];
99
                }
100
                return $abilities;
101
        }
102
    }
103
104
    /**
105
     * @inheritDoc
106
     */
107
    public static function getDescription()
108
    {
109
        return 'php-library for tar';
110
    }
111
112
    /**
113
     * @inheritDoc
114
     */
115
    public static function getInstallationInstruction()
116
    {
117
        return !class_exists('\Archive_Tar')
118
            ? 'install library [pear/archive_tar]: `composer require pear/archive_tar`' . "\n"  . ' and optionally php-extensions (zlib, bzip2)'
119
            : null;
120
    }
121
122
    /**
123
     * @param array $files
124
     * @param string $archiveFileName
125
     * @param int $archiveFormat
126
     * @param int $compressionLevel
127
     * @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...
128
     * @param $fileProgressCallable
129
     * @return int
130
     * @throws ArchiveCreationException
131
     * @throws UnsupportedOperationException
132
     */
133
    public static function createArchive(
134
        array $files,
135
        $archiveFileName,
136
        $archiveFormat,
137
        $compressionLevel = self::COMPRESSION_AVERAGE,
138
        $password = null,
139
        $fileProgressCallable = null
140
    )
141
    {
142
        if ($password !== null) {
0 ignored issues
show
introduced by
The condition $password !== null is always false.
Loading history...
143
            throw new UnsupportedOperationException('One-file format ('.__CLASS__.') could not encrypt an archive');
144
        }
145
146
        if ($fileProgressCallable !== null && !is_callable($fileProgressCallable)) {
147
            throw new ArchiveCreationException('File progress callable is not callable');
148
        }
149
150
        $compression = null;
151
        switch (strtolower(pathinfo($archiveFileName, PATHINFO_EXTENSION))) {
0 ignored issues
show
Bug introduced by
It seems like pathinfo($archiveFileNam...ers\PATHINFO_EXTENSION) can also be of type array; 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

151
        switch (strtolower(/** @scrutinizer ignore-type */ pathinfo($archiveFileName, PATHINFO_EXTENSION))) {
Loading history...
152
            case 'gz':
153
            case 'tgz':
154
                $compression = 'gz';
155
                break;
156 3
            case 'bz2':
157
            case 'tbz2':
158 3
                $compression = 'bz2';
159 3
                break;
160 3
            case 'xz':
161
                $compression = 'lzma2';
162 3
                break;
163
            case 'z':
164
                $tar_aname = 'compress.lzw://' . $archiveFileName;
165 3
                break;
166
        }
167
168
        if (isset($tar_aname))
169 3
            $tar = new Archive_Tar($tar_aname, $compression);
170
        else
171
            $tar = new Archive_Tar($archiveFileName, $compression);
172
173 3
        $current_file = 0;
174 3
        $total_files = count($files);
175 3
        foreach ($files as $localName => $filename) {
176
            $remove_dir = dirname($filename);
177
            $add_dir = dirname($localName);
178
179
            if (is_null($filename)) {
180
                if ($tar->addString($localName, '') === false)
181
                    throw new ArchiveCreationException('Error when adding directory '.$localName.' to archive');
182
            } else {
183
                if ($tar->addModify($filename, $add_dir, $remove_dir) === false)
184
                    throw new ArchiveCreationException('Error when adding file '.$filename.' to archive');
185
            }
186 3
            if ($fileProgressCallable !== null) {
187
                call_user_func_array($fileProgressCallable, [$current_file++, $total_files, $filename, $localName]);
188
            }
189
        }
190
        $tar = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $tar is dead and can be removed.
Loading history...
191 3
192
        return count($files);
193 3
    }
194 3
195
    /**
196 3
     * @inheritDoc
197
     */
198 3
    public function __construct($archiveFileName, $format, $password = null)
199
    {
200
        $this->archiveFileName = realpath($archiveFileName);
201
        $this->open($format);
202 3
    }
203 3
204 3
    protected function open($archiveType)
205 3
    {
206 3
        switch ($archiveType) {
207
            case Formats::TAR_GZIP:
208
                $this->tar = new Archive_Tar($this->archiveFileName, 'gz');
209 3
                break;
210 3
211 3
            case Formats::TAR_BZIP:
212
                $this->tar = new Archive_Tar($this->archiveFileName, 'bz2');
213 3
                break;
214
215
            case Formats::TAR_LZMA:
216
                $this->tar = new Archive_Tar($this->archiveFileName, 'lzma2');
217
                break;
218
219
            case Formats::TAR_LZW:
220
                LzwStreamWrapper::registerWrapper();
221
                $this->tar = new Archive_Tar('compress.lzw://' . $this->archiveFileName);
222
                break;
223
224
            default:
225
                $this->tar = new Archive_Tar($this->archiveFileName);
226
                break;
227
        }
228
    }
229
230
    /**
231
     * @inheritDoc
232
     */
233
    public function getArchiveInformation()
234
    {
235
        $information = new ArchiveInformation();
236
        $this->pearFilesIndex = [];
237
        $this->pureFilesNumber = 0;
238
239
        foreach ($this->tar->listContent() as $i => $file) {
240
            // BUG workaround: http://pear.php.net/bugs/bug.php?id=20275
241
            if ($file['filename'] === 'pax_global_header') {
242
                continue;
243
            }
244
            // skip directories
245
            if ($file['typeflag'] == '5' || substr($file['filename'], -1) === '/')
246 1
                continue;
247
            $information->files[] = $file['filename'];
248 1
            $information->uncompressedFilesSize += $file['size'];
249
            $this->pearFilesIndex[$file['filename']] = $i;
250
            $this->pureFilesNumber++;
251 1
        }
252
253 1
        $information->compressedFilesSize = filesize($this->archiveFileName);
254 1
        $this->pearCompressionRatio = $information->uncompressedFilesSize != 0
255
            ? ceil($information->compressedFilesSize / $information->uncompressedFilesSize)
256
            : 1;
257 1
        return $information;
258 1
    }
259
260 1
    /**
261 1
     * @inheritDoc
262 1
     */
263
    public function getFileNames()
264
    {
265
        $files = [];
266
267
        $Content = $this->tar->listContent();
268 1
        foreach ($Content as $i => $file) {
269
            // BUG workaround: http://pear.php.net/bugs/bug.php?id=20275
270 1
            if ($file['filename'] === 'pax_global_header') {
271
                continue;
272
            }
273 1
            $files[] = $file['filename'];
274
        }
275
276
        return $files;
277
    }
278
279 1
    /**
280
     * @inheritDoc
281 1
     */
282
    public function isFileExists($fileName)
283
    {
284 1
        return isset($this->pearFilesIndex[$fileName]);
285
    }
286
287
    /**
288
     * @inheritDoc
289
     */
290
    public function getFileData($fileName)
291
    {
292
        if (!isset($this->pearFilesIndex[$fileName]))
293
            throw new NonExistentArchiveFileException('File '.$fileName.' is not found in archive files list');
294
295
        $index = $this->pearFilesIndex[$fileName];
296
297
        $files_list = $this->tar->listContent();
298
        if (!isset($files_list[$index]))
299
            throw new NonExistentArchiveFileException('File '.$fileName.' is not found in Tar archive');
300
301
        $data = $files_list[$index];
302
        unset($files_list);
303
304
        return new ArchiveEntry($fileName, $data['size'] / $this->pearCompressionRatio,
0 ignored issues
show
Bug introduced by
$data['size'] / $this->pearCompressionRatio of type double is incompatible with the type integer expected by parameter $compressedSize of wapmorgan\UnifiedArchive...iveEntry::__construct(). ( Ignorable by Annotation )

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

304
        return new ArchiveEntry($fileName, /** @scrutinizer ignore-type */ $data['size'] / $this->pearCompressionRatio,
Loading history...
305
            $data['size'], $data['mtime'], in_array(strtolower(pathinfo($this->archiveFileName,
0 ignored issues
show
Bug introduced by
It seems like pathinfo($this->archiveF...ers\PATHINFO_EXTENSION) can also be of type array; 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

305
            $data['size'], $data['mtime'], in_array(strtolower(/** @scrutinizer ignore-type */ pathinfo($this->archiveFileName,
Loading history...
306
                PATHINFO_EXTENSION)), ['gz', 'bz2', 'xz', 'z']));
307
    }
308
309
    /**
310
     * @inheritDoc
311
     */
312
    public function getFileContent($fileName)
313
    {
314
        if (!isset($this->pearFilesIndex[$fileName]))
315
            throw new NonExistentArchiveFileException('File '.$fileName.' is not found in archive files list');
316
317
        return $this->tar->extractInString($fileName);
318
    }
319
320
    /**
321
     * @inheritDoc
322
     */
323
    public function getFileStream($fileName)
324
    {
325
        if (!isset($this->pearFilesIndex[$fileName]))
326
            throw new NonExistentArchiveFileException('File '.$fileName.' is not found in archive files list');
327
328
        return self::wrapStringInStream($this->tar->extractInString($fileName));
329
    }
330
331
    /**
332
     * @inheritDoc
333
     */
334
    public function extractFiles($outputFolder, array $files)
335
    {
336
        $result = $this->tar->extractList($files, $outputFolder);
337
        if ($result === false) {
338
            throw new ArchiveExtractionException('Error when extracting from '.$this->archiveFileName);
339
        }
340
341
        return count($files);
342
    }
343
344
    /**
345
     * @inheritDoc
346
     */
347
    public function extractArchive($outputFolder)
348
    {
349
        $result = $this->tar->extract($outputFolder);
350
        if ($result === false) {
351
            throw new ArchiveExtractionException('Error when extracting from '.$this->archiveFileName);
352
        }
353
354
        return $this->pureFilesNumber;
355
    }
356
357
    /**
358
     * @inheritDoc
359
     */
360
    public function addFiles(array $files)
361
    {
362
        $added = 0;
363
        foreach ($files as $localName => $filename) {
364
            $remove_dir = dirname($filename);
365
            $add_dir = dirname($localName);
366
            if (is_null($filename)) {
367
                if ($this->tar->addString($localName, "") === false) {
368
                    throw new ArchiveModificationException('Could not add directory "'.$filename.'": '.$this->tar->error_object->message, $this->tar->error_object->code);
369
                }
370
            } else {
371
                if ($this->tar->addModify($filename, $add_dir, $remove_dir) === false) {
372
                    throw new ArchiveModificationException('Could not add file "'.$filename.'": '.$this->tar->error_object->message, $this->tar->error_object->code);
373
                }
374
                $added++;
375
            }
376
        }
377
        return $added;
378
    }
379
380
    /**
381
     * @param string $inArchiveName
382
     * @param string $content
383
     * @return bool|true
384
     */
385
    public function addFileFromString($inArchiveName, $content)
386
    {
387
        return $this->tar->addString($inArchiveName, $content);
388
    }
389
}