Passed
Push — master ( 2af7c1...77631b )
by f
03:12
created

TarByPhar::checkFormatSupport()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 11
ccs 8
cts 8
cp 1
rs 9.2222
cc 6
nc 6
nop 1
crap 6
1
<?php
2
namespace wapmorgan\UnifiedArchive\Drivers;
3
4
use Exception;
5
use FilesystemIterator;
6
use Phar;
7
use PharData;
8
use PharFileInfo;
9
use RecursiveIteratorIterator;
10
use wapmorgan\UnifiedArchive\ArchiveEntry;
11
use wapmorgan\UnifiedArchive\ArchiveInformation;
12
use wapmorgan\UnifiedArchive\Drivers\BasicDriver;
13
use wapmorgan\UnifiedArchive\Exceptions\ArchiveCreationException;
14
use wapmorgan\UnifiedArchive\Exceptions\ArchiveExtractionException;
15
use wapmorgan\UnifiedArchive\Exceptions\ArchiveModificationException;
16
use wapmorgan\UnifiedArchive\Exceptions\UnsupportedOperationException;
17
use wapmorgan\UnifiedArchive\Formats;
18
19
class TarByPhar extends BasicDriver
20
{
21
    /**
22
     * @var false|string
23
     */
24
    protected $archiveFileName;
25
26
    /**
27
     * @var PharData
28
     */
29
    protected $tar;
30
31
    /**
32
     * @var int Flags for iterator
33
     */
34
    const PHAR_FLAGS = FilesystemIterator::UNIX_PATHS;
35
36
    /**
37
     * @return array
38
     */
39 1
    public static function getSupportedFormats()
40
    {
41
        return [
42 1
            Formats::TAR,
43
            Formats::TAR_GZIP,
44
            Formats::TAR_BZIP,
45
//            Formats::ZIP,
46
        ];
47
    }
48
49
    /**
50
     * @param $format
51
     * @return bool
52
     */
53 3
    public static function checkFormatSupport($format)
54
    {
55 3
        $availability = class_exists('\PharData');
56
        switch ($format) {
57 3
            case Formats::TAR:
58
//            case Formats::ZIP:
59 1
                return $availability;
60 2
            case Formats::TAR_GZIP:
61 1
                return $availability && extension_loaded('zlib');
62 1
            case Formats::TAR_BZIP:
63 1
                return $availability && extension_loaded('bz2');
64
        }
65
    }
66
67
    /**
68
     * @inheritDoc
69
     */
70
    public static function getDescription()
71
    {
72
        return 'adapter for ext-phar';
73
    }
74
75
    /**
76
     * @inheritDoc
77
     */
78
    public static function getInstallationInstruction()
79
    {
80
        return !extension_loaded('phar')
81
            ? 'install `phar` extension and optionally php-extensions (zlib, bzip2)'
82
            : null;
83
    }
84
85
    /**
86
     * @inheritDoc
87
     */
88 10
    public function __construct($archiveFileName, $format, $password = null)
89
    {
90 10
        $this->archiveFileName = realpath($archiveFileName);
91 10
        $this->open();
92 10
    }
93
94
    /**
95
     *
96
     */
97 10
    protected function open()
98
    {
99 10
        $this->tar = new PharData($this->archiveFileName, self::PHAR_FLAGS);
100 10
    }
101
102
    /**
103
     * @inheritDoc
104
     */
105 10
    public function getArchiveInformation()
106
    {
107 10
        $information = new ArchiveInformation();
108 10
        $stream_path_length = strlen('phar://'.$this->archiveFileName.'/');
109
        /**
110
         * @var string $i
111
         * @var PharFileInfo $file
112
         */
113 10
        foreach (new RecursiveIteratorIterator($this->tar) as $i => $file) {
114 10
            $information->files[] = substr($file->getPathname(), $stream_path_length);
115 10
            $information->compressedFilesSize += $file->getCompressedSize();
116 10
            $information->uncompressedFilesSize += filesize($file->getPathname());
117
        }
118 10
        return $information;
119
    }
120
121
    /**
122
     * @inheritDoc
123
     */
124
    public function getFileNames()
125
    {
126
        $files = [];
127
128
        $stream_path_length = strlen('phar://'.$this->archiveFileName.'/');
129
        foreach (new RecursiveIteratorIterator($this->tar) as $i => $file) {
130
            $files[] = substr($file->getPathname(), $stream_path_length);
131
        }
132
133
        return $files;
134
    }
135
136
    /**
137
     * @inheritDoc
138
     */
139
    public function isFileExists($fileName)
140
    {
141
        try {
142
            $this->tar->offsetGet($fileName);
143
            return true;
144
        } catch (Exception $e) {
145
            return false;
146
        }
147
    }
148
149
    /**
150
     * @inheritDoc
151
     */
152 3
    public function getFileData($fileName)
153
    {
154
        /** @var \PharFileInfo $entry_info */
155 3
        $entry_info = $this->tar->offsetGet($fileName);
156 3
        return new ArchiveEntry($fileName, $entry_info->getSize(), filesize($entry_info->getPathname()),
157 3
            0, $entry_info->isCompressed());
158
    }
159
160
    /**
161
     * @inheritDoc
162
     */
163 4
    public function getFileContent($fileName)
164
    {
165 4
        return $this->tar->offsetGet($fileName)->getContent();
166
    }
167
168
    /**
169
     * @inheritDoc
170
     */
171 3
    public function getFileStream($fileName)
172
    {
173 3
        return self::wrapStringInStream($this->tar->offsetGet($fileName)->getContent());
174
    }
175
176
    /**
177
     * @inheritDoc
178
     */
179
    public function extractFiles($outputFolder, array $files)
180
    {
181
        $result = $this->tar->extractTo($outputFolder, $files, true);
182
        if ($result === false) {
183
            throw new ArchiveExtractionException('Error when extracting from '.$this->archiveFileName);
184
        }
185
        return count($files);
186
    }
187
188
    /**
189
     * @inheritDoc
190
     */
191
    public function extractArchive($outputFolder)
192
    {
193
        $result = $this->tar->extractTo($outputFolder, null, true);
194
        if ($result === false) {
195
            throw new ArchiveExtractionException('Error when extracting from '.$this->archiveFileName);
196
        }
197
198
        return 1;
199
    }
200
201
    /**
202
     * @inheritDoc
203
     */
204 1
    public function deleteFiles(array $files)
205
    {
206 1
        $deleted = 0;
207
208 1
        foreach ($files as $i => $file) {
209 1
            if ($this->tar->delete($file))
210 1
                $deleted++;
211
        }
212
213 1
        $this->tar = null;
214 1
        $this->open();
215
216 1
        return $deleted;
217
    }
218
219
    /**
220
     * @inheritDoc
221
     */
222 1
    public function addFiles(array $files)
223
    {
224 1
        $added = 0;
225
        try {
226 1
            foreach ($files as $localName => $filename) {
227 1
                if (is_null($filename)) {
228
                    $this->tar->addEmptyDir($localName);
229
                } else {
230 1
                    $this->tar->addFile($filename, $localName);
231 1
                    $added++;
232
                }
233
            }
234
        } catch (Exception $e) {
235
            throw new ArchiveModificationException('Could not add file "'.$filename.'": '.$e->getMessage(), $e->getCode());
236
        }
237 1
        $this->tar = null;
238
        // reopen to refresh files list properly
239 1
        $this->open();
240 1
        return $added;
241
    }
242
243
    /**
244
     * @param $format
245
     * @return bool
246
     */
247 1
    public static function canCreateArchive($format)
248
    {
249 1
        return true;
250
    }
251
252
    /**
253
     * @param $format
254
     * @return bool
255
     */
256 1
    public static function canAddFiles($format)
257
    {
258 1
        return true;
259
    }
260
261
    /**
262
     * @param $format
263
     * @return bool
264
     */
265 1
    public static function canDeleteFiles($format)
266
    {
267 1
        return true;
268
    }
269
270
    /**
271
     * @param array $files
272
     * @param string $archiveFileName
273
     * @param int $compressionLevel
274
     * @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...
275
     * @return int
276
     * @throws ArchiveCreationException
277
     * @throws UnsupportedOperationException
278
     */
279 1
    public static function createArchive(array $files, $archiveFileName, $compressionLevel = self::COMPRESSION_AVERAGE, $password = null)
280
    {
281 1
        if ($password !== null) {
0 ignored issues
show
introduced by
The condition $password !== null is always false.
Loading history...
282
            throw new UnsupportedOperationException('One-file format ('.__CLASS__.') could not encrypt an archive');
283
        }
284
285 1
        if (preg_match('~^(.+)\.(tar\.(gz|bz2))$~i', $archiveFileName, $match)) {
286
            $ext = $match[2];
287
            $basename = $match[1];
288
        } else {
289 1
            $ext = pathinfo($archiveFileName, PATHINFO_EXTENSION);
290 1
            $basename = dirname($archiveFileName).'/'.basename($archiveFileName, '.'.$ext);
0 ignored issues
show
Bug introduced by
Are you sure $ext of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

290
            $basename = dirname($archiveFileName).'/'.basename($archiveFileName, '.'./** @scrutinizer ignore-type */ $ext);
Loading history...
291
        }
292
293 1
        $compression = null;
294
        switch ($ext) {
295 1
            case 'tar.gz':
296 1
            case 'tgz':
297
                $compression = Phar::GZ;
298
                break;
299 1
            case 'tar.bz2':
300 1
            case 'tbz2':
301
                $compression = Phar::BZ2;
302
                break;
303
        }
304
305 1
        $destination_file = $basename.'.tar';
306
        // if compression used and there is .tar archive with that's name,
307
        // use temp file
308 1
        if ($compression !== null && file_exists($basename.'.tar')) {
309
            $temp_basename = tempnam(sys_get_temp_dir(), 'tar-archive');
310
            unlink($temp_basename);
311
            $destination_file = $temp_basename.'.tar';
312
        }
313
314 1
        $tar = new PharData(
315 1
            $destination_file,
316 1
            0, null, Phar::TAR
317
        );
318
319
        try {
320 1
            foreach ($files as $localName => $filename) {
321 1
                if (is_null($filename)) {
322 1
                    if (!in_array($localName, ['/', ''], true)) {
323 1
                        if ($tar->addEmptyDir($localName) === false) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $tar->addEmptyDir($localName) targeting Phar::addEmptyDir() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
324 1
                            throw new ArchiveCreationException('Error when adding directory '.$localName.' to archive');
325
                        }
326
                    }
327
                } else {
328 1
                    if ($tar->addFile($filename, $localName) === false) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $tar->addFile($filename, $localName) targeting Phar::addFile() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
329 1
                        throw new ArchiveCreationException('Error when adding file '.$localName.' to archive');
330
                    }
331
                }
332
            }
333
        } catch (Exception $e) {
334
            throw new ArchiveCreationException('Error when creating archive: '.$e->getMessage(), $e->getCode(), $e);
335
        }
336
337
        switch ($compression) {
338 1
            case Phar::GZ:
339
                $tar->compress(Phar::GZ, $ext);
340
                break;
341 1
            case Phar::BZ2:
342
                $tar->compress(Phar::BZ2, $ext);
343
                break;
344
        }
345 1
        $tar = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $tar is dead and can be removed.
Loading history...
346
347
        // if compression used and original .tar file exist, clean it
348 1
        if ($compression !== null && file_exists($destination_file)) {
349
            unlink($destination_file);
350
        }
351
352
        // it temp file was used, rename it to destination archive name
353 1
        if (isset($temp_basename)) {
354
            rename($temp_basename.'.'.$ext, $archiveFileName);
355
        }
356
357 1
        return count($files);
358
    }
359
360
    /**
361
     * @param string $inArchiveName
362
     * @param string $content
363
     * @return bool
364
     */
365
    public function addFileFromString($inArchiveName, $content)
366
    {
367
        $this->tar->addFromString($inArchiveName, $content);
368
        return true;
369
    }
370
}