Issues (29)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/ZipArchive.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace iqb;
4
5
use const iqb\stream\SUBSTREAM_SCHEME;
6
use morgue\zip\CentralDirectoryHeader;
7
use morgue\zip\EndOfCentralDirectory;
8
use morgue\zip\LocalFileHeader;
9
use const morgue\zip\ZIP_COMPRESSION_METHOD_BZIP2;
10
use const morgue\zip\ZIP_COMPRESSION_METHOD_DEFLATE;
11
use const morgue\zip\ZIP_COMPRESSION_METHOD_DEFLATE64;
12
use const morgue\zip\ZIP_COMPRESSION_METHOD_IMPLODE;
13
use const morgue\zip\ZIP_COMPRESSION_METHOD_LZ77;
14
use const morgue\zip\ZIP_COMPRESSION_METHOD_LZMA;
15
use const morgue\zip\ZIP_COMPRESSION_METHOD_PKWARE_IMPLODE;
16
use const morgue\zip\ZIP_COMPRESSION_METHOD_PPMD;
17
use const morgue\zip\ZIP_COMPRESSION_METHOD_REDUCE_1;
18
use const morgue\zip\ZIP_COMPRESSION_METHOD_REDUCE_2;
19
use const morgue\zip\ZIP_COMPRESSION_METHOD_REDUCE_3;
20
use const morgue\zip\ZIP_COMPRESSION_METHOD_REDUCE_4;
21
use const morgue\zip\ZIP_COMPRESSION_METHOD_SHRINK;
22
use const morgue\zip\ZIP_COMPRESSION_METHOD_STORE;
23
use const morgue\zip\ZIP_COMPRESSION_METHOD_TERSE;
24
use const morgue\zip\ZIP_COMPRESSION_METHOD_TOKENIZE;
25
use const morgue\zip\ZIP_COMPRESSION_METHOD_WAVPACK;
26
27
/**
28
 *
29
 * @property-read int $status Status of the Zip Archive
30
 * @property-read int statusSys System status of the Zip Archive
31
 * @property-read int numFiles Number of files in the archive
32
 * @property-read string $comment Comment for the archive
33
 * @property-read string $filename File name in the file system
34
 *
35
 * @link http://php.net/manual/en/class.ziparchive.php
36
 * @link http://php.net/manual/en/zip.constants.php
37
 */
