Passed
Push — master ( 25d5dd...d305ab )
by Alexey
08:29 queued 11s
created

NtfsExtraField::setCreateNtfsTime()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
3
namespace PhpZip\Model\Extra\Fields;
4
5
use PhpZip\Exception\InvalidArgumentException;
6
use PhpZip\Exception\ZipException;
7
use PhpZip\Model\Extra\ZipExtraField;
8
use PhpZip\Model\ZipEntry;
9
use PhpZip\Util\PackUtil;
10
11
/**
12
 * NTFS Extra Field.
13
 *
14
 * @see     https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
15
 *
16
 * @license MIT
17
 */
18
class NtfsExtraField implements ZipExtraField
19
{
20
    /** @var int Header id */
21
    const HEADER_ID = 0x000a;
22
23
    /** @var int Tag ID */
24
    const TIME_ATTR_TAG = 0x0001;
25
26
    /** @var int Attribute size */
27
    const TIME_ATTR_SIZE = 24; // 3 * 8
28
29
    /**
30
     * @var int A file time is a 64-bit value that represents the number of
31
     *          100-nanosecond intervals that have elapsed since 12:00
32
     *          A.M. January 1, 1601 Coordinated Universal Time (UTC).
33
     *          this is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
34
     */
35
    const EPOCH_OFFSET = -116444736000000000;
36
37
    /** @var int Modify ntfs time */
38
    private $modifyNtfsTime;
39
40
    /** @var int Access ntfs time */
41
    private $accessNtfsTime;
42
43
    /** @var int Create ntfs time */
44
    private $createNtfsTime;
45
46
    /**
47
     * @param int $modifyNtfsTime
48
     * @param int $accessNtfsTime
49
     * @param int $createNtfsTime
50 2
     */
51
    public function __construct($modifyNtfsTime, $accessNtfsTime, $createNtfsTime)
52 2
    {
53 2
        $this->modifyNtfsTime = (int) $modifyNtfsTime;
54 2
        $this->accessNtfsTime = (int) $accessNtfsTime;
55 2
        $this->createNtfsTime = (int) $createNtfsTime;
56
    }
57
58
    /**
59
     * @param \DateTimeInterface $modifyDateTime
60
     * @param \DateTimeInterface $accessDateTime
61
     * @param \DateTimeInterface $createNtfsTime
62
     *
63
     * @return NtfsExtraField
64
     */
65
    public static function create(
66
        \DateTimeInterface $modifyDateTime,
67
        \DateTimeInterface $accessDateTime,
68
        \DateTimeInterface $createNtfsTime
69
    ) {
70
        return new self(
71
            self::dateTimeToNtfsTime($modifyDateTime),
72
            self::dateTimeToNtfsTime($accessDateTime),
73
            self::dateTimeToNtfsTime($createNtfsTime)
74
        );
75
    }
76
77
    /**
78
     * Returns the Header ID (type) of this Extra Field.
79
     * The Header ID is an unsigned short integer (two bytes)
80 4
     * which must be constant during the life cycle of this object.
81
     *
82 4
     * @return int
83
     */
84
    public function getHeaderId()
85
    {
86
        return self::HEADER_ID;
87
    }
88
89
    /**
90
     * Populate data from this array as if it was in local file data.
91
     *
92
     * @param string        $buffer the buffer to read data from
93 1
     * @param ZipEntry|null $entry
94
     *
95 1
     * @throws ZipException
96
     *
97 1
     * @return NtfsExtraField
98 1
     */
99 1
    public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
100
    {
101 1
        if (\PHP_INT_SIZE === 4) {
102 1
            throw new ZipException('not supported for php-32bit');
103
        }
104 1
105
        $buffer = substr($buffer, 4);
106 1
107 1
        $modifyTime = 0;
108 1
        $accessTime = 0;
109
        $createTime = 0;
110 1
111
        while ($buffer || $buffer !== '') {
112
            $unpack = unpack('vtag/vsizeAttr', $buffer);
113
114
            if ($unpack['tag'] === self::TIME_ATTR_TAG && $unpack['sizeAttr'] === self::TIME_ATTR_SIZE) {
115 1
                // refactoring will be needed when php 5.5 support ends
116
                $modifyTime = PackUtil::unpackLongLE(substr($buffer, 4, 8));
117
                $accessTime = PackUtil::unpackLongLE(substr($buffer, 12, 8));
118
                $createTime = PackUtil::unpackLongLE(substr($buffer, 20, 8));
119
120
                break;
121
            }
122
            $buffer = substr($buffer, 4 + $unpack['sizeAttr']);
123
        }
124
125
        return new self($modifyTime, $accessTime, $createTime);
126 1
    }
127
128 1
    /**
129
     * Populate data from this array as if it was in central directory data.
130
     *
131
     * @param string        $buffer the buffer to read data from
132
     * @param ZipEntry|null $entry
133
     *
134
     * @throws ZipException
135
     *
136
     * @return NtfsExtraField
137
     */
138
    public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
139
    {
140
        return self::unpackLocalFileData($buffer, $entry);
141
    }
142
143
    /**
144
     * The actual data to put into local file data - without Header-ID
145
     * or length specifier.
146
     *
147
     * @return string the data
148
     */
149
    public function packLocalFileData()
150
    {
151
        $data = pack('Vvv', 0, self::TIME_ATTR_TAG, self::TIME_ATTR_SIZE);
152
        // refactoring will be needed when php 5.5 support ends
153
        $data .= PackUtil::packLongLE($this->modifyNtfsTime);
154
        $data .= PackUtil::packLongLE($this->accessNtfsTime);
155
        $data .= PackUtil::packLongLE($this->createNtfsTime);
156
157
        return $data;
158
    }
159
160
    /**
161
     * @return int
162 3
     */
163
    public function getModifyNtfsTime()
164 3
    {
165
        return $this->modifyNtfsTime;
166
    }
167
168
    /**
169
     * @param int $modifyNtfsTime
170
     */
171
    public function setModifyNtfsTime($modifyNtfsTime)
172
    {
173
        $this->modifyNtfsTime = (int) $modifyNtfsTime;
174
    }
175
176
    /**
177
     * @return int
178 3
     */
179
    public function getAccessNtfsTime()
180 3
    {
181
        return $this->accessNtfsTime;
182
    }
183
184
    /**
185
     * @param int $accessNtfsTime
186
     */
187
    public function setAccessNtfsTime($accessNtfsTime)
188
    {
189
        $this->accessNtfsTime = (int) $accessNtfsTime;
190
    }
191
192
    /**
193
     * @return int
194 2
     */
195
    public function getCreateNtfsTime()
196 2
    {
197
        return $this->createNtfsTime;
198
    }
199
200
    /**
201
     * @param int $createNtfsTime
202
     */
203
    public function setCreateNtfsTime($createNtfsTime)
204
    {
205
        $this->createNtfsTime = (int) $createNtfsTime;
206
    }
207
208
    /**
209
     * The actual data to put into central directory - without Header-ID or
210
     * length specifier.
211
     *
212
     * @return string the data
213
     */
214
    public function packCentralDirData()
215
    {
216
        return $this->packLocalFileData();
217
    }
218
219
    /**
220
     * @return \DateTimeInterface
221
     */
222 3
    public function getModifyDateTime()
223
    {
224 3
        return self::ntfsTimeToDateTime($this->modifyNtfsTime);
225
    }
226
227 3
    /**
228
     * @param \DateTimeInterface $modifyTime
229
     */
230
    public function setModifyDateTime(\DateTimeInterface $modifyTime)
231
    {
232
        $this->modifyNtfsTime = self::dateTimeToNtfsTime($modifyTime);
233
    }
234
235
    /**
236
     * @return \DateTimeInterface
237
     */
238
    public function getAccessDateTime()
239
    {
240
        return self::ntfsTimeToDateTime($this->accessNtfsTime);
241
    }
242
243
    /**
244
     * @param \DateTimeInterface $accessTime
245
     */
246
    public function setAccessDateTime(\DateTimeInterface $accessTime)
247
    {
248
        $this->accessNtfsTime = self::dateTimeToNtfsTime($accessTime);
249
    }
250
251
    /**
252
     * @return \DateTimeInterface
253
     */
254
    public function getCreateDateTime()
255
    {
256
        return self::ntfsTimeToDateTime($this->createNtfsTime);
257
    }
258
259
    /**
260
     * @param \DateTimeInterface $createTime
261
     */
262
    public function setCreateDateTime(\DateTimeInterface $createTime)
263
    {
264
        $this->createNtfsTime = self::dateTimeToNtfsTime($createTime);
265
    }
266
267
    /**
268
     * @param float $timestamp Float timestamp
269
     *
270
     * @return int
271
     */
272
    public static function timestampToNtfsTime($timestamp)
273
    {
274
        return (int) (((float) $timestamp * 10000000) - self::EPOCH_OFFSET);
275
    }
276
277
    /**
278
     * @param \DateTimeInterface $dateTime
279
     *
280
     * @return int
281
     */
282
    public static function dateTimeToNtfsTime(\DateTimeInterface $dateTime)
283
    {
284
        return self::timestampToNtfsTime((float) $dateTime->format('U.u'));
285
    }
286
287
    /**
288
     * @param int $ntfsTime
289
     *
290
     * @return float Float unix timestamp
291
     */
292
    public static function ntfsTimeToTimestamp($ntfsTime)
293
    {
294
        return (float) (($ntfsTime + self::EPOCH_OFFSET) / 10000000);
295
    }
296
297
    /**
298
     * @param int $ntfsTime
299
     *
300
     * @return \DateTimeInterface
301
     */
302
    public static function ntfsTimeToDateTime($ntfsTime)
303
    {
304
        $timestamp = self::ntfsTimeToTimestamp($ntfsTime);
305
        $dateTime = \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6f', $timestamp));
306
307
        if ($dateTime === false) {
308
            throw new InvalidArgumentException('Cannot create date/time object for timestamp ' . $timestamp);
309
        }
310
311
        return $dateTime;
312
    }
313
314
    /**
315
     * @return string
316
     */
317
    public function __toString()
318
    {
319
        $args = [self::HEADER_ID];
320
        $format = '0x%04x NtfsExtra:';
321
322
        if ($this->modifyNtfsTime !== 0) {
323
            $format .= ' Modify:[%s]';
324
            $args[] = $this->getModifyDateTime()->format(\DATE_ATOM);
325
        }
326
327
        if ($this->accessNtfsTime !== 0) {
328
            $format .= ' Access:[%s]';
329
            $args[] = $this->getAccessDateTime()->format(\DATE_ATOM);
330
        }
331
332
        if ($this->createNtfsTime !== 0) {
333
            $format .= ' Create:[%s]';
334
            $args[] = $this->getCreateDateTime()->format(\DATE_ATOM);
335
        }
336
337
        return vsprintf($format, $args);
338
    }
339
}
340