ArchiveEntry::getTargetSize()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * (c) 2018 Dennis Birkholz <[email protected]>
5
 *
6
 * $Id$
7
 * Author:    $Format:%an <%ae>, %ai$
8
 * Committer: $Format:%cn <%ce>, %cI$
9
 */
10
11
namespace morgue\archive;
12
13
/**
14
 * This class represents a single entry in an archive (file, directory)
15
 *
16
 * @author Dennis Birkholz <[email protected]>
17
 */
18
final class ArchiveEntry
19
{
20
    const UNIX_ATTRIBUTES_DEFAULT           = UNIX_ATTRIBUTE_USER_READ|UNIX_ATTRIBUTE_USER_WRITE|UNIX_ATTRIBUTE_GROUP_READ|UNIX_ATTRIBUTE_OTHER_READ;
21
    const UNIX_ATTRIBUTES_DEFAULT_FILE      = self::UNIX_ATTRIBUTES_DEFAULT|UNIX_ATTRIBUTE_TYPE_FILE;
22
    const UNIX_ATTRIBUTES_DEFAULT_DIRECTORY = self::UNIX_ATTRIBUTES_DEFAULT|UNIX_ATTRIBUTE_TYPE_DIRECTORY|UNIX_ATTRIBUTE_USER_EXECUTE|UNIX_ATTRIBUTE_GROUP_EXECUTE|UNIX_ATTRIBUTE_OTHER_EXECUTE;
23
24
    /**
25
     * The name of this entry, relative to the archive root or absolute/fully qualified
26
     * @var string
27
     */
28
    private $name;
29
30
    /**
31
     * @var int
32
     */
33
    private $uncompressedSize;
34
35
    /**
36
     * @var int
37
     */
38
    private $sourceSize;
39
40
    /**
41
     * @var int
42
     */
43
    private $targetSize;
44
45
    /**
46
     * @var \DateTimeInterface
47
     */
48
    private $creationTime;
49
50
    /**
51
     * @var \DateTimeInterface
52
     */
53
    private $modificationTime;
54
55
    /**
56
     * CRC32 checksum of the uncompressed file
57
     * @var int
58
     */
59
    private $checksumCrc32;
60
61
    /**
62
     * @var string
63
     */
64
    private $comment;
65
66
    /**
67
     * One of the COMPRESSION_METHOD_* constants
68
     * @var string
69
     */
70
    private $sourceCompressionMethod;
71
72
    /**
73
     * One of the COMPRESSION_METHOD_* constants
74
     * @var string
75
     */
76
    private $targetCompressionMethod;
77
78
    /**
79
     * Combination of DOS_ATTRIBUTE_* flags
80
     * May be unsupported by target file format
81
     * @var int
82
     */
83
    private $dosAttributes;
84
85
    /**
86
     * Combination of UNIX_ATTRIBUTE_* flags
87
     * May be unsupported by target file format
88
     * @var int
89
     */
90
    private $unixAttributes;
91
92
    /**
93
     * A PHP stream to the underlying file
94
     * The stream should not perform any translation (like decompression)
95
     *  so reading from this stream should yield data in the format
96
     *  specified by $sourceCompressionMethod
97
     * @var resource
98
     */
99
    private $sourceStream;
100
101
    /**
102
     * Fully qualified path to the underlying uncompressed file
103
     * @var string
104
     */
105
    private $sourcePath;
106
107
    /**
108
     * Binary string containing the content of this entry
109
     * @var string
110
     */
111
    private $sourceString;
112
113 2
    public function __construct(string $name)
114
    {
115 2
        $this->name = $name;
116 2
    }
117
118
    /**
119
     * Import data from the supplied $stat array. Format is the one returned by stat()/fstat()
120
     * @param array $stat
121
     */
122 3
    private function importStat(array $stat)
123
    {
124 3
        if (!empty($stat['mode']) && $this->unixAttributes === null) {
125 3
            $this->unixAttributes = $stat['mode'];
126
        }
127
128 3
        $timezone = new \DateTimeZone(\date_default_timezone_get());
129
130 3 View Code Duplication
        if (!empty($stat['ctime']) && $this->creationTime === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
131 3
            $this->creationTime = (new \DateTimeImmutable('@' . $stat['ctime']))->setTimezone($timezone);
0 ignored issues
show
Documentation Bug introduced by
It seems like (new \DateTimeImmutable(...>setTimezone($timezone) can also be of type false. However, the property $creationTime is declared as type object<DateTimeInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
132
        }
133
134 3 View Code Duplication
        if (!empty($stat['mtime']) && $this->modificationTime === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135 3
            $this->modificationTime = (new \DateTimeImmutable('@' . $stat['mtime']))->setTimezone($timezone);
0 ignored issues
show
Documentation Bug introduced by
It seems like (new \DateTimeImmutable(...>setTimezone($timezone) can also be of type false. However, the property $modificationTime is declared as type object<DateTimeInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
136
        }
137
138 3
        if ($this->unixAttributes & UNIX_ATTRIBUTE_TYPE_DIRECTORY) {
139 1
            $this->sourceSize = 0;
140 1
            $this->targetSize = 0;
141 1
            $this->uncompressedSize = 0;
142 1
            $this->sourceCompressionMethod = COMPRESSION_METHOD_STORE;
143 1
            $this->targetCompressionMethod = COMPRESSION_METHOD_STORE;
144
        }
145
146 2
        elseif ($this->unixAttributes & UNIX_ATTRIBUTE_TYPE_FILE) {
147 2
            $this->sourceSize = $stat['size'];
148 2
            if ($this->sourceCompressionMethod === COMPRESSION_METHOD_STORE) {
149 2
                $this->uncompressedSize = $this->sourceSize;
150
            }
151
        }
152 3
    }
153
154
    /**
155
     * @return resource
156
     */
157 3
    public function getSourceAsStream()
158
    {
159 3
        if ($this->sourceStream !== null) {
160 1
            return $this->sourceStream;
161
        }
162
163 2
        elseif ($this->sourcePath !== null) {
164 1
            return \fopen($this->sourcePath, 'r');
165
        }
166
167
        else {
168 1
            $fp = \fopen('php://memory', 'r+');
169 1
            \fwrite($fp, $this->sourceString);
170 1
            \fseek($fp, 0);
171 1
            return $fp;
172
        }
173
    }
174
175
    /**
176
     * @return string|null
177
     */
178 2
    public function getSourcePath()
179
    {
180 2
        return $this->sourcePath;
181
    }
182
183
    /**
184
     * Set the path of the file that will serve as source for this entry.
185
     * The file must exist and be readable.
186
     * You may provide a URL if a matching stream wrapper is registered with stat support.
187
     * Otherwise, use withSourceStream() to add a stream directly.
188
     *
189
     * @param string $path
190
     * @param string|null $compressionMethod
191
     * @return ArchiveEntry
192
     */
193 6
    public function withSourcePath(string $path, string $compressionMethod = null) : self
194
    {
195 6
        if (!\file_exists($path) || !\is_readable($path)) {
196 1
            throw new \InvalidArgumentException('Can not use non-existing or unreadable file as source.');
197
        }
198
199 5 View Code Duplication
        if ($this->sourcePath !== null || $this->sourceStream !== null || $this->sourceString !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
200 3
            throw new \InvalidArgumentException('Can not replace source, create a new entry instead.');
201
        }
202
203 3
        $obj = clone $this;
204 3
        $obj->sourcePath = $path;
205
206 3
        if ($compressionMethod !== null) {
207 1
            $obj->sourceCompressionMethod = $compressionMethod;
208
        }
209
210 3
        if ($compressionMethod === COMPRESSION_METHOD_STORE) {
211 1
            $obj->uncompressedSize = $obj->sourceSize;
212
        }
213
214 3
        if (($stat = @\stat($path)) !== false) {
215 3
            $obj->importStat($stat);
216
        }
217
218 3
        return $obj;
219
    }
220
221
    /**
222
     * @return resource|null
223
     */
224 1
    public function getSourceStream()
225
    {
226 1
        return $this->sourceStream;
227
    }
228
229
    /**
230
     * @param resource $stream
231
     * @param string $compressionMethod
232
     * @return ArchiveEntry
233
     */
234 4
    public function withSourceStream($stream, string $compressionMethod = null) : self
235
    {
236 4 View Code Duplication
        if ($this->sourcePath !== null || $this->sourceStream !== null || $this->sourceString !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
237 3
            throw new \InvalidArgumentException('Can not replace source, create a new entry instead.');
238
        }
239
240 2
        $obj = clone $this;
241 2
        $obj->sourcePath = null;
242 2
        $obj->sourceStream = $stream;
243 2
        $obj->sourceString = null;
244
245 2
        if ($compressionMethod !== null) {
246 1
            $obj->sourceCompressionMethod = $compressionMethod;
247
        }
248
249 2
        if (($stat = @\fstat($stream)) !== false) {
250 2
            $obj->importStat($stat);
251
        }
252
253 2
        return $obj;
254
    }
255
256
    /**
257
     * @return string|null
258
     */
259 1
    public function getSourceString()
260
    {
261 1
        return $this->sourceString;
262
    }
263
264
    /**
265
     * @param string $content
266
     * @param string $compressionMethod
267
     * @return ArchiveEntry
268
     */
269 4
    public function withSourceString(string $content, string $compressionMethod = null) : self
270
    {
271 4 View Code Duplication
        if ($this->sourcePath !== null || $this->sourceStream !== null || $this->sourceString !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
272 3
            throw new \InvalidArgumentException('Can not replace source, create a new entry instead.');
273
        }
274
275 2
        $obj = clone $this;
276 2
        $obj->sourcePath = null;
277 2
        $obj->sourceStream = null;
278 2
        $obj->sourceString = $content;
279 2
        $obj->sourceSize = \strlen($content);
280
281 2
        if ($compressionMethod !== null) {
282 1
            $obj->sourceCompressionMethod = $compressionMethod;
283
        }
284
285 2
        if ($compressionMethod === COMPRESSION_METHOD_STORE) {
286 1
            $obj->uncompressedSize = $obj->sourceSize;
287
        }
288
289 2
        $now = new \DateTimeImmutable('now', new \DateTimeZone(\date_default_timezone_get()));
290 2
        if ($obj->creationTime === null) {
291 2
            $obj->creationTime = $now;
292
        }
293
294 2
        if ($obj->modificationTime === null) {
295 2
            $obj->modificationTime = $now;
296
        }
297
298 2
        return $obj;
299
    }
300
301
    /**
302
     * @return string|null
303
     */
304 2
    public function getName()
305
    {
306 2
        return $this->name;
307
    }
308
309
    /**
310
     * @param string $name
311
     * @return ArchiveEntry
312
     */
313 1
    public function withName(string $name) : self
314
    {
315 1
        $obj = clone $this;
316 1
        $obj->name = $name;
317 1
        return $obj;
318
    }
319
320
    /**
321
     * @return int|null
322
     */
323 1
    public function getUncompressedSize()
324
    {
325 1
        return $this->uncompressedSize;
326
    }
327
328
    /**
329
     * @param int $uncompressedSize
330
     * @return ArchiveEntry
331
     */
332 1
    public function withUncompressedSize(int $uncompressedSize) : self
333
    {
334 1
        $obj = clone $this;
335 1
        $obj->uncompressedSize = $uncompressedSize;
336 1
        return $obj;
337
    }
338
339
    /**
340
     * @return int|null
341
     */
342 1
    public function getSourceSize()
343
    {
344 1
        return $this->sourceSize;
345
    }
346
347
    /**
348
     * @param int $sourceSize
349
     * @return ArchiveEntry
350
     */
351 1
    public function withSourceSize(int $sourceSize) : self
352
    {
353 1
        $obj = clone $this;
354 1
        $obj->sourceSize = $sourceSize;
355 1
        return $obj;
356
    }
357
358
    /**
359
     * @return int|null
360
     */
361 1
    public function getTargetSize()
362
    {
363 1
        return $this->targetSize;
364
    }
365
366
    /**
367
     * @param int $targetSize
368
     * @return ArchiveEntry
369
     */
370 1
    public function withTargetSize(int $targetSize) : self
371
    {
372 1
        $obj = clone $this;
373 1
        $obj->targetSize = $targetSize;
374 1
        return $obj;
375
    }
376
377
    /**
378
     * @return \DateTimeInterface|null
379
     */
380 1
    public function getCreationTime()
381
    {
382 1
        return $this->creationTime;
383
    }
384
385
    /**
386
     * @param \DateTimeInterface $creationTime
387
     * @return ArchiveEntry
388
     */
389 1
    public function withCreationTime(\DateTimeInterface $creationTime) : self
390
    {
391 1
        $obj = clone $this;
392 1
        $obj->creationTime = $creationTime;
393 1
        return $obj;
394
    }
395
396
    /**
397
     * @return \DateTimeInterface|null
398
     */
399 1
    public function getModificationTime()
400
    {
401 1
        return $this->modificationTime;
402
    }
403
404
    /**
405
     * @param \DateTimeInterface $modificationTime
406
     * @return ArchiveEntry
407
     */
408 1
    public function withModificationTime(\DateTimeInterface $modificationTime) : self
409
    {
410 1
        $obj = clone $this;
411 1
        $obj->modificationTime = $modificationTime;
412 1
        return $obj;
413
    }
414
415
    /**
416
     * @return int|null
417
     */
418 1
    public function getChecksumCrc32()
419
    {
420 1
        return $this->checksumCrc32;
421
    }
422
423
    /**
424
     * @param int $checksumCrc32
425
     * @return ArchiveEntry
426
     */
427 1
    public function withChecksumCrc32(int $checksumCrc32) : self
428
    {
429 1
        $obj = clone $this;
430 1
        $obj->checksumCrc32 = $checksumCrc32;
431 1
        return $obj;
432
    }
433
434
    /**
435
     * @return string|null
436
     */
437 1
    public function getComment()
438
    {
439 1
        return $this->comment;
440
    }
441
442
    /**
443
     * @param string|null $comment
444
     * @return ArchiveEntry
445
     */
446 2
    public function withComment(string $comment = null) : self
447
    {
448 2
        $obj = clone $this;
449 2
        $obj->comment = $comment;
450 2
        return $obj;
451
    }
452
453
    /**
454
     * @return string|null
455
     */
456 1
    public function getSourceCompressionMethod()
457
    {
458 1
        return $this->sourceCompressionMethod;
459
    }
460
461
    /**
462
     * Any of the COMPRESSION_METHOD_* constants
463
     *
464
     * @param string $sourceCompressionMethod
465
     * @return ArchiveEntry
466
     */
467 1
    public function withSourceCompressionMethod($sourceCompressionMethod) : self
468
    {
469 1
        $obj = clone $this;
470 1
        $obj->sourceCompressionMethod = $sourceCompressionMethod;
471 1
        return $obj;
472
    }
473
474
    /**
475
     * @return string|null
476
     */
477 1
    public function getTargetCompressionMethod()
478
    {
479 1
        return $this->targetCompressionMethod;
480
    }
481
482
    /**
483
     * @param string $targetCompressionMethod
484
     * @return ArchiveEntry
485
     */
486 1
    public function withTargetCompressionMethod($targetCompressionMethod) : self
487
    {
488 1
        $obj = clone $this;
489 1
        $obj->targetCompressionMethod = $targetCompressionMethod;
490 1
        return $obj;
491
    }
492
493
    /**
494
     * @return int|null
495
     */
496 1
    public function getDosAttributes()
497
    {
498 1
        return $this->dosAttributes;
499
    }
500
501
    /**
502
     * @param int $dosAttributes
503
     * @return ArchiveEntry
504
     */
505 1
    public function withDosAttributes(int $dosAttributes) : self
506
    {
507 1
        $obj = clone $this;
508 1
        $obj->dosAttributes = $dosAttributes;
509 1
        return $obj;
510
    }
511
512
    /**
513
     * @return int|null
514
     */
515 1
    public function getUnixAttributes()
516
    {
517 1
        return $this->unixAttributes;
518
    }
519
520
    /**
521
     * @param int $unixAttributes
522
     * @return ArchiveEntry
523
     */
524 1
    public function withUnixAttributes(int $unixAttributes) : self
525
    {
526 1
        $obj = clone $this;
527 1
        $obj->unixAttributes = $unixAttributes;
528 1
        return $obj;
529
    }
530
}
531