38
class ZipArchive implements \Countable
39
{
40
    // Flags for open
41
42
    /**
43
     * Create the archive if it does not exist
44
     */
45
    const CREATE = 1;
46
47
    /**
48
     * Error if archive already exists
49
     */
50
    const EXCL = 2;
51
52
    /**
53
     * Perform additional consistency checks on the archive, and error if they fail
54
     */
55
    const CHECKCONS = 4;
56
57
    /**
58
     * Always start a new archive, this mode will overwrite the file if it already exists
59
     */
60
    const OVERWRITE = 8;
61
62
    // Generic flags
63
64
    /**
65
     * Ignore case on name lookup
66
     */
67
    const FL_NOCASE = 1;
68
69
    /**
70
     * Ignore directory component
71
     */
72
    const FL_NODIR = 2;
73
74
    /**
75
     * Read compressed data
76
     */
77
    const FL_COMPRESSED = 4;
78
79
    /**
80
     * Use original data, ignoring changes.
81
     */
82
    const FL_UNCHANGED = 8;
83
84
    // Encoding flags
85
86
    /**
87
     * Guess string encoding (is default)
88
     */
89
    const FL_ENC_GUESS = 0;
90
91
    /**
92
     * Get unmodified string
93
     */
94
    const FL_ENC_RAW = 64;
95
96
    /**
97
     * Follow specification strictly
98
     */
99
    const FL_ENC_STRICT = 128;
100
101
    /**
102
     * String is UTF-8 encoded
103
     */
104
    const FL_ENC_UTF_8 = 2048;
105
106
    /**
107
     * String is CP437 encoded
108
     */
109
    const FL_ENC_CP437 = 4096;
110
111
    // Compression methods
112
113
    /**
114
     * Better of store or deflate
115
     */
116
    const CM_DEFAULT = -1;
117
118
    const CM_STORE          = ZIP_COMPRESSION_METHOD_STORE;
119
    const CM_SHRINK         = ZIP_COMPRESSION_METHOD_SHRINK;
120
    const CM_REDUCE_1       = ZIP_COMPRESSION_METHOD_REDUCE_1;
121
    const CM_REDUCE_2       = ZIP_COMPRESSION_METHOD_REDUCE_2;
122
    const CM_REDUCE_3       = ZIP_COMPRESSION_METHOD_REDUCE_3;
123
    const CM_REDUCE_4       = ZIP_COMPRESSION_METHOD_REDUCE_4;
124
    const CM_IMPLODE        = ZIP_COMPRESSION_METHOD_IMPLODE;
125
    const CM_TOKENIZE       = ZIP_COMPRESSION_METHOD_TOKENIZE;
126
    const CM_DEFLATE        = ZIP_COMPRESSION_METHOD_DEFLATE;
127
    const CM_DEFLATE64      = ZIP_COMPRESSION_METHOD_DEFLATE64;
128
    const CM_PKWARE_IMPLODE = ZIP_COMPRESSION_METHOD_PKWARE_IMPLODE;
129
    const CM_BZIP2          = ZIP_COMPRESSION_METHOD_BZIP2;
130
    const CM_LZMA           = ZIP_COMPRESSION_METHOD_LZMA;
131
    const CM_TERSE          = ZIP_COMPRESSION_METHOD_TERSE;
132
    const CM_LZ77           = ZIP_COMPRESSION_METHOD_LZ77;
133
    const CM_WAVPACK        = ZIP_COMPRESSION_METHOD_WAVPACK;
134
    const CM_PPMD           = ZIP_COMPRESSION_METHOD_PPMD;
135
136
    // Error constants
137
138
    /**
139
     * No error
140
     */
141
    const ER_OK = 0;
142
143
    /**
144
     * Multi-disk zip archives not supported
145
     */
146
    const ER_MULTIDISK = 1;
147
148
    /**
149
     * Renaming temporary file failed
150
     */
151
    const ER_RENAME = 2;
152
153
    /**
154
     * Closing zip archive failed
155
     */
156
    const ER_CLOSE = 3;
157
158
    /**
159
     * Seek error
160
     */
161
    const ER_SEEK = 4;
162
163
    /**
164
     * Read error
165
     */
166
    const ER_READ = 5;
167
168
    /**
169
     * Write error
170
     */
171
    const ER_WRITE = 6;
172
173
    /**
174
     * CRC error
175
     */
176
    const ER_CRC = 7;
177
178
    /**
179
     * Containing zip archive was closed
180
     */
181
    const ER_ZIPCLOSED = 8;
182
183
    /**
184
     * No such file
185
     */
186
    const ER_NOENT = 9;
187
188
    /**
189
     * File already exists
190
     */
191
    const ER_EXISTS = 10;
192
193
    /**
194
     * Can't open file
195
     */
196
    const ER_OPEN = 11;
197
198
    /**
199
     * Failure to create temporary file
200
     */
201
    const ER_TMPOPEN = 12;
202
203
    /**
204
     * Zlib error
205
     */
206
    const ER_ZLIB = 13;
207
208
    /**
209
     * Memory allocation failure
210
     */
211
    const ER_MEMORY = 14;
212
213
    /**
214
     * Entry has been changed
215
     */
216
    const ER_CHANGED = 15;
217
218
    /**
219
     * Compression method not supported
220
     */
221
    const ER_COMPNOTSUPP = 16;
222
223
    /**
224
     * Premature EOF
225
     */
226
    const ER_EOF = 17;
227
228
    /**
229
     * Invalid argument
230
     */
231
    const ER_INVAL = 18;
232
233
    /**
234
     * Not a zip archive
235
     */
236
    const ER_NOZIP = 19;
237
238
    /**
239
     * Internal error
240
     */
241
    const ER_INTERNAL = 20;
242
243
    /**
244
     * Zip archive inconsistent
245
     */
246
    const ER_INCONS = 21;
247
248
    /**
249
     * Can't remove file
250
     */
251
    const ER_REMOVE = 22;
252
253
    /**
254
     * Entry has been deleted
255
     */
256
    const ER_DELETED = 23;
257
258
    // Encryption
259
260
    /**
261
     * No encryption
262
     */
263
    const EM_NONE = 0;
264
265
    /**
266
     * AES 128 encryption
267
     */
268
    const EM_AES_128 = 257;
269
270
    /**
271
     * AES 192 encryption
272
     */
273
    const EM_AES_192 = 258;
274
275
    /**
276
     * AES 256 encryption
277
     */
278
    const EM_AES_256 = 259;
279
280
    // Operating system constants for external attributes
281
282
    /**
283
     * MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)
284
     */
285
    const OPSYS_DOS = 0;
286
287
    /**
288
     * Amiga
289
     */
290
    const OPSYS_AMIGA = 1;
291
292
    /**
293
     * OpenVMS
294
     */
295
    const OPSYS_OPENVMS = 2;
296
297
    /**
298
     * UNIX
299
     */
300
    const OPSYS_UNIX = 3;
301
302
    /**
303
     * VM/CMS
304
     */
305
    const OPSYS_VM_CMS = 4;
306
307
    /**
308
     * Atari ST
309
     */
310
    const OPSYS_ATARI_ST = 5;
311
312
    /**
313
     * OS/2 H.P.F.S.
314
     */
315
    const OPSYS_OS_2 = 6;
316
317
    /**
318
     * Macintosh
319
     */
320
    const OPSYS_MACINTOSH = 7;
321
322
    /**
323
     * Z-System
324
     */
325
    const OPSYS_Z_SYSTEM = 8;
326
327
    /**
328
     * CP/M
329
     */
330
    const OPSYS_CPM = 9;
331
    const OPSYS_Z_CPM = self::OPSYS_CPM;
332
333
    /**
334
     * Windows NTFS
335
     */
336
    const OPSYS_WINDOWS_NTFS = 10;
337
338
    /**
339
     * MVS (OS/390 - Z/OS)
340
     */
341
    const OPSYS_MVS = 11;
342
343
    /**
344
     * VSE
345
     */
346
    const OPSYS_VSE = 12;
347
348
    /**
349
     * Acorn Risc
350
     */
351
    const OPSYS_ACORN_RISC = 13;
352
353
    /**
354
     * VFAT
355
     */
356
    const OPSYS_VFAT = 14;
357
358
    /**
359
     * alternate MVS
360
     */
361
    const OPSYS_ALTERNATE_MVS = 15;
362
363
    /**
364
     * BeOS
365
     */
366
    const OPSYS_BEOS = 16;
367
368
    /**
369
     * Tandem
370
     */
371
    const OPSYS_TANDEM = 17;
372
373
    /**
374
     * OS/400
375
     */
376
    const OPSYS_OS_400 = 18;
377
378
    /**
379
     * OS X (Darwin)
380
     */
381
    const OPSYS_OS_X = 19;
382
383
    const OPSYS_DEFAULT = self::OPSYS_UNIX;
384
385
    /**
386
     * @var bool
387
     */
388
    private $bzip2Support = false;
389
390
    /**
391
     * @var bool
392
     */
393
    private $deflateSupport = false;
394
395
    /**
396
     * Status of the Zip Archive
397
     * @var int
398
     */
399
    private $status = 0;
400
401
    /**
402
     * System status of the Zip Archive
403
     * @var int
404
     */
405
    private $statusSys = 0;
406
407
    /**
408
     * File name in the file system
409
     * @var string
410
     */
411
    private $filename;
412
413
    /**
414
     * @var resource
415
     */
416
    private $handle;
417
418
    /**
419
     * @var CentralDirectoryHeader[]
420
     */
421
    private $originalCentralDirectory = [];
422
423
    /**
424
     * @var EndOfCentralDirectory
425
     */
426
    private $originalEndOfCentralDirectory;
427
428
    /**
429
     * @var CentralDirectoryHeader[]
430
     */
431
    private $modifiedCentralDirectory = [];
432
433
    /**
434
     * @var EndOfCentralDirectory
435
     */
436
    private $modifiedEndOfCentralDirectory;
437
438
439 2048
    public function __construct()
440
    {
441 2048
        $this->deflateSupport(true);
442 2048
        $this->bzip2Support(true);
443 2048
    }
444
445
446
    /**
447
     * Enable or disable deflate compression support.
448
     * Can be called without parameter to check status.
449
     *
450
     * @param bool $enableSupport Enable or disable deflate compression support
451
     * @return bool The deflate compression support status valid from now on
452
     */
453 2048
    public function deflateSupport(bool $enableSupport = null)
454
    {
455 2048
        if ($enableSupport !== null) {
456 2048
            $this->deflateSupport = ($enableSupport && \in_array('zlib.*', \stream_get_filters()));
457
        }
458
459 2048
        return $this->deflateSupport;
460
    }
461
462
463
    /**
464
     * Enable or disable BZip2 compression support.
465
     * Can be called without parameter to check status.
466
     *
467
     * @param bool $enableSupport Enable or disable BZip2 compression support
468
     * @return bool The BZip2 support status valid from now on
469
     */
470 2048
    public function bzip2Support(bool $enableSupport = null)
471
    {
472 2048
        if ($enableSupport !== null) {
473 2048
            $this->bzip2Support = ($enableSupport && \in_array('bzip2.*', \stream_get_filters()));
474
        }
475
476 2048
        return $this->bzip2Support;
477
    }
478
479
480
    /**
481
     * Emulate read-only access to class variables
482
     */
483 2045
    public function __get(string $name)
484
    {
485 2045
        if ($name === 'status') {
486 2044
            return $this->status;
487 2045
        } elseif ($name === 'statusSys') {
488 2044
            return $this->statusSys;
489 2045
        } elseif ($name === 'filename') {
490 2043
            return $this->filename;
491 2045
        } elseif ($name === 'numFiles') {
492 2044
            return $this->count();
493 2045
        } elseif ($name === 'comment') {
494 2045
            return $this->getArchiveComment();
495
        }
496
    }
497
498
499
    /**
500
     * Emulate writes that vanish into the air for class variables
501
     */
502 2048
    public function __set(string $name, $value)
503
    {
504 2048
    }
505
506
507
    /**
508
     * @implements \Countable
509
     * @return int
510
     */
511 2044
    public function count()
512
    {
513 2044
        return \count($this->modifiedCentralDirectory);
514
    }
515
516
517 2048
    public function open(string $filename)
518
    {
519 2048
        $this->filename = \realpath($filename);
520 2048
        $this->handle = \fopen($this->filename, 'r+');
521
522
        // Find end of central directory
523 2048
        $this->originalEndOfCentralDirectory = $this->modifiedEndOfCentralDirectory = $this->findEndOfCentralDirectory();
524
525
        // Read central directory
526 2048
        if (\fseek($this->handle, $this->originalEndOfCentralDirectory->getOffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber()) === -1) {
527
            throw new \RuntimeException("Unable to read Central Directory");
528
        }
529
530 2048
        $centralDirectoryData = \fread($this->handle, $this->originalEndOfCentralDirectory->getSizeOfTheCentralDirectory());
531 2048
        $offset = 0;
532
533 2048
        for ($i=0; $i<$this->originalEndOfCentralDirectory->getTotalNumberOfEntriesInTheCentralDirectory(); $i++) {
534 2048
            $centralDirectoryEntry = CentralDirectoryHeader::parse($centralDirectoryData, $offset);
535 2048
            $offset += CentralDirectoryHeader::MIN_LENGTH;
536
537 2048
            if ($centralDirectoryEntry->getVariableLength() > 0) {
538 2048
                $offset += $centralDirectoryEntry->parseAdditionalData($centralDirectoryData, $offset);
539
            }
540
541 2048
            $this->originalCentralDirectory[] = $centralDirectoryEntry;
542
        }
543
544 2048
        $this->modifiedCentralDirectory = $this->originalCentralDirectory;
545 2048
        $this->numFiles = \count($this->originalCentralDirectory);
546 2048
    }
547
548
549
    /**
550
     * @return EndOfCentralDirectory|null
551
     */
552 2048
    private function findEndOfCentralDirectory()
553
    {
554 2048
        $signature = \pack('N', EndOfCentralDirectory::SIGNATURE);
555
556 2048
        for ($offset = EndOfCentralDirectory::MIN_LENGTH; $offset <= EndOfCentralDirectory::MAX_LENGTH; $offset++) {
557 2048
            if (\fseek($this->handle, -$offset, \SEEK_END) === -1) {
558
                throw new \RuntimeException("Can not find EndOfDirectoryDirectory record");
559
            }
560
561 2048
            $chunk = \fread($this->handle, EndOfCentralDirectory::MIN_LENGTH);
562 2048
            if (\substr($chunk, 0, \strlen($signature)) !== $signature) {
563 2042
                continue;
564
            }
565
566 2048
            $endOfCentralDirectory = EndOfCentralDirectory::parse($chunk);
567 2048
            if ($endOfCentralDirectory->getVariableLength() > 0) {
568 2042
                $additionalData = \fread($this->handle, $endOfCentralDirectory->getVariableLength());
569 2042
                $endOfCentralDirectory->parseAdditionalData($additionalData);
570
            }
571
572 2048
            return $endOfCentralDirectory;
573
        }
574
575
        return null;
576
    }
577
578
579
    /**
580
     * Returns the Zip archive comment
581
     *
582
     * @param int $flags ZipArchive::FL_UNCHANGED or 0
583
     * @return string|bool
584
     *
585
     * @link http://php.net/manual/en/ziparchive.getarchivecomment.php
586
     */
587 2045
    final public function getArchiveComment(int $flags = null)
588
    {
589 2045
        $validFlags = (is_null($flags) ? 0 : $flags & self::FL_UNCHANGED);
590
591 2045
        if ($validFlags & self::FL_UNCHANGED) {
592 1
            return $this->originalEndOfCentralDirectory->getZipFileComment();
593
        }
594
595
        else {
596 2045
            return $this->modifiedEndOfCentralDirectory->getZipFileComment();
597
        }
598
    }
599
600
601
    /**
602
     * Returns the comment of an entry using the entry index.
603
     *
604
     * @param int $index Index of the entry
605
     * @param int $flags ZipArchive::FL_UNCHANGED or 0
606
     * @return string|false
607
     *
608
     * @link http://php.net/manual/en/ziparchive.getcommentindex.php
609
     */
610 73
    final public function getCommentIndex(int $index, int $flags = null)
611
    {
612 73
        $validFlags = (is_null($flags) ? 0 : $flags & self::FL_UNCHANGED);
613 73
        $directory = ($validFlags & self::FL_UNCHANGED ? $this->originalCentralDirectory : $this->modifiedCentralDirectory);
614
615 73
        if (isset($directory[$index])) {
616 72
            $this->status = self::ER_OK;
617 72
            return $directory[$index]->getFileComment();
618
        } else {
619 1
            $this->status = self::ER_INVAL;
620 1
            return false;
621
        }
622
    }
623
624
625
    /**
626
     * Returns the comment of an entry using the entry name
627
     *
628
     * @param string $name Name of the entry
629
     * @param int $flags Any combination of ZipArchive::FL_UNCHANGED
630
     * @return string|false
631
     *
632
     * @link http://php.net/manual/en/ziparchive.getcommentname.php
633
     */
634 211
    final public function getCommentName(string $name, int $flags = null)
635
    {
636 211
        $validFlags = (is_null($flags) ? 0 : $flags & (self::FL_UNCHANGED));
637
638 211
        if (($index = $this->locateName($name, $validFlags)) !== false) {
639
            // Hack to align behaviour with \ZipArchive
640 56
            if ($flags & self::FL_NODIR) {
641 32
                $this->status = self::ER_NOENT;
642
            }
643 56
            return $this->getCommentIndex($index, $validFlags);
644
        } else {
645 155
            return false;
646
        }
647
    }
648
649
650
    /**
651
     * Retrieve the external attributes of an entry defined by its index
652
     *
653
     * @param int $index Index of the entry.
654
     * @param int $opsys On success, receive the operating system code defined by one of the ZipArchive::OPSYS_ constants.
655
     * @param int $attr On success, receive the external attributes. Value depends on operating system.
656
     * @param int $flags ZipArchive::FL_UNCHANGED or 0
657
     * @return bool
658
     *
659
     * @link http://php.net/manual/en/ziparchive.getexternalattributesindex.php
660
     */
661 54
    final public function getExternalAttributesIndex(int $index, &$opsys, &$attr, int $flags = null)
662
    {
663 54
        $validFlags = (is_null($flags) ? 0 : $flags & self::FL_UNCHANGED);
664 54
        $directory = ($validFlags & self::FL_UNCHANGED ? $this->originalCentralDirectory : $this->modifiedCentralDirectory);
665
666 54
        if (!isset($directory[$index])) {
667
            return false;
668
        }
669
670 54
        $opsys = $directory[$index]->getEncodingHost();
671 54
        $attr = $directory[$index]->getExternalFileAttributes();
672
673 54
        return true;
674
    }
675
676
677
    /**
678
     * Retrieve the external attributes of an entry defined by its name
679
     *
680
     * @param string $name Name of the entry.
681
     * @param int $opsys On success, receive the operating system code defined by one of the ZipArchive::OPSYS_ constants.
682
     * @param int $attr On success, receive the external attributes. Value depends on operating system.
683
     * @param int $flags ZipArchive::FL_UNCHANGED or 0
684
     * @return bool
685
     *
686
     * @link http://php.net/manual/en/ziparchive.getexternalattributesname.php
687
     */
688 168
    final public function getExternalAttributesName(string $name, &$opsys, &$attr, int $flags = null)
689
    {
690 168
        $validFlags = (is_null($flags) ? 0 : $flags & (self::FL_UNCHANGED));
691
692 168
        if (($index = $this->locateName($name, $validFlags)) !== false) {
693 42
            return $this->getExternalAttributesIndex($index, $opsys, $attr, $flags);
694
        } else {
695 126
            return false;
696
        }
697
    }
698
699
700
    /**
701
     * Returns the entry contents using its index
702
     *
703
     * @param int $index Index of the entry
704
     * @param int $length The length to be read from the entry. If 0, then the entire entry is read.
705
     * @param int $flags Any combination of ZipArchive::FL_COMPRESSED|ZipArchive::FL_UNCHANGED
706
     * @return string|false
707
     *
708
     * @link http://php.net/manual/en/ziparchive.getfromindex.php
709
     */
710 486
    final public function getFromIndex(int $index, int $length = null, int $flags = null)
711
    {
712 486
        $validFlags = (is_null($flags) ? 0 : $flags & (self::FL_COMPRESSED|self::FL_UNCHANGED));
713
714 486
        if (($stream = $this->getStreamIndex($index, $validFlags)) === false) {
715 1
            return false;
716
        }
717
718 485
        $length = (is_null($length) ? 0 : $length);
719 485
        $string = '';
720
        do {
721 485
            $chunkSize = ($length ? $length - \strlen($string) : 8192);
722 485
            if (($chunk = \fread($stream, $chunkSize)) !== false) {
723 485
                $string .= $chunk;
724
            } else {
725
                break;
726
            }
727 485
        } while (!\feof($stream) && ($length === 0 || \strlen($string) < $length));
728
729 485
        return $string;
730
    }
731
732
733
    /**
734
     * Returns the entry contents using its name
735
     *
736
     * @param string $name Name of the entry
737
     * @param int $length The length to be read from the entry. If 0, then the entire entry is read.
738
     * @param int $flags Any combination of ZipArchive::FL_COMPRESSED|ZipArchive::FL_NOCASE|ZipArchive::FL_UNCHANGED
739
     * @return bool
740
     *
741
     * @link http://php.net/manual/en/ziparchive.getfromname.php
742
     */
743 1320 View Code Duplication
    final public function getFromName(string $name, int $length = null, int $flags = null)
0 ignored issues
show
This method seems to be duplicated in 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...
744
    {
745 1320
        $validFlags = (is_null($flags) ? 0 : $flags & (self::FL_COMPRESSED|self::FL_NOCASE|self::FL_NODIR|self::FL_UNCHANGED));
746
747 1320
        if (($index = $this->locateName($name, $validFlags)) === false) {
748 960
            return false;
749
        }
750
751 360
        return $this->getFromIndex($index, $length, $validFlags);
752
    }
753
754
755
    /**
756
     * @return string
757
     */
758 2031
    final public function getStatusString()
759
    {
760 2031
        switch ($this->status) {
761 2031
            case self::ER_OK:
762 662
                return "No error";
763
764 1369
            case self::ER_NOENT:
765 1367
                return "No such file";
766
767 2
            case self::ER_COMPNOTSUPP:
768 1
                return "Compression method not supported";
769
770 1
            case self::ER_INVAL:
771 1
                return "Invalid argument";
772
773
            default:
774
                return "";
775
        }
776
    }
777
778
779
    /**
780
     * Get a file handler to the entry defined by its name (read only)
781
     *
782
     * @param string $name The name of the entry to use.
783
     * @return resource|false
784
     *
785
     * @link http://php.net/manual/en/ziparchive.getstream.php
786
     */
787 6
    final public function getStream(string $name)
788
    {
789 6
        return $this->getStreamName($name);
790
    }
791
792
793
    /**
794
     * Get a file handle to the entry defined by its index (read only)
795
     *
796
     * @param int $index Index of the entry
797
     * @param int $flags Any combination of ZipArchive::FL_COMPRESSED|ZipArchive::FL_UNCHANGED
798
     * @return resource|false
799
     */
800 486
    final public function getStreamIndex(int $index, int $flags = null)
801
    {
802 486
        $validFlags = (is_null($flags) ? 0 : $flags & (self::FL_COMPRESSED|self::FL_UNCHANGED));
803 486
        $directory = ($validFlags & self::FL_UNCHANGED ? $this->originalCentralDirectory : $this->modifiedCentralDirectory);
804
805 486
        $this->status = self::ER_OK;
806
807 486
        if (!isset($directory[$index])) {
808
            return false;
809
        }
810 486
        $entry = $directory[$index];
811
812 486
        if ($entry->getCompressedSize() === 0) {
813 165
            return \fopen('php://memory', 'r');
814
        }
815
816 321
        \fseek($this->handle, $entry->getRelativeOffsetOfLocalHeader());
817 321
        $localHeader = LocalFileHeader::parse(\fread($this->handle, LocalFileHeader::MIN_LENGTH));
818 321
        if ($localHeader->getVariableLength() > 0) {
819 321
            $localHeader->parseAdditionalData(\fread($this->handle, $localHeader->getVariableLength()));
820
        }
821
822 321
        $offset = \ftell($this->handle);
823 321
        $length = $entry->getCompressedSize();
824
825 321
        if (($handle = \fopen(SUBSTREAM_SCHEME . '://' . $offset . ':' . $length . '/' . (int)$this->handle, 'r')) === false) {
826
            return false;
827
        }
828
829 321
        if (($entry->getCompressionMethod() === self::CM_STORE) || ($validFlags & self::FL_COMPRESSED)) {
830 137
            return $handle;
831
        }
832
833 184
        elseif ($this->deflateSupport && ($entry->getCompressionMethod() === self::CM_DEFLATE)) {
834 182
            \stream_filter_append($handle, 'zlib.inflate', \STREAM_FILTER_READ);
835 182
            return $handle;
836
        }
837
838 2
        elseif ($this->bzip2Support && ($entry->getCompressionMethod() === self::CM_BZIP2)) {
839 1
            \stream_filter_append($handle, 'bzip2.decompress', \STREAM_FILTER_READ);
840 1
            return $handle;
841
        }
842
843
        else {
844 1
            $this->status = self::ER_COMPNOTSUPP;
845 1
            return false;
846
        }
847
    }
848
849
850
    /**
851
     * Get a file handler to the entry defined by its name (read only)
852
     *
853
     * @param string $name The name of the entry to use.
854
     * @param int|null $flags Any combination of ZipArchive::FL_COMPRESSED|ZipArchive::FL_NOCASE|ZipArchive::FL_NODIR|ZipArchive::FL_UNCHANGED
855
     * @return resource|false
856
     */
857 6 View Code Duplication
    public function getStreamName(string $name, int $flags = null)
0 ignored issues
show
This method seems to be duplicated in 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...
858
    {
859 6
        $validFlags = (is_null($flags) ? 0 : $flags & (self::FL_COMPRESSED|self::FL_NOCASE|self::FL_NODIR|self::FL_UNCHANGED));
860
861 6
        if (($index = $this->locateName($name, $validFlags)) === false) {
862 6
            return false;
863
        }
864
865
        return $this->getStreamIndex($index, $validFlags);
866
    }
867
868
869
    /**
870
     * Returns the index of the entry in the archive
871
     *
872
     * @param string $name The name of the entry to look up
873
     * @param int $flags Any combination of ZipArchive::FL_NOCASE|ZipArchive::FL_NODIR
874
     * @return int|false
875
     *
876
     * @link http://php.net/manual/en/ziparchive.locatename.php
877
     */
878 1879
    final public function locateName(string $name, int $flags = 0)
879
    {
880 1879
        $validFlags = (is_null($flags) ? 0 : $flags & (self::FL_NOCASE|self::FL_NODIR));
881 1879
        $this->status = self::ER_OK;
882
883 1879
        $ignoreCase = (($validFlags & self::FL_NOCASE) !== 0);
884 1879
        $ignoreDir = (($validFlags & self::FL_NODIR) !== 0);
885
886 1879
        $name = ($ignoreCase ? \strtolower($name) : $name);
887
888 1879
        foreach ($this->originalCentralDirectory as $possibleIndex => $possibleEntry) {
889 1879
            if ($ignoreDir && $possibleEntry->isDirectory()) {
890 822
                continue;
891
            }
892
893 1879
            $entryName = $possibleEntry->getFileName();
894 1879
            $entryName = ($ignoreCase ? \strtolower($entryName) : $entryName);
895 1879
            $entryName = ($ignoreDir ? \basename($entryName) : $entryName);
896
897 1879
            if ($name === $entryName) {
898 1879
                return $possibleIndex;
899
            }
900
        }
901
902 1379
        $this->status = self::ER_NOENT;
903 1379
        return false;
904
    }
905
906
907
    /**
908
     * Get the details of an entry defined by its index
909
     *
910
     * @param int $index Index of the entry
911
     * @param int $flags ZipArchive::FL_UNCHANGED or 0
912
     * @return array|false
913
     *
914
     * @link http://php.net/manual/en/ziparchive.statindex.php
915
     */
916 54
    final public function statIndex(int $index, int $flags = null)
917
    {
918 54
        $validFlags = (is_null($flags) ? 0 : $flags & (self::FL_UNCHANGED));
0 ignored issues
show
$validFlags is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
919
920 54
        if (!isset($this->originalCentralDirectory[$index])) {
921
            return false;
922
        }
923
924
        /* @var $entry CentralDirectoryHeader */
925 54
        $entry = $this->originalCentralDirectory[$index];
926
927
        return [
928 54
            'name' => $entry->getFileName(),
929 54
            'index' => $index,
930 54
            'crc' => $entry->getCrc32(),
931 54
            'size' => $entry->getUncompressedSize(),
932 54
            'mtime' => $entry->getLastModification()->getTimestamp(),
933 54
            'comp_size' => $entry->getCompressedSize(),
934 54
            'comp_method' => $entry->getCompressionMethod(),
935 54
            'encryption_method' => 0,
936
        ];
937
    }
938
939
940
    /**
941
     * Get the details of an entry defined by its name
942
     *
943
     * @param string $name Name of the entry
944
     * @param int $flags Any combination of ZipArchive::FL_NOCASE|ZipArchive::FL_NODIR|ZipArchive::FL_UNCHANGED
945
     * @return array|false
946
     *
947
     * @link http://php.net/manual/en/ziparchive.statname.php
948
     */
949 168
    final public function statName(string $name, int $flags = 0)
950
    {
951 168
        $validFlags = (is_null($flags) ? 0 : $flags & (self::FL_NOCASE|self::FL_NODIR|self::FL_UNCHANGED));
952
953 168
        if (($index = $this->locateName($name, $validFlags)) !== false) {
954 42
            return $this->statIndex($index, $validFlags);
955
        }
956
957 126
        return false;
958
    }
959
}
960