TarByPear   F
last analyzed

Complexity

Total Complexity 63

Size/Duplication

Total Lines 373
Duplicated Lines 0 %

Test Coverage

Coverage 36.24%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 161
dl 0
loc 373
ccs 54
cts 149
cp 0.3624
rs 3.36
c 1
b 0
f 0
wmc 63

17 Methods

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

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

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

298
        return new ArchiveEntry($fileName, /** @scrutinizer ignore-type */ $data['size'] / $this->pearCompressionRatio,
Loading history...
299
            $data['size'], $data['mtime'], in_array(strtolower(pathinfo($this->fileName,
0 ignored issues
show
Bug introduced by
It seems like pathinfo($this->fileName...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

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