ZipEntry   F
last analyzed

Complexity

Total Complexity 180

Size/Duplication

Total Lines 1532
Duplicated Lines 0 %

Test Coverage

Coverage 90.23%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 382
c 1
b 0
f 1
dl 0
loc 1532
ccs 397
cts 440
cp 0.9023
rs 2
wmc 180

89 Methods

Rating   Name   Duplication   Size   Complexity  
A enableUtf8Name() 0 3 1
A setEncrypted() 0 6 1
A getDosTime() 0 3 1
A getLocalHeaderOffset() 0 3 1
A setCharset() 0 8 3
A getCompressedSize() 0 3 1
A isEncrypted() 0 3 1
A __construct() 0 6 1
A setExtractedOS() 0 10 3
B getExtractVersion() 0 27 8
A setCompressedSize() 0 10 2
A getSize() 0 5 1
A getCharset() 0 3 1
A setData() 0 3 1
A setSoftwareVersion() 0 5 1
A getSoftwareVersion() 0 7 2
A enableDataDescriptor() 0 3 1
A rename() 0 8 1
A isSetGeneralBitFlag() 0 3 1
A getName() 0 3 1
A setUncompressedSize() 0 10 2
A disableEncryption() 0 9 1
A isUtf8Flag() 0 3 1
A getGeneralPurposeBitFlags() 0 3 1
A setPlatform() 0 5 1
A getUncompressedSize() 0 3 1
A setCreatedOS() 0 10 3
A isDataDescriptorEnabled() 0 3 1
B updateCompressionLevel() 0 14 8
B setName() 0 38 10
A setSize() 0 5 1
A getCreatedOS() 0 3 1
A getOffset() 0 8 1
A getExtractedOS() 0 3 1
A isStrongEncryption() 0 3 1
A getData() 0 3 1
A setOffset() 0 8 1
A getCompressionMethod() 0 3 1
A getTime() 0 7 2
A setVersionNeededToExtract() 0 5 1
A getPlatform() 0 5 1
A setCompressionMethod() 0 15 3
A setExtractVersion() 0 5 1
A setMethod() 0 8 1
A getMethod() 0 8 1
A setGeneralPurposeBitFlags() 0 11 3
A create() 0 37 1
A setLocalHeaderOffset() 0 10 2
A getVersionNeededToExtract() 0 5 1
A setGeneralBitFlag() 0 9 2
A getCTime() 0 17 3
A setDosTime() 0 13 4
A getPassword() 0 3 1
A setInternalAttributes() 0 10 3
A getLocalExtraField() 0 3 1
A getATime() 0 24 5
A getCrc() 0 3 1
A addCdExtraField() 0 3 1
A getCompressionLevel() 0 3 1
A addExtraField() 0 4 1
A setCdExtraFields() 0 5 1
A isDirectory() 0 3 1
A getCdExtraField() 0 3 1
A getLocalExtraFields() 0 3 1
A isUnixSymlink() 0 3 1
A setEncryptionMethod() 0 14 2
A getCdExtraFields() 0 3 1
A setUnixMode() 0 13 3
A setCrc() 0 5 1
B updateGbpfCompLevel() 0 24 7
A __clone() 0 7 2
A setLocalExtraFields() 0 5 1
A getInternalAttributes() 0 3 1
A setExternalAttributes() 0 13 4
A setCompressionLevel() 0 22 4
A addLocalExtraField() 0 3 1
A hasExtraField() 0 7 2
A setTime() 0 9 2
A removeExtraField() 0 6 1
A setComment() 0 16 5
A getExtraField() 0 10 2
A getExternalAttributes() 0 3 1
B setPassword() 0 21 7
A getUnixMode() 0 17 5
A getEncryptionMethod() 0 3 1
A isDataDescriptorRequired() 0 3 1
B getMTime() 0 29 7
A isZip64ExtensionsRequired() 0 4 2
A getComment() 0 3 2

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
/** @noinspection PhpUsageOfSilenceOperatorInspection */
4
5
namespace PhpZip\Model;
6
7
use PhpZip\Constants\DosAttrs;
8
use PhpZip\Constants\DosCodePage;
9
use PhpZip\Constants\GeneralPurposeBitFlag;
10
use PhpZip\Constants\UnixStat;
11
use PhpZip\Constants\ZipCompressionLevel;
12
use PhpZip\Constants\ZipCompressionMethod;
13
use PhpZip\Constants\ZipConstants;
14
use PhpZip\Constants\ZipEncryptionMethod;
15
use PhpZip\Constants\ZipPlatform;
16
use PhpZip\Constants\ZipVersion;
17
use PhpZip\Exception\InvalidArgumentException;
18
use PhpZip\Exception\RuntimeException;
19
use PhpZip\Exception\ZipUnsupportMethodException;
20
use PhpZip\Model\Extra\ExtraFieldsCollection;
21
use PhpZip\Model\Extra\Fields\AsiExtraField;
22
use PhpZip\Model\Extra\Fields\ExtendedTimestampExtraField;
23
use PhpZip\Model\Extra\Fields\NtfsExtraField;
24
use PhpZip\Model\Extra\Fields\OldUnixExtraField;
25
use PhpZip\Model\Extra\Fields\UnicodePathExtraField;
26
use PhpZip\Model\Extra\Fields\WinZipAesExtraField;
27
use PhpZip\Model\Extra\ZipExtraField;
28
use PhpZip\Util\DateTimeConverter;
29
use PhpZip\Util\StringUtil;
30
31
/**
32
 * ZIP file entry.
33
 *
34
 * @see     https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
35
 *
36
 * @author  Ne-Lexa [email protected]
37
 * @license MIT
38
 */
