Passed
Push — master ( 77631b...eb0eba )
by f
01:48
created

TarByPear   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 342
Duplicated Lines 0 %

Test Coverage

Coverage 36.24%

Importance

Changes 0
Metric Value
wmc 59
eloc 142
dl 0
loc 342
ccs 54
cts 149
cp 0.3624
rs 4.08
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getSupportedFormats() 0 8 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 5
A extractArchive() 0 8 2
A getArchiveInformation() 0 23 6
B checkFormatSupport() 0 19 7
A extractFiles() 0 8 2
C createArchive() 0 44 13
A addFileFromString() 0 3 1
A canCreateArchive() 0 3 1
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 canAddFiles() 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 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\NonExistentArchiveFileException;
12
use wapmorgan\UnifiedArchive\Exceptions\UnsupportedOperationException;
13
use wapmorgan\UnifiedArchive\Formats;
14
use wapmorgan\UnifiedArchive\LzwStreamWrapper;
15
16
class TarByPear extends BasicDriver
17
{
18
    /**
19
     * @var string Full path to archive
20
     */
21
    protected $archiveFileName;
22
23
    /**
24
     * @var Archive_Tar
25
     */
26
    protected $tar;
27
28
    /**
29
     * @var float Overall compression ratio of Tar archive when Archive_Tar is used
30
     */
31
    protected $pearCompressionRatio;
32
33
    /**
34
     * @var array<string, integer> List of files and their index in listContent() result
35
     */
36
    protected $pearFilesIndex;
37
38
    /**
39
     * @return array
40
     */
41 1
    public static function getSupportedFormats()
42
    {
43
        return [
44 1
            Formats::TAR,
45
            Formats::TAR_GZIP,
46
            Formats::TAR_BZIP,
47
            Formats::TAR_LZMA,
48
            Formats::TAR_LZW,
49
        ];
50
    }
51
52
    /**
53
     * @param $format
54
     * @return bool
55
     * @throws \Exception
56
     */
57 4
    public static function checkFormatSupport($format)
58
    {
59 4
        $availability = class_exists('\Archive_Tar');
60 4
        if (!$availability) return false;
61
        switch ($format) {
62 4
            case Formats::TAR:
63 1
                return true;
64
65 3
            case Formats::TAR_GZIP:
66 1
                return extension_loaded('zlib');
67
68 2
            case Formats::TAR_BZIP:
69 1
                return extension_loaded('bz2');
70
71 1
            case Formats::TAR_LZMA:
72 1
                return extension_loaded('xz');
73
74
            case Formats::TAR_LZW:
75
                return LzwStreamWrapper::isBinaryAvailable();
76
        }
77
    }
78
79
    /**
80
     * @inheritDoc
81
     */
82
    public static function getDescription()
83
    {
84
        return 'php-library for tar';
85
    }
86
87
    /**
88
     * @inheritDoc
89
     */
90
    public static function getInstallationInstruction()
91
    {
92
        return !class_exists('\Archive_Tar')
93
            ? 'install library `pear/archive_tar` and optionally php-extensions (zlib, bzip2)'
94
            : null;
95
    }
96
97
    /**
98
     * @param array $files
99
     * @param string $archiveFileName
100
     * @param int $archiveFormat
101
     * @param int $compressionLevel
102
     * @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...
103
     * @return int
104
     * @throws ArchiveCreationException
105
     * @throws UnsupportedOperationException
106
     */
107
    public static function createArchive(array $files, $archiveFileName, $archiveFormat, $compressionLevel = self::COMPRESSION_AVERAGE, $password = null)
108
    {
109
        if ($password !== null) {
0 ignored issues
show
introduced by
The condition $password !== null is always false.
Loading history...
110
            throw new UnsupportedOperationException('One-file format ('.__CLASS__.') could not encrypt an archive');
111
        }
112
113
        $compression = null;
114
        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

114
        switch (strtolower(/** @scrutinizer ignore-type */ pathinfo($archiveFileName, PATHINFO_EXTENSION))) {
Loading history...
115
            case 'gz':
116
            case 'tgz':
117
                $compression = 'gz';
118
                break;
119
            case 'bz2':
120
            case 'tbz2':
121
                $compression = 'bz2';
122
                break;
123
            case 'xz':
124
                $compression = 'lzma2';
125
                break;
126
            case 'z':
127
                $tar_aname = 'compress.lzw://' . $archiveFileName;
128
                break;
129
        }
130
131
        if (isset($tar_aname))
132
            $tar = new Archive_Tar($tar_aname, $compression);
133
        else
134
            $tar = new Archive_Tar($archiveFileName, $compression);
135
136
        foreach ($files  as $localName => $filename) {
137
            $remove_dir = dirname($filename);
138
            $add_dir = dirname($localName);
139
140
            if (is_null($filename)) {
141
                if ($tar->addString($localName, '') === false)
142
                    throw new ArchiveCreationException('Error when adding directory '.$localName.' to archive');
143
            } else {
144
                if ($tar->addModify($filename, $add_dir, $remove_dir) === false)
145
                    throw new ArchiveCreationException('Error when adding file '.$filename.' to archive');
146
            }
147
        }
148
        $tar = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $tar is dead and can be removed.
Loading history...
149
150
        return count($files);
151
    }
152
153
    /**
154
     * @inheritDoc
155
     */
156 3
    public function __construct($archiveFileName, $format, $password = null)
157
    {
158 3
        $this->archiveFileName = realpath($archiveFileName);
159 3
        $this->open($format);
160 3
    }
161
162 3
    protected function open($archiveType)
163
    {
164
        switch ($archiveType) {
165 3
            case Formats::TAR_GZIP:
166
                $this->tar = new Archive_Tar($this->archiveFileName, 'gz');
167
                break;
168
169 3
            case Formats::TAR_BZIP:
170
                $this->tar = new Archive_Tar($this->archiveFileName, 'bz2');
171
                break;
172
173 3
            case Formats::TAR_LZMA:
174 3
                $this->tar = new Archive_Tar($this->archiveFileName, 'lzma2');
175 3
                break;
176
177
            case Formats::TAR_LZW:
178
                LzwStreamWrapper::registerWrapper();
179
                $this->tar = new Archive_Tar('compress.lzw://' . $this->archiveFileName);
180
                break;
181
182
            default:
183
                $this->tar = new Archive_Tar($this->archiveFileName);
184
                break;
185
        }
186 3
    }
187
188
    /**
189
     * @inheritDoc
190
     */
191 3
    public function getArchiveInformation()
192
    {
193 3
        $information = new ArchiveInformation();
194 3
        $this->pearFilesIndex = [];
195
196 3
        foreach ($this->tar->listContent() as $i => $file) {
197
            // BUG workaround: http://pear.php.net/bugs/bug.php?id=20275
198 3
            if ($file['filename'] === 'pax_global_header') {
199
                continue;
200
            }
201
            // skip directories
202 3
            if ($file['typeflag'] == '5' || substr($file['filename'], -1) === '/')
203 3
                continue;
204 3
            $information->files[] = $file['filename'];
205 3
            $information->uncompressedFilesSize += $file['size'];
206 3
            $this->pearFilesIndex[$file['filename']] = $i;
207
        }
208
209 3
        $information->compressedFilesSize = filesize($this->archiveFileName);
210 3
        $this->pearCompressionRatio = $information->uncompressedFilesSize != 0
211 3
            ? ceil($information->compressedFilesSize / $information->uncompressedFilesSize)
212
            : 1;
213 3
        return $information;
214
    }
215
216
    /**
217
     * @inheritDoc
218
     */
219
    public function getFileNames()
220
    {
221
        $files = [];
222
223
        $Content = $this->tar->listContent();
224
        foreach ($Content as $i => $file) {
225
            // BUG workaround: http://pear.php.net/bugs/bug.php?id=20275
226
            if ($file['filename'] === 'pax_global_header') {
227
                continue;
228
            }
229
            $files[] = $file['filename'];
230
        }
231
232
        return $files;
233
    }
234
235
    /**
236
     * @inheritDoc
237
     */
238
    public function isFileExists($fileName)
239
    {
240
        return isset($this->pearFilesIndex[$fileName]);
241
    }
242
243
    /**
244
     * @inheritDoc
245
     */
246 1
    public function getFileData($fileName)
247
    {
248 1
        if (!isset($this->pearFilesIndex[$fileName]))
249
            throw new NonExistentArchiveFileException('File '.$fileName.' is not found in archive files list');
250
251 1
        $index = $this->pearFilesIndex[$fileName];
252
253 1
        $files_list = $this->tar->listContent();
254 1
        if (!isset($files_list[$index]))
255
            throw new NonExistentArchiveFileException('File '.$fileName.' is not found in Tar archive');
256
257 1
        $data = $files_list[$index];
258 1
        unset($files_list);
259
260 1
        return new ArchiveEntry($fileName, $data['size'] / $this->pearCompressionRatio,
261 1
            $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

261
            $data['size'], $data['mtime'], in_array(strtolower(/** @scrutinizer ignore-type */ pathinfo($this->archiveFileName,
Loading history...
262 1
                PATHINFO_EXTENSION)), ['gz', 'bz2', 'xz', 'z']));
263
    }
264
265
    /**
266
     * @inheritDoc
267
     */
268 1
    public function getFileContent($fileName)
269
    {
270 1
        if (!isset($this->pearFilesIndex[$fileName]))
271
            throw new NonExistentArchiveFileException('File '.$fileName.' is not found in archive files list');
272
273 1
        return $this->tar->extractInString($fileName);
274
    }
275
276
    /**
277
     * @inheritDoc
278
     */
279 1
    public function getFileStream($fileName)
280
    {
281 1
        if (!isset($this->pearFilesIndex[$fileName]))
282
            throw new NonExistentArchiveFileException('File '.$fileName.' is not found in archive files list');
283
284 1
        return self::wrapStringInStream($this->tar->extractInString($fileName));
285
    }
286
287
    /**
288
     * @inheritDoc
289
     */
290
    public function extractFiles($outputFolder, array $files)
291
    {
292
        $result = $this->tar->extractList($files, $outputFolder);
293
        if ($result === false) {
294
            throw new ArchiveExtractionException('Error when extracting from '.$this->archiveFileName);
295
        }
296
297
        return count($files);
298
    }
299
300
    /**
301
     * @inheritDoc
302
     */
303
    public function extractArchive($outputFolder)
304
    {
305
        $result = $this->tar->extract($outputFolder);
306
        if ($result === false) {
307
            throw new ArchiveExtractionException('Error when extracting from '.$this->archiveFileName);
308
        }
309
310
        return 1;
311
    }
312
313
    /**
314
     * @inheritDoc
315
     */
316
    public function addFiles(array $files)
317
    {
318
        $added = 0;
319
        foreach ($files as $localName => $filename) {
320
            $remove_dir = dirname($filename);
321
            $add_dir = dirname($localName);
322
            if (is_null($filename)) {
323
                if ($this->tar->addString($localName, "") === false) {
324
                    throw new ArchiveModificationException('Could not add directory "'.$filename.'": '.$this->tar->error_object->message, $this->tar->error_object->code);
325
                }
326
            } else {
327
                if ($this->tar->addModify($filename, $add_dir, $remove_dir) === false) {
328
                    throw new ArchiveModificationException('Could not add file "'.$filename.'": '.$this->tar->error_object->message, $this->tar->error_object->code);
329
                }
330
                $added++;
331
            }
332
        }
333
        return $added;
334
    }
335
336
    public static function canCreateArchive($format)
337
    {
338
        return true;
339
    }
340
341
    /**
342
     * @param $format
343
     * @return bool
344
     */
345
    public static function canAddFiles($format)
346
    {
347
        return true;
348
    }
349
350
    /**
351
     * @param string $inArchiveName
352
     * @param string $content
353
     * @return bool|true
354
     */
355
    public function addFileFromString($inArchiveName, $content)
356
    {
357
        return $this->tar->addString($inArchiveName, $content);
358
    }
359
}