getOffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber()   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
namespace morgue\zip;
4
5
final class EndOfCentralDirectory
6
{
7
    const SIGNATURE = 0x504b0506;
8
9
    /// Minimum length of this entry if zip file comment is empty
10
    const MIN_LENGTH = 22;
11
12
    /// Maximum length of this entry if zip file comment has the maximum length
13
    const MAX_LENGTH = self::MIN_LENGTH + self::ZIP_FILE_COMMENT_MAX_LENGTH;
14
15
    /// The zip file comment can not be longer than this (the length field has only 2 bytes)
16
    const ZIP_FILE_COMMENT_MAX_LENGTH = (255 * 255) - 1;
17
18
    /**
19
     * @var int
20
     */
21
    private $numberOfThisDisk;
22
23
    /**
24
     * @var int
25
     */
26
    private $numberOfTheDiskWithTheStartOfTheCentralDirectory;
27
28
    /**
29
     * @var int
30
     */
31
    private $totalNumberOfEntriesInTheCentralDirectoryOnThisDisk;
32
33
    /**
34
     * @var int
35
     */
36
    private $totalNumberOfEntriesInTheCentralDirectory;
37
38
    /**
39
     * @var int
40
     */
41
    private $sizeOfTheCentralDirectory;
42
43
    /**
44
     * @var int
45
     */
46
    private $offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
47
48
    /**
49
     * @var int
50
     */
51
    private $zipFileCommentLength;
52
53
    /**
54
     * @var string
55
     */
56
    private $zipFileComment = "";
57
58
    /**
59
     * @var bool
60
     */
61
    private $requireAdditionalData = false;
62
63 2049
    public function __construct(
64
        int $numberOfThisDisk,
65
        int $numberOfTheDiskWithTheStartOfTheCentralDirectory,
66
        int $totalNumberOfEntriesInTheCentralDirectoryOnThisDisk,
67
        int $totalNumberOfEntriesInTheCentralDirectory,
68
        int $sizeOfTheCentralDirectory,
69
        int $offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber,
70
        string $zipFileComment = null
71
    ) {
72 2049
        $this->numberOfThisDisk = $numberOfThisDisk;
73 2049
        $this->numberOfTheDiskWithTheStartOfTheCentralDirectory = $numberOfTheDiskWithTheStartOfTheCentralDirectory;
74 2049
        $this->totalNumberOfEntriesInTheCentralDirectoryOnThisDisk = $totalNumberOfEntriesInTheCentralDirectoryOnThisDisk;
75 2049
        $this->totalNumberOfEntriesInTheCentralDirectory = $totalNumberOfEntriesInTheCentralDirectory;
76 2049
        $this->sizeOfTheCentralDirectory = $sizeOfTheCentralDirectory;
77 2049
        $this->offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = $offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
78
79 2049
        if ($zipFileComment !== null) {
80
            $this->zipFileComment = $zipFileComment;
81
            $this->zipFileCommentLength = \strlen($this->zipFileComment);
82
        }
83 2049
    }
84
85
    /**
86
     * Parse the end of central directory from a binary string.
87
     * Use getVariableLength() to get the required number of bytes to execute parseAdditionalData().
88
     *
89
     * @param string $input
90
     * @param int $offset Start at this position inside the string
91
     * @return static
92
     */
93 2048
    public static function parse(string $input, int $offset = 0)
94
    {
95 2048
        if (\strlen($input) < ($offset+self::MIN_LENGTH)) {
96
            throw new \InvalidArgumentException("Not enough data to parse end of central directory!");
97
        }
98
99 2048
        $parsed = \unpack(
100
            'Nsignature'
101
            . '/vnumberOfThisDisk'
102
            . '/vnumberOfTheDiskWithTheStartOfTheCentralDirectory'
103
            . '/vtotalNumberOfEntriesInTheCentralDirectoryOnThisDisk'
104
            . '/vtotalNumberOfEntriesInTheCentralDirectory'
105
            . '/VsizeOfTheCentralDirectory'
106
            . '/VoffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber'
107 2048
            . '/vzipFileCommentLength',
108 2048
            ($offset ? \substr($input, $offset) : $input)
109
        );
110 2048
        if ($parsed['signature'] !== self::SIGNATURE) {
111
            throw new \InvalidArgumentException("Invalid signature for end of central directory!");
112
        }
113
114 2048
        $endOfCentralDirectory = new static(
115 2048
            $parsed['numberOfThisDisk'],
116 2048
            $parsed['numberOfTheDiskWithTheStartOfTheCentralDirectory'],
117 2048
            $parsed['totalNumberOfEntriesInTheCentralDirectoryOnThisDisk'],
118 2048
            $parsed['totalNumberOfEntriesInTheCentralDirectory'],
119 2048
            $parsed['sizeOfTheCentralDirectory'],
120 2048
            $parsed['offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber']
121
        );
122 2048
        $endOfCentralDirectory->zipFileCommentLength = $parsed['zipFileCommentLength'];
123 2048
        $endOfCentralDirectory->requireAdditionalData = ($endOfCentralDirectory->zipFileCommentLength > 0);
124
125 2048
        return $endOfCentralDirectory;
126
    }
127
128
    /**
129
     * After a new object has been created by parse(), this method must be called to initialize the zip file comment entry which has variable field length.
130
     * The required number of bytes can be obtained by getVariableLength()
131
     *
132
     * @param string $input
133
     * @param int $offset
134
     * @return int Consumed bytes, equals getVariableLength()
135
     */
136 2042
    public function parseAdditionalData(string $input, int $offset = 0) : int
137
    {
138 2042
        if (!$this->requireAdditionalData) {
139
            throw new \BadMethodCallException("No additional data required!");
140
        }
141
142 2042
        if (\strlen($input) < ($offset + $this->zipFileCommentLength)) {
143
            throw new \InvalidArgumentException("Not enough input to parse additional data!");
144
        }
145
146 2042
        $this->zipFileComment = \substr($input, $offset, $this->zipFileCommentLength);
147 2042
        $this->requireAdditionalData = false;
148
149 2042
        return $this->zipFileCommentLength;
150
    }
151
152
    /**
153
     * Create the binary on disk representation
154
     *
155
     * @return string
156
     */
157
    public function marshal() : string
158
    {
159
        return \pack(
160
                'NvvvvVVv',
161
                self::SIGNATURE,
162
                $this->numberOfThisDisk,
163
                $this->numberOfTheDiskWithTheStartOfTheCentralDirectory,
164
                $this->totalNumberOfEntriesInTheCentralDirectoryOnThisDisk,
165
                $this->totalNumberOfEntriesInTheCentralDirectory,
166
                $this->sizeOfTheCentralDirectory,
167
                $this->offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber,
168
                \strlen($this->zipFileComment)
169
            )
170
            . $this->zipFileComment
171
            ;
172
    }
173
174
    /**
175
     * The number of bytes the fields with variable length require.
176
     *
177
     * @return int
178
     */
179 2048
    public function getVariableLength(): int
180
    {
181 2048
        return $this->zipFileCommentLength;
182
    }
183
184
    /**
185
     * @return int
186
     */
187 1
    public function getNumberOfThisDisk(): int
188
    {
189 1
        return $this->numberOfThisDisk;
190
    }
191
192
    /**
193
     * @param int $numberOfThisDisk
194
     * @return EndOfCentralDirectory
195
     */
196 1
    public function setNumberOfThisDisk(int $numberOfThisDisk): EndOfCentralDirectory
197
    {
198 1
        $obj = clone $this;
199 1
        $obj->numberOfThisDisk = $numberOfThisDisk;
200 1
        return $obj;
201
    }
202
203
    /**
204
     * @return int
205
     */
206 1
    public function getNumberOfTheDiskWithTheStartOfTheCentralDirectory(): int
207
    {
208 1
        return $this->numberOfTheDiskWithTheStartOfTheCentralDirectory;
209
    }
210
211
    /**
212
     * @param int $numberOfTheDiskWithTheStartOfTheCentralDirectory
213
     * @return EndOfCentralDirectory
214
     */
215 1
    public function setNumberOfTheDiskWithTheStartOfTheCentralDirectory(int $numberOfTheDiskWithTheStartOfTheCentralDirectory): EndOfCentralDirectory
216
    {
217 1
        $obj = clone $this;
218 1
        $obj->numberOfTheDiskWithTheStartOfTheCentralDirectory = $numberOfTheDiskWithTheStartOfTheCentralDirectory;
219 1
        return $obj;
220
    }
221
222
    /**
223
     * @return int
224
     */
225 1
    public function getTotalNumberOfEntriesInTheCentralDirectoryOnThisDisk(): int
226
    {
227 1
        return $this->totalNumberOfEntriesInTheCentralDirectoryOnThisDisk;
228
    }
229
230
    /**
231
     * @param int $totalNumberOfEntriesInTheCentralDirectoryOnThisDisk
232
     * @return EndOfCentralDirectory
233
     */
234 1
    public function setTotalNumberOfEntriesInTheCentralDirectoryOnThisDisk(int $totalNumberOfEntriesInTheCentralDirectoryOnThisDisk): EndOfCentralDirectory
235
    {
236 1
        $obj = clone $this;
237 1
        $obj->totalNumberOfEntriesInTheCentralDirectoryOnThisDisk = $totalNumberOfEntriesInTheCentralDirectoryOnThisDisk;
238 1
        return $obj;
239
    }
240
241
    /**
242
     * @return int
243
     */
244 2049
    public function getTotalNumberOfEntriesInTheCentralDirectory(): int
245
    {
246 2049
        return $this->totalNumberOfEntriesInTheCentralDirectory;
247
    }
248
249
    /**
250
     * @param int $totalNumberOfEntriesInTheCentralDirectory
251
     * @return EndOfCentralDirectory
252
     */
253 1
    public function setTotalNumberOfEntriesInTheCentralDirectory(int $totalNumberOfEntriesInTheCentralDirectory): EndOfCentralDirectory
254
    {
255 1
        $obj = clone $this;
256 1
        $obj->totalNumberOfEntriesInTheCentralDirectory = $totalNumberOfEntriesInTheCentralDirectory;
257 1
        return $obj;
258
    }
259
260
    /**
261
     * @return int
262
     */
263 2049
    public function getSizeOfTheCentralDirectory(): int
264
    {
265 2049
        return $this->sizeOfTheCentralDirectory;
266
    }
267
268
    /**
269
     * @param int $sizeOfTheCentralDirectory
270
     * @return EndOfCentralDirectory
271
     */
272 1
    public function setSizeOfTheCentralDirectory(int $sizeOfTheCentralDirectory): EndOfCentralDirectory
273
    {
274 1
        $obj = clone $this;
275 1
        $obj->sizeOfTheCentralDirectory = $sizeOfTheCentralDirectory;
276 1
        return $obj;
277
    }
278
279
    /**
280
     * @return int
281
     */
282 2049
    public function getOffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber(): int
283
    {
284 2049
        return $this->offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
285
    }
286
287
    /**
288
     * @param int $offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber
289
     * @return EndOfCentralDirectory
290
     */
291 1
    public function setOffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber(int $offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber): EndOfCentralDirectory
292
    {
293 1
        $obj = clone $this;
294 1
        $obj->offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = $offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
295 1
        return $obj;
296
    }
297
298
    /**
299
     * @return int
300
     */
301 1
    public function getZipFileCommentLength(): int
302
    {
303 1
        return $this->zipFileCommentLength;
304
    }
305
306
    /**
307
     * @return string
308
     */
309 2046
    public function getZipFileComment(): string
310
    {
311 2046
        return $this->zipFileComment;
312
    }
313
314
    /**
315
     * @param string $zipFileComment
316
     * @return EndOfCentralDirectory
317
     */
318 1
    public function setZipFileComment(string $zipFileComment): EndOfCentralDirectory
319
    {
320 1
        $obj = clone $this;
321 1
        $obj->zipFileComment = $zipFileComment;
322 1
        $obj->zipFileCommentLength = \strlen($zipFileComment);
323 1
        return $obj;
324
    }
325
}
326