39
class ZipEntry
40
{
41
    /** @var int the unknown value for numeric properties */
42
    const UNKNOWN = -1;
43
44
    /**
45
     * @var int DOS platform
46
     *
47
     * @deprecated Use {@see ZipPlatform::OS_DOS}
48
     */
49
    const PLATFORM_FAT = ZipPlatform::OS_DOS;
50
51
    /**
52
     * @var int Unix platform
53
     *
54
     * @deprecated Use {@see ZipPlatform::OS_UNIX}
55
     */
56
    const PLATFORM_UNIX = ZipPlatform::OS_UNIX;
57
58
    /**
59
     * @var int MacOS platform
60
     *
61
     * @deprecated Use {@see ZipPlatform::OS_MAC_OSX}
62
     */
63
    const PLATFORM_OS_X = ZipPlatform::OS_MAC_OSX;
64
65
    /**
66
     * Pseudo compression method for WinZip AES encrypted entries.
67
     * Require php extension openssl or mcrypt.
68
     *
69
     * @deprecated Use {@see ZipCompressionMethod::WINZIP_AES}
70
     */
71
    const METHOD_WINZIP_AES = ZipCompressionMethod::WINZIP_AES;
72
73
    /** @var string Entry name (filename in archive) */
74
    private $name;
75
76
    /** @var bool Is directory */
77
    private $isDirectory;
78
79
    /** @var ZipData|null Zip entry contents */
80
    private $data;
81
82
    /** @var int Made by platform */
83
    private $createdOS = self::UNKNOWN;
84
85
    /** @var int Extracted by platform */
86
    private $extractedOS = self::UNKNOWN;
87
88
    /** @var int Software version */
89
    private $softwareVersion = self::UNKNOWN;
90
91
    /** @var int Version needed to extract */
92
    private $extractVersion = self::UNKNOWN;
93
94
    /** @var int Compression method */
95
    private $compressionMethod = self::UNKNOWN;
96
97
    /** @var int General purpose bit flags */
98
    private $generalPurposeBitFlags = 0;
99
100
    /** @var int Dos time */
101
    private $dosTime = self::UNKNOWN;
102
103
    /** @var int Crc32 */
104
    private $crc = self::UNKNOWN;
105
106
    /** @var int Compressed size */
107
    private $compressedSize = self::UNKNOWN;
108
109
    /** @var int Uncompressed size */
110
    private $uncompressedSize = self::UNKNOWN;
111
112
    /** @var int Internal attributes */
113
    private $internalAttributes = 0;
114
115
    /** @var int External attributes */
116
    private $externalAttributes = 0;
117
118
    /** @var int relative Offset Of Local File Header */
119
    private $localHeaderOffset = 0;
120
121
    /**
122
     * Collections of Extra Fields in Central Directory.
123
     * Keys from Header ID [int] and value Extra Field [ExtraField].
124
     *
125
     * @var ExtraFieldsCollection
126
     */
127
    protected $cdExtraFields;
128
129
    /**
130
     * Collections of Extra Fields int local header.
131
     * Keys from Header ID [int] and value Extra Field [ExtraField].
132
     *
133
     * @var ExtraFieldsCollection
134
     */
135
    protected $localExtraFields;
136
137
    /** @var string|null comment field */
138
    private $comment;
139
140
    /** @var string|null entry password for read or write encryption data */
141
    private $password;
142
143
    /** @var int encryption method */
144
    private $encryptionMethod = ZipEncryptionMethod::NONE;
145
146
    /** @var int */
147
    private $compressionLevel = ZipCompressionLevel::NORMAL;
148
149
    /** @var string|null */
150
    private $charset;
151
152
    /**
153
     * ZipEntry constructor.
154
     *
155
     * @param string      $name    Entry name
156
     * @param string|null $charset DOS charset
157
     */
158 351
    public function __construct($name, $charset = null)
159
    {
160 351
        $this->setName($name, $charset);
161
162 348
        $this->cdExtraFields = new ExtraFieldsCollection();
163 348
        $this->localExtraFields = new ExtraFieldsCollection();
164 348
    }
165
166
    /**
167
     * This method only internal use.
168
     *
169
     * @param string      $name
170
     * @param int         $createdOS
171
     * @param int         $extractedOS
172
     * @param int         $softwareVersion
173
     * @param int         $extractVersion
174
     * @param int         $compressionMethod
175
     * @param int         $gpbf
176
     * @param int         $dosTime
177
     * @param int         $crc
178
     * @param int         $compressedSize
179
     * @param int         $uncompressedSize
180
     * @param int         $internalAttributes
181
     * @param int         $externalAttributes
182
     * @param int         $offsetLocalHeader
183
     * @param string|null $comment
184
     * @param string|null $charset
185
     *
186
     * @return ZipEntry
187
     *
188
     * @internal
189
     *
190
     * @noinspection PhpTooManyParametersInspection
191
     */
192 134
    public static function create(
193
        $name,
194
        $createdOS,
195
        $extractedOS,
196
        $softwareVersion,
197
        $extractVersion,
198
        $compressionMethod,
199
        $gpbf,
200
        $dosTime,
201
        $crc,
202
        $compressedSize,
203
        $uncompressedSize,
204
        $internalAttributes,
205
        $externalAttributes,
206
        $offsetLocalHeader,
207
        $comment,
208
        $charset
209
    ) {
210 134
        $entry = new self($name);
211 134
        $entry->createdOS = (int) $createdOS;
212 134
        $entry->extractedOS = (int) $extractedOS;
213 134
        $entry->softwareVersion = (int) $softwareVersion;
214 134
        $entry->extractVersion = (int) $extractVersion;
215 134
        $entry->compressionMethod = (int) $compressionMethod;
216 134
        $entry->generalPurposeBitFlags = (int) $gpbf;
217 134
        $entry->dosTime = (int) $dosTime;
218 134
        $entry->crc = (int) $crc;
219 134
        $entry->compressedSize = (int) $compressedSize;
220 134
        $entry->uncompressedSize = (int) $uncompressedSize;
221 134
        $entry->internalAttributes = (int) $internalAttributes;
222 134
        $entry->externalAttributes = (int) $externalAttributes;
223 134
        $entry->localHeaderOffset = (int) $offsetLocalHeader;
224 134
        $entry->setComment($comment);
225 134
        $entry->setCharset($charset);
226 134
        $entry->updateCompressionLevel();
227
228 134
        return $entry;
229
    }
230
231
    /**
232
     * Set entry name.
233
     *
234
     * @param string      $name    New entry name
235
     * @param string|null $charset
236
     *
237
     * @return ZipEntry
238
     */
239 351
    private function setName($name, $charset = null)
240
    {
241 351
        if ($name === null) {
0 ignored issues
show
introduced by
The condition $name === null is always false.
Loading history...
242 1
            throw new InvalidArgumentException('zip entry name is null');
243
        }
244
245 350
        $name = ltrim((string) $name, '\\/');
246
247 350
        if ($name === '') {
248 2
            throw new InvalidArgumentException('Empty zip entry name');
249
        }
250
251 348
        $name = (string) $name;
252 348
        $length = \strlen($name);
253
254 348
        if ($length > 0xffff) {
255
            throw new InvalidArgumentException('Illegal zip entry name parameter');
256
        }
257
258 348
        $this->setCharset($charset);
259
260 348
        if ($this->charset === null && !StringUtil::isASCII($name)) {
261 24
            $this->enableUtf8Name(true);
262
        }
263 348
        $this->name = $name;
264 348
        $this->isDirectory = ($length = \strlen($name)) >= 1 && $name[$length - 1] === '/';
265 348
        $this->externalAttributes = $this->isDirectory ? DosAttrs::DOS_DIRECTORY : DosAttrs::DOS_ARCHIVE;
266
267 348
        if ($this->extractVersion !== self::UNKNOWN) {
268 5
            $this->extractVersion = max(
269 5
                $this->extractVersion,
270 5
                $this->isDirectory ?
271 1
                    ZipVersion::v20_DEFLATED_FOLDER_ZIPCRYPTO :
272 5
                    ZipVersion::v10_DEFAULT_MIN
273
            );
274
        }
275
276 348
        return $this;
277
    }
278
279
    /**
280
     * @param string|null $charset
281
     *
282
     * @return ZipEntry
283
     *
284
     * @see DosCodePage::getCodePages()
285
     */
286 348
    public function setCharset($charset = null)
287
    {
288 348
        if ($charset !== null && $charset === '') {
289 1
            throw new InvalidArgumentException('Empty charset');
290
        }
291 348
        $this->charset = $charset;
292
293 348
        return $this;
294
    }
295
296
    /**
297
     * @return string|null
298
     */
299 137
    public function getCharset()
300
    {
301 137
        return $this->charset;
302
    }
303
304
    /**
305
     * @param string $newName New entry name
306
     *
307
     * @return ZipEntry new {@see ZipEntry} object with new name
308
     *
309
     * @internal
310
     */
311 8
    public function rename($newName)
312
    {
313 8
        $newEntry = clone $this;
314 8
        $newEntry->setName($newName);
315
316 8
        $newEntry->removeExtraField(UnicodePathExtraField::HEADER_ID);
317
318 8
        return $newEntry;
319
    }
320
321
    /**
322
     * Returns the ZIP entry name.
323
     *
324
     * @return string
325
     */
326 182
    public function getName()
327
    {
328 182
        return $this->name;
329
    }
330
331
    /**
332
     * @return ZipData|null
333
     *
334
     * @internal
335
     */
336 151
    public function getData()
337
    {
338 151
        return $this->data;
339
    }
340
341
    /**
342
     * @param ZipData|null $data
343
     *
344
     * @internal
345
     */
346 173
    public function setData($data)
347
    {
348 173
        $this->data = $data;
349 173
    }
350
351
    /**
352
     * @return int Get platform
353
     *
354
     * @deprecated Use {@see ZipEntry::getCreatedOS()}
355
     */
356
    public function getPlatform()
357
    {
358
        @trigger_error(__METHOD__ . ' is deprecated. Use ' . __CLASS__ . '::getCreatedOS()', \E_USER_DEPRECATED);
359
360
        return $this->getCreatedOS();
361
    }
362
363
    /**
364
     * @param int $platform
365
     *
366
     * @return ZipEntry
367
     *
368
     * @deprecated Use {@see ZipEntry::setCreatedOS()}
369
     */
370
    public function setPlatform($platform)
371
    {
372
        @trigger_error(__METHOD__ . ' is deprecated. Use ' . __CLASS__ . '::setCreatedOS()', \E_USER_DEPRECATED);
373
374
        return $this->setCreatedOS($platform);
375
    }
376
377
    /**
378
     * @return int platform
379
     */
380 154
    public function getCreatedOS()
381
    {
382 154
        return $this->createdOS;
383
    }
384
385
    /**
386
     * Set platform.
387
     *
388
     * @param int $platform
389
     *
390
     * @return ZipEntry
391
     */
392 178
    public function setCreatedOS($platform)
393
    {
394 178
        $platform = (int) $platform;
395
396 178
        if ($platform < 0x00 || $platform > 0xff) {
397 2
            throw new InvalidArgumentException('Platform out of range');
398
        }
399 176
        $this->createdOS = $platform;
400
401 176
        return $this;
402
    }
403
404
    /**
405
     * @return int
406
     */
407 139
    public function getExtractedOS()
408
    {
409 139
        return $this->extractedOS;
410
    }
411
412
    /**
413
     * Set extracted OS.
414
     *
415
     * @param int $platform
416
     *
417
     * @return ZipEntry
418
     */
419 178
    public function setExtractedOS($platform)
420
    {
421 178
        $platform = (int) $platform;
422
423 178
        if ($platform < 0x00 || $platform > 0xff) {
424 2
            throw new InvalidArgumentException('Platform out of range');
425
        }
426 176
        $this->extractedOS = $platform;
427
428 176
        return $this;
429
    }
430
431
    /**
432
     * @return int
433
     */
434 136
    public function getSoftwareVersion()
435
    {
436 136
        if ($this->softwareVersion === self::UNKNOWN) {
437 134
            return $this->getExtractVersion();
438
        }
439
440 37
        return $this->softwareVersion;
441
    }
442
443
    /**
444
     * @param int $softwareVersion
445
     *
446
     * @return ZipEntry
447
     */
448 1
    public function setSoftwareVersion($softwareVersion)
449
    {
450 1
        $this->softwareVersion = (int) $softwareVersion;
451
452 1
        return $this;
453
    }
454
455
    /**
456
     * Version needed to extract.
457
     *
458
     * @return int
459
     *
460
     * @deprecated Use {@see ZipEntry::getExtractVersion()}
461
     */
462
    public function getVersionNeededToExtract()
463
    {
464
        @trigger_error(__METHOD__ . ' is deprecated. Use ' . __CLASS__ . '::getExtractVersion()', \E_USER_DEPRECATED);
465
466
        return $this->getExtractVersion();
467
    }
468
469
    /**
470
     * Version needed to extract.
471
     *
472
     * @return int
473
     */
474 145
    public function getExtractVersion()
475
    {
476 145
        if ($this->extractVersion === self::UNKNOWN) {
477 143
            if (ZipEncryptionMethod::isWinZipAesMethod($this->encryptionMethod)) {
478 14
                return ZipVersion::v51_ENCR_AES_RC2_CORRECT;
479
            }
480
481 138
            if ($this->compressionMethod === ZipCompressionMethod::BZIP2) {
482 6
                return ZipVersion::v46_BZIP2;
483
            }
484
485 138
            if ($this->isZip64ExtensionsRequired()) {
486
                return ZipVersion::v45_ZIP64_EXT;
487
            }
488
489
            if (
490 138
                $this->compressionMethod === ZipCompressionMethod::DEFLATED ||
491 121
                $this->isDirectory ||
492 138
                $this->encryptionMethod === ZipEncryptionMethod::PKWARE
493
            ) {
494 69
                return ZipVersion::v20_DEFLATED_FOLDER_ZIPCRYPTO;
495
            }
496
497 113
            return ZipVersion::v10_DEFAULT_MIN;
498
        }
499
500 32
        return $this->extractVersion;
501
    }
502
503
    /**
504
     * Set version needed to extract.
505
     *
506
     * @param int $version
507
     *
508
     * @return ZipEntry
509
     *
510
     * @deprecated Use {@see ZipEntry::setExtractVersion()}
511
     */
512
    public function setVersionNeededToExtract($version)
513
    {
514
        @trigger_error(__METHOD__ . ' is deprecated. Use ' . __CLASS__ . '::setExtractVersion()', \E_USER_DEPRECATED);
515
516
        return $this->setExtractVersion($version);
517
    }
518
519
    /**
520
     * Set version needed to extract.
521
     *
522
     * @param int $version
523
     *
524
     * @return ZipEntry
525
     */
526 2
    public function setExtractVersion($version)
527
    {
528 2
        $this->extractVersion = max(ZipVersion::v10_DEFAULT_MIN, (int) $version);
529
530 2
        return $this;
531
    }
532
533
    /**
534
     * Returns the compressed size of this entry.
535
     *
536
     * @return int
537
     */
538 154
    public function getCompressedSize()
539
    {
540 154
        return $this->compressedSize;
541
    }
542
543
    /**
544
     * Sets the compressed size of this entry.
545
     *
546
     * @param int $compressedSize the Compressed Size
547
     *
548
     * @return ZipEntry
549
     *
550
     * @internal
551
     */
552 154
    public function setCompressedSize($compressedSize)
553
    {
554 154
        $compressedSize = (int) $compressedSize;
555
556 154
        if ($compressedSize < self::UNKNOWN) {
557 1
            throw new InvalidArgumentException('Compressed size < ' . self::UNKNOWN);
558
        }
559 153
        $this->compressedSize = $compressedSize;
560
561 153
        return $this;
562
    }
563
564
    /**
565
     * Returns the uncompressed size of this entry.
566
     *
567
     * @return int
568
     *
569
     * @deprecated Use {@see ZipEntry::getUncompressedSize()}
570
     */
571
    public function getSize()
572
    {
573
        @trigger_error(__METHOD__ . ' is deprecated. Use ' . __CLASS__ . '::getUncompressedSize()', \E_USER_DEPRECATED);
574
575
        return $this->getUncompressedSize();
576
    }
577
578
    /**
579
     * Sets the uncompressed size of this entry.
580
     *
581
     * @param int $size the (Uncompressed) Size
582
     *
583
     * @return ZipEntry
584
     *
585
     * @deprecated Use {@see ZipEntry::setUncompressedSize()}
586
     *
587
     * @internal
588
     */
589
    public function setSize($size)
590
    {
591
        @trigger_error(__METHOD__ . ' is deprecated. Use ' . __CLASS__ . '::setUncompressedSize()', \E_USER_DEPRECATED);
592
593
        return $this->setUncompressedSize($size);
594
    }
595
596
    /**
597
     * Returns the uncompressed size of this entry.
598
     *
599
     * @return int
600
     */
601 154
    public function getUncompressedSize()
602
    {
603 154
        return $this->uncompressedSize;
604
    }
605
606
    /**
607
     * Sets the uncompressed size of this entry.
608
     *
609
     * @param int $uncompressedSize the (Uncompressed) Size
610
     *
611
     * @return ZipEntry
612
     *
613
     * @internal
614
     */
615 183
    public function setUncompressedSize($uncompressedSize)
616
    {
617 183
        $uncompressedSize = (int) $uncompressedSize;
618
619 183
        if ($uncompressedSize < self::UNKNOWN) {
620 1
            throw new InvalidArgumentException('Uncompressed size < ' . self::UNKNOWN);
621
        }
622 182
        $this->uncompressedSize = $uncompressedSize;
623
624 182
        return $this;
625
    }
626
627
    /**
628
     * Return relative Offset Of Local File Header.
629
     *
630
     * @return int
631
     */
632 146
    public function getLocalHeaderOffset()
633
    {
634 146
        return $this->localHeaderOffset;
635
    }
636
637
    /**
638
     * @param int $localHeaderOffset
639
     *
640
     * @return ZipEntry
641
     *
642
     * @internal
643
     */
644 138
    public function setLocalHeaderOffset($localHeaderOffset)
645
    {
646 138
        $localHeaderOffset = (int) $localHeaderOffset;
647
648 138
        if ($localHeaderOffset < 0) {
649 1
            throw new InvalidArgumentException('Negative $localHeaderOffset');
650
        }
651 138
        $this->localHeaderOffset = $localHeaderOffset;
652
653 138
        return $this;
654
    }
655
656
    /**
657
     * Return relative Offset Of Local File Header.
658
     *
659
     * @return int
660
     *
661
     * @deprecated Use {@see ZipEntry::getLocalHeaderOffset()}
662
     */
663
    public function getOffset()
664
    {
665
        @trigger_error(
666
            __METHOD__ . ' is deprecated. Use ' . __CLASS__ . '::getLocalHeaderOffset()',
667
            \E_USER_DEPRECATED
668
        );
669
670
        return $this->getLocalHeaderOffset();
671
    }
672
673
    /**
674
     * @param int $offset
675
     *
676
     * @return ZipEntry
677
     *
678
     * @deprecated Use {@see ZipEntry::setLocalHeaderOffset()}
679
     *
680
     * @internal
681
     */
682
    public function setOffset($offset)
683
    {
684
        @trigger_error(
685
            __METHOD__ . ' is deprecated. Use ' . __CLASS__ . '::setLocalHeaderOffset()',
686
            \E_USER_DEPRECATED
687
        );
688
689
        return $this->setLocalHeaderOffset($offset);
690
    }
691
692
    /**
693
     * Returns the General Purpose Bit Flags.
694
     *
695
     * @return int
696
     */
697 150
    public function getGeneralPurposeBitFlags()
698
    {
699 150
        return $this->generalPurposeBitFlags;
700
    }
701
702
    /**
703
     * Sets the General Purpose Bit Flags.
704
     *
705
     * @param int $gpbf general purpose bit flags
706
     *
707
     * @return ZipEntry
708
     *
709
     * @internal
710
     */
711 8
    public function setGeneralPurposeBitFlags($gpbf)
712
    {
713 8
        $gpbf = (int) $gpbf;
714
715 8
        if ($gpbf < 0x0000 || $gpbf > 0xffff) {
716 2
            throw new InvalidArgumentException('general purpose bit flags out of range');
717
        }
718 6
        $this->generalPurposeBitFlags = $gpbf;
719 6
        $this->updateCompressionLevel();
720
721 6
        return $this;
722
    }
723
724 194
    private function updateCompressionLevel()
725
    {
726 194
        if ($this->compressionMethod === ZipCompressionMethod::DEFLATED) {
727 81
            $bit1 = $this->isSetGeneralBitFlag(GeneralPurposeBitFlag::COMPRESSION_FLAG1);
728 81
            $bit2 = $this->isSetGeneralBitFlag(GeneralPurposeBitFlag::COMPRESSION_FLAG2);
729
730 81
            if ($bit1 && !$bit2) {
731 4
                $this->compressionLevel = ZipCompressionLevel::MAXIMUM;
732 80
            } elseif (!$bit1 && $bit2) {
733 3
                $this->compressionLevel = ZipCompressionLevel::FAST;
734 80
            } elseif ($bit1 && $bit2) {
735 7
                $this->compressionLevel = ZipCompressionLevel::SUPER_FAST;
736
            } else {
737 80
                $this->compressionLevel = ZipCompressionLevel::NORMAL;
738
            }
739
        }
740 194
    }
741
742
    /**
743
     * @param int  $mask
744
     * @param bool $enable
745
     *
746
     * @return ZipEntry
747
     */
748 52
    private function setGeneralBitFlag($mask, $enable)
749
    {
750 52
        if ($enable) {
751 47
            $this->generalPurposeBitFlags |= $mask;
752
        } else {
753 8
            $this->generalPurposeBitFlags &= ~$mask;
754
        }
755
756 52
        return $this;
757
    }
758
759
    /**
760
     * @param int $mask
761
     *
762
     * @return bool
763
     */
764 185
    private function isSetGeneralBitFlag($mask)
765
    {
766 185
        return ($this->generalPurposeBitFlags & $mask) === $mask;
767
    }
768
769
    /**
770
     * @return bool
771
     */
772 136
    public function isDataDescriptorEnabled()
773
    {
774 136
        return $this->isSetGeneralBitFlag(GeneralPurposeBitFlag::DATA_DESCRIPTOR);
775
    }
776
777
    /**
778
     * Enabling or disabling the use of the Data Descriptor block.
779
     *
780
     * @param bool $enabled
781
     */
782 5
    public function enableDataDescriptor($enabled = true)
783
    {
784 5
        $this->setGeneralBitFlag(GeneralPurposeBitFlag::DATA_DESCRIPTOR, (bool) $enabled);
785 5
    }
786
787
    /**
788
     * @param bool $enabled
789
     */
790 27
    public function enableUtf8Name($enabled)
791
    {
792 27
        $this->setGeneralBitFlag(GeneralPurposeBitFlag::UTF8, (bool) $enabled);
793 27
    }
794
795
    /**
796
     * @return bool
797
     */
798 3
    public function isUtf8Flag()
799
    {
800 3
        return $this->isSetGeneralBitFlag(GeneralPurposeBitFlag::UTF8);
801
    }
802
803
    /**
804
     * Returns true if and only if this ZIP entry is encrypted.
805
     *
806
     * @return bool
807
     */
808 160
    public function isEncrypted()
809
    {
810 160
        return $this->isSetGeneralBitFlag(GeneralPurposeBitFlag::ENCRYPTION);
811
    }
812
813
    /**
814
     * @return bool
815
     */
816 81
    public function isStrongEncryption()
817
    {
818 81
        return $this->isSetGeneralBitFlag(GeneralPurposeBitFlag::STRONG_ENCRYPTION);
819
    }
820
821
    /**
822
     * Sets the encryption property to false and removes any other
823
     * encryption artifacts.
824
     *
825
     * @return ZipEntry
826
     */
827 5
    public function disableEncryption()
828
    {
829 5
        $this->setEncrypted(false);
830 5
        $this->removeExtraField(WinZipAesExtraField::HEADER_ID);
831 5
        $this->encryptionMethod = ZipEncryptionMethod::NONE;
832 5
        $this->password = null;
833 5
        $this->extractVersion = self::UNKNOWN;
834
835 5
        return $this;
836
    }
837
838
    /**
839
     * Sets the encryption flag for this ZIP entry.
840
     *
841
     * @param bool $encrypted
842
     *
843
     * @return ZipEntry
844
     */
845 32
    private function setEncrypted($encrypted)
846
    {
847 32
        $encrypted = (bool) $encrypted;
848 32
        $this->setGeneralBitFlag(GeneralPurposeBitFlag::ENCRYPTION, $encrypted);
849
850 32
        return $this;
851
    }
852
853
    /**
854
     * Returns the compression method for this entry.
855
     *
856
     * @return int
857
     *
858
     * @deprecated Use {@see ZipEntry::getCompressionMethod()}
859
     */
860
    public function getMethod()
861
    {
862
        @trigger_error(
863
            __METHOD__ . ' is deprecated. Use ' . __CLASS__ . '::getCompressionMethod()',
864
            \E_USER_DEPRECATED
865
        );
866
867
        return $this->getCompressionMethod();
868
    }
869
870
    /**
871
     * Returns the compression method for this entry.
872
     *
873
     * @return int
874
     */
875 142
    public function getCompressionMethod()
876
    {
877 142
        return $this->compressionMethod;
878
    }
879
880
    /**
881
     * Sets the compression method for this entry.
882
     *
883
     * @param int $method
884
     *
885
     * @throws ZipUnsupportMethodException
886
     *
887
     * @return ZipEntry
888
     *
889
     * @deprecated Use {@see ZipEntry::setCompressionMethod()}
890
     */
891
    public function setMethod($method)
892
    {
893
        @trigger_error(
894
            __METHOD__ . ' is deprecated. Use ' . __CLASS__ . '::setCompressionMethod()',
895
            \E_USER_DEPRECATED
896
        );
897
898
        return $this->setCompressionMethod($method);
899
    }
900
901
    /**
902
     * Sets the compression method for this entry.
903
     *
904
     * @param int $compressionMethod
905
     *
906
     * @throws ZipUnsupportMethodException
907
     *
908
     * @return ZipEntry
909
     *
910
     * @see ZipCompressionMethod::STORED
911
     * @see ZipCompressionMethod::DEFLATED
912
     * @see ZipCompressionMethod::BZIP2
913
     */
914 213
    public function setCompressionMethod($compressionMethod)
915
    {
916 213
        $compressionMethod = (int) $compressionMethod;
917
918 213
        if ($compressionMethod < 0x0000 || $compressionMethod > 0xffff) {
919 2
            throw new InvalidArgumentException('method out of range: ' . $compressionMethod);
920
        }
921
922 211
        ZipCompressionMethod::checkSupport($compressionMethod);
923
924 183
        $this->compressionMethod = $compressionMethod;
925 183
        $this->updateCompressionLevel();
926 183
        $this->extractVersion = self::UNKNOWN;
927
928 183
        return $this;
929
    }
930
931
    /**
932
     * Get Unix Timestamp.
933
     *
934
     * @return int
935
     */
936 15
    public function getTime()
937
    {
938 15
        if ($this->getDosTime() === self::UNKNOWN) {
939 1
            return self::UNKNOWN;
940
        }
941
942 14
        return DateTimeConverter::msDosToUnix($this->getDosTime());
943
    }
944
945
    /**
946
     * Get Dos Time.
947
     *
948
     * @return int
949
     */
950 147
    public function getDosTime()
951
    {
952 147
        return $this->dosTime;
953
    }
954
955
    /**
956
     * Set Dos Time.
957
     *
958
     * @param int $dosTime
959
     *
960
     * @return ZipEntry
961
     */
962 170
    public function setDosTime($dosTime)
963
    {
964 170
        $dosTime = (int) $dosTime;
965
966 170
        if (\PHP_INT_SIZE === 8) {
967 170
            if ($dosTime < 0x00000000 || $dosTime > 0xffffffff) {
968 2
                throw new InvalidArgumentException('DosTime out of range');
969
            }
970
        }
971
972 168
        $this->dosTime = $dosTime;
973
974 168
        return $this;
975
    }
976
977
    /**
978
     * Set time from unix timestamp.
979
     *
980
     * @param int $unixTimestamp
981
     *
982
     * @return ZipEntry
983
     */
984 164
    public function setTime($unixTimestamp)
985
    {
986 164
        if ($unixTimestamp !== self::UNKNOWN) {
987 164
            $this->setDosTime(DateTimeConverter::unixToMsDos($unixTimestamp));
988
        } else {
989 1
            $this->dosTime = 0;
990
        }
991
992 164
        return $this;
993
    }
994
995
    /**
996
     * Returns the external file attributes.
997
     *
998
     * @return int the external file attributes
999
     */
1000 145
    public function getExternalAttributes()
1001
    {
1002 145
        return $this->externalAttributes;
1003
    }
1004
1005
    /**
1006
     * Sets the external file attributes.
1007
     *
1008
     * @param int $externalAttributes the external file attributes
1009
     *
1010
     * @return ZipEntry
1011
     */
1012 196
    public function setExternalAttributes($externalAttributes)
1013
    {
1014 196
        $this->externalAttributes = (int) $externalAttributes;
1015
1016 196
        if (\PHP_INT_SIZE === 8) {
1017 196
            if ($externalAttributes < 0x00000000 || $externalAttributes > 0xffffffff) {
1018 2
                throw new InvalidArgumentException('external attributes out of range: ' . $externalAttributes);
1019
            }
1020
        }
1021
1022 194
        $this->externalAttributes = $externalAttributes;
1023
1024 194
        return $this;
1025
    }
1026
1027
    /**
1028
     * Returns the internal file attributes.
1029
     *
1030
     * @return int the internal file attributes
1031
     */
1032 136
    public function getInternalAttributes()
1033
    {
1034 136
        return $this->internalAttributes;
1035
    }
1036
1037
    /**
1038
     * Sets the internal file attributes.
1039
     *
1040
     * @param int $internalAttributes the internal file attributes
1041
     *
1042
     * @return ZipEntry
1043
     */
1044 5
    public function setInternalAttributes($internalAttributes)
1045
    {
1046 5
        $internalAttributes = (int) $internalAttributes;
1047
1048 5
        if ($internalAttributes < 0x0000 || $internalAttributes > 0xffff) {
1049 2
            throw new InvalidArgumentException('internal attributes out of range');
1050
        }
1051 3
        $this->internalAttributes = $internalAttributes;
1052
1053 3
        return $this;
1054
    }
1055
1056
    /**
1057
     * Returns true if and only if this ZIP entry represents a directory entry
1058
     * (i.e. end with '/').
1059
     *
1060
     * @return bool
1061
     */
1062 202
    final public function isDirectory()
1063
    {
1064 202
        return $this->isDirectory;
1065
    }
1066
1067
    /**
1068
     * @return ExtraFieldsCollection
1069
     */
1070 149
    public function getCdExtraFields()
1071
    {
1072 149
        return $this->cdExtraFields;
1073
    }
1074
1075
    /**
1076
     * @param int $headerId
1077
     *
1078
     * @return ZipExtraField|null
1079
     */
1080 145
    public function getCdExtraField($headerId)
1081
    {
1082 145
        return $this->cdExtraFields->get((int) $headerId);
1083
    }
1084
1085
    /**
1086
     * @param ExtraFieldsCollection $cdExtraFields
1087
     *
1088
     * @return ZipEntry
1089
     */
1090 1
    public function setCdExtraFields(ExtraFieldsCollection $cdExtraFields)
1091
    {
1092 1
        $this->cdExtraFields = $cdExtraFields;
1093
1094 1
        return $this;
1095
    }
1096
1097
    /**
1098
     * @return ExtraFieldsCollection
1099
     */
1100 148
    public function getLocalExtraFields()
1101
    {
1102 148
        return $this->localExtraFields;
1103
    }
1104
1105
    /**
1106
     * @param int $headerId
1107
     *
1108
     * @return ZipExtraField|null
1109
     */
1110 154
    public function getLocalExtraField($headerId)
1111
    {
1112 154
        return $this->localExtraFields[(int) $headerId];
1113
    }
1114
1115
    /**
1116
     * @param ExtraFieldsCollection $localExtraFields
1117
     *
1118
     * @return ZipEntry
1119
     */
1120 1
    public function setLocalExtraFields(ExtraFieldsCollection $localExtraFields)
1121
    {
1122 1
        $this->localExtraFields = $localExtraFields;
1123
1124 1
        return $this;
1125
    }
1126
1127
    /**
1128
     * @param int $headerId
1129
     *
1130
     * @return ZipExtraField|null
1131
     */
1132 142
    public function getExtraField($headerId)
1133
    {
1134 142
        $headerId = (int) $headerId;
1135 142
        $local = $this->getLocalExtraField($headerId);
1136
1137 142
        if ($local === null) {
1138 141
            return $this->getCdExtraField($headerId);
1139
        }
1140
1141 16
        return $local;
1142
    }
1143
1144
    /**
1145
     * @param int $headerId
1146
     *
1147
     * @return bool
1148
     */
1149 17
    public function hasExtraField($headerId)
1150
    {
1151 17
        $headerId = (int) $headerId;
1152
1153
        return
1154 17
            isset($this->localExtraFields[$headerId]) ||
1155 17
            isset($this->cdExtraFields[$headerId]);
1156
    }
1157
1158
    /**
1159
     * @param int $headerId
1160
     */
1161 18
    public function removeExtraField($headerId)
1162
    {
1163 18
        $headerId = (int) $headerId;
1164
1165 18
        $this->cdExtraFields->remove($headerId);
1166 18
        $this->localExtraFields->remove($headerId);
1167 18
    }
1168
1169
    /**
1170
     * @param ZipExtraField $zipExtraField
1171
     */
1172 6
    public function addExtraField(ZipExtraField $zipExtraField)
1173
    {
1174 6
        $this->addLocalExtraField($zipExtraField);
1175 6
        $this->addCdExtraField($zipExtraField);
1176 6
    }
1177
1178
    /**
1179
     * @param ZipExtraField $zipExtraField
1180
     */
1181 6
    public function addLocalExtraField(ZipExtraField $zipExtraField)
1182
    {
1183 6
        $this->localExtraFields->add($zipExtraField);
1184 6
    }
1185
1186
    /**
1187
     * @param ZipExtraField $zipExtraField
1188
     */
1189 6
    public function addCdExtraField(ZipExtraField $zipExtraField)
1190
    {
1191 6
        $this->cdExtraFields->add($zipExtraField);
1192 6
    }
1193
1194
    /**
1195
     * Returns comment entry.
1196
     *
1197
     * @return string
1198
     */
1199 137
    public function getComment()
1200
    {
1201 137
        return $this->comment !== null ? $this->comment : '';
1202
    }
1203
1204
    /**
1205
     * Set entry comment.
1206
     *
1207
     * @param string|null $comment
1208
     *
1209
     * @return ZipEntry
1210
     */
1211 138
    public function setComment($comment)
1212
    {
1213 138
        if ($comment !== null) {
1214 7
            $commentLength = \strlen($comment);
1215
1216 7
            if ($commentLength > 0xffff) {
1217 3
                throw new InvalidArgumentException('Comment too long');
1218
            }
1219
1220 4
            if ($this->charset === null && !StringUtil::isASCII($comment)) {
1221 4
                $this->enableUtf8Name(true);
1222
            }
1223
        }
1224 135
        $this->comment = $comment;
1225
1226 135
        return $this;
1227
    }
1228
1229
    /**
1230
     * @return bool
1231
     */
1232 142
    public function isDataDescriptorRequired()
1233
    {
1234 142
        return ($this->getCrc() | $this->getCompressedSize() | $this->getUncompressedSize()) === self::UNKNOWN;
1235
    }
1236
1237
    /**
1238
     * Return crc32 content or 0 for WinZip AES v2.
1239
     *
1240
     * @return int
1241
     */
1242 147
    public function getCrc()
1243
    {
1244 147
        return $this->crc;
1245
    }
1246
1247
    /**
1248
     * Set crc32 content.
1249
     *
1250
     * @param int $crc
1251
     *
1252
     * @return ZipEntry
1253
     *
1254
     * @internal
1255
     */
1256 141
    public function setCrc($crc)
1257
    {
1258 141
        $this->crc = (int) $crc;
1259
1260 141
        return $this;
1261
    }
1262
1263
    /**
1264
     * @return string|null
1265
     */
1266 47
    public function getPassword()
1267
    {
1268 47
        return $this->password;
1269
    }
1270
1271
    /**
1272
     * Set password and encryption method from entry.
1273
     *
1274
     * @param string|null $password
1275
     * @param int|null    $encryptionMethod
1276
     *
1277
     * @return ZipEntry
1278
     */
1279 27
    public function setPassword($password, $encryptionMethod = null)
1280
    {
1281 27
        if (!$this->isDirectory) {
1282 26
            if ($password === null || $password === '') {
1283 4
                $this->password = null;
1284 4
                $this->disableEncryption();
1285
            } else {
1286 24
                $this->password = (string) $password;
1287
1288 24
                if ($encryptionMethod === null && $this->encryptionMethod === ZipEncryptionMethod::NONE) {
1289 14
                    $encryptionMethod = ZipEncryptionMethod::WINZIP_AES_256;
1290
                }
1291
1292 24
                if ($encryptionMethod !== null) {
1293 22
                    $this->setEncryptionMethod($encryptionMethod);
1294
                }
1295 23
                $this->setEncrypted(true);
1296
            }
1297
        }
1298
1299 26
        return $this;
1300
    }
1301
1302
    /**
1303
     * @return int
1304
     */
1305 54
    public function getEncryptionMethod()
1306
    {
1307 54
        return $this->encryptionMethod;
1308
    }
1309
1310
    /**
1311
     * Set encryption method.
1312
     *
1313
     * @param int|null $encryptionMethod
1314
     *
1315
     * @return ZipEntry
1316
     *
1317
     * @see ZipEncryptionMethod::NONE
1318
     * @see ZipEncryptionMethod::PKWARE
1319
     * @see ZipEncryptionMethod::WINZIP_AES_256
1320
     * @see ZipEncryptionMethod::WINZIP_AES_192
1321
     * @see ZipEncryptionMethod::WINZIP_AES_128
1322
     */
1323 33
    public function setEncryptionMethod($encryptionMethod)
1324
    {
1325 33
        if ($encryptionMethod === null) {
1326 1
            $encryptionMethod = ZipEncryptionMethod::NONE;
1327
        }
1328
1329 33
        $encryptionMethod = (int) $encryptionMethod;
1330 33
        ZipEncryptionMethod::checkSupport($encryptionMethod);
1331 29
        $this->encryptionMethod = $encryptionMethod;
1332
1333 29
        $this->setEncrypted($this->encryptionMethod !== ZipEncryptionMethod::NONE);
1334 29
        $this->extractVersion = self::UNKNOWN;
1335
1336 29
        return $this;
1337
    }
1338
1339
    /**
1340
     * @return int
1341
     */
1342 78
    public function getCompressionLevel()
1343
    {
1344 78
        return $this->compressionLevel;
1345
    }
1346
1347
    /**
1348
     * @param int $compressionLevel
1349
     *
1350
     * @return ZipEntry
1351
     */
1352 38
    public function setCompressionLevel($compressionLevel)
1353
    {
1354 38
        $compressionLevel = (int) $compressionLevel;
1355
1356 38
        if ($compressionLevel === self::UNKNOWN) {
1357 1
            $compressionLevel = ZipCompressionLevel::NORMAL;
1358
        }
1359
1360
        if (
1361 38
            $compressionLevel < ZipCompressionLevel::LEVEL_MIN ||
1362 38
            $compressionLevel > ZipCompressionLevel::LEVEL_MAX
1363
        ) {
1364 16
            throw new InvalidArgumentException(
1365
                'Invalid compression level. Minimum level ' .
1366 16
                ZipCompressionLevel::LEVEL_MIN . '. Maximum level ' . ZipCompressionLevel::LEVEL_MAX
1367
            );
1368
        }
1369 22
        $this->compressionLevel = $compressionLevel;
1370
1371 22
        $this->updateGbpfCompLevel();
1372
1373 22
        return $this;
1374
    }
1375
1376
    /**
1377
     * Update general purpose bit flogs.
1378
     */
1379 22
    private function updateGbpfCompLevel()
1380
    {
1381 22
        if ($this->compressionMethod === ZipCompressionMethod::DEFLATED) {
1382 22
            $bit1 = false;
1383 22
            $bit2 = false;
1384
1385 22
            switch ($this->compressionLevel) {
1386
                case ZipCompressionLevel::MAXIMUM:
1387 6
                    $bit1 = true;
1388 6
                    break;
1389
1390
                case ZipCompressionLevel::FAST:
1391 3
                    $bit2 = true;
1392 3
                    break;
1393
1394
                case ZipCompressionLevel::SUPER_FAST:
1395 5
                    $bit1 = true;
1396 5
                    $bit2 = true;
1397 5
                    break;
1398
                // default is ZipCompressionLevel::NORMAL
1399
            }
1400
1401 22
            $this->generalPurposeBitFlags |= ($bit1 ? GeneralPurposeBitFlag::COMPRESSION_FLAG1 : 0);
1402 22
            $this->generalPurposeBitFlags |= ($bit2 ? GeneralPurposeBitFlag::COMPRESSION_FLAG2 : 0);
1403
        }
1404 22
    }
1405
1406
    /**
1407
     * Sets Unix permissions in a way that is understood by Info-Zip's
1408
     * unzip command.
1409
     *
1410
     * @param int $mode mode an int value
1411
     *
1412
     * @return ZipEntry
1413
     */
1414 191
    public function setUnixMode($mode)
1415
    {
1416 191
        $mode = (int) $mode;
1417 191
        $this->setExternalAttributes(
1418
            ($mode << 16)
1419
            // MS-DOS read-only attribute
1420 191
            | (($mode & UnixStat::UNX_IWUSR) === 0 ? DosAttrs::DOS_HIDDEN : 0)
1421
            // MS-DOS directory flag
1422 191
            | ($this->isDirectory() ? DosAttrs::DOS_DIRECTORY : DosAttrs::DOS_ARCHIVE)
1423
        );
1424 191
        $this->createdOS = ZipPlatform::OS_UNIX;
1425
1426 191
        return $this;
1427
    }
1428
1429
    /**
1430
     * Unix permission.
1431
     *
1432
     * @return int the unix permissions
1433
     */
1434 51
    public function getUnixMode()
1435
    {
1436 51
        $mode = 0;
1437
1438 51
        if ($this->createdOS === ZipPlatform::OS_UNIX) {
1439 45
            $mode = ($this->externalAttributes >> 16) & 0xFFFF;
1440 7
        } elseif ($this->hasExtraField(AsiExtraField::HEADER_ID)) {
1441
            /** @var AsiExtraField $asiExtraField */
1442 1
            $asiExtraField = $this->getExtraField(AsiExtraField::HEADER_ID);
1443 1
            $mode = $asiExtraField->getMode();
1444
        }
1445
1446 51
        if ($mode > 0) {
1447 43
            return $mode;
1448
        }
1449
1450 8
        return $this->isDirectory ? 040755 : 0100644;
1451
    }
1452
1453
    /**
1454
     * Offset MUST be considered in decision about ZIP64 format - see
1455
     * description of Data Descriptor in ZIP File Format Specification.
1456
     *
1457
     * @return bool
1458
     */
1459 146
    public function isZip64ExtensionsRequired()
1460
    {
1461 146
        return $this->compressedSize > ZipConstants::ZIP64_MAGIC
1462 146
            || $this->uncompressedSize > ZipConstants::ZIP64_MAGIC;
1463
    }
1464
1465
    /**
1466
     * Returns true if this entry represents a unix symlink,
1467
     * in which case the entry's content contains the target path
1468
     * for the symlink.
1469
     *
1470
     * @return bool true if the entry represents a unix symlink,
1471
     *              false otherwise
1472
     */
1473 25
    public function isUnixSymlink()
1474
    {
1475 25
        return ($this->getUnixMode() & UnixStat::UNX_IFMT) === UnixStat::UNX_IFLNK;
1476
    }
1477
1478
    /**
1479
     * @return \DateTimeInterface
1480
     */
1481 16
    public function getMTime()
1482
    {
1483
        /** @var NtfsExtraField|null $ntfsExtra */
1484 16
        $ntfsExtra = $this->getExtraField(NtfsExtraField::HEADER_ID);
1485
1486 16
        if ($ntfsExtra !== null) {
1487 3
            return $ntfsExtra->getModifyDateTime();
1488
        }
1489
1490
        /** @var ExtendedTimestampExtraField|null $extendedExtra */
1491 15
        $extendedExtra = $this->getExtraField(ExtendedTimestampExtraField::HEADER_ID);
1492
1493 15
        if ($extendedExtra !== null && ($mtime = $extendedExtra->getModifyDateTime()) !== null) {
1494 3
            return $mtime;
1495
        }
1496
1497
        /** @var OldUnixExtraField|null $oldUnixExtra */
1498 14
        $oldUnixExtra = $this->getExtraField(OldUnixExtraField::HEADER_ID);
1499
1500 14
        if ($oldUnixExtra !== null && ($mtime = $oldUnixExtra->getModifyDateTime()) !== null) {
1501 1
            return $mtime;
1502
        }
1503
1504 14
        $timestamp = $this->getTime();
1505
1506
        try {
1507 14
            return new \DateTimeImmutable('@' . $timestamp);
1508
        } catch (\Exception $e) {
1509
            throw new RuntimeException('Error create DateTime object with timestamp ' . $timestamp, 1, $e);
1510
        }
1511
    }
1512
1513
    /**
1514
     * @return \DateTimeInterface|null
1515
     */
1516 17
    public function getATime()
1517
    {
1518
        /** @var NtfsExtraField|null $ntfsExtra */
1519 17
        $ntfsExtra = $this->getExtraField(NtfsExtraField::HEADER_ID);
1520
1521 17
        if ($ntfsExtra !== null) {
1522 3
            return $ntfsExtra->getAccessDateTime();
1523
        }
1524
1525
        /** @var ExtendedTimestampExtraField|null $extendedExtra */
1526 16
        $extendedExtra = $this->getExtraField(ExtendedTimestampExtraField::HEADER_ID);
1527
1528 16
        if ($extendedExtra !== null && ($atime = $extendedExtra->getAccessDateTime()) !== null) {
1529 3
            return $atime;
1530
        }
1531
1532
        /** @var OldUnixExtraField|null $oldUnixExtra */
1533 15
        $oldUnixExtra = $this->getExtraField(OldUnixExtraField::HEADER_ID);
1534
1535 15
        if ($oldUnixExtra !== null) {
1536 1
            return $oldUnixExtra->getAccessDateTime();
1537
        }
1538
1539 15
        return null;
1540
    }
1541
1542
    /**
1543
     * @return \DateTimeInterface|null
1544
     */
1545 7
    public function getCTime()
1546
    {
1547
        /** @var NtfsExtraField|null $ntfsExtra */
1548 7
        $ntfsExtra = $this->getExtraField(NtfsExtraField::HEADER_ID);
1549
1550 7
        if ($ntfsExtra !== null) {
1551 2
            return $ntfsExtra->getCreateDateTime();
1552
        }
1553
1554
        /** @var ExtendedTimestampExtraField|null $extendedExtra */
1555 7
        $extendedExtra = $this->getExtraField(ExtendedTimestampExtraField::HEADER_ID);
1556
1557 7
        if ($extendedExtra !== null) {
1558 2
            return $extendedExtra->getCreateDateTime();
1559
        }
1560
1561 7
        return null;
1562
    }
1563
1564 147
    public function __clone()
1565
    {
1566 147
        $this->cdExtraFields = clone $this->cdExtraFields;
1567 147
        $this->localExtraFields = clone $this->localExtraFields;
1568
1569 147
        if ($this->data !== null) {
1570 144
            $this->data = clone $this->data;
1571
        }
1572 147
    }
1573
}
1574