Passed
Branch master (25d5dd)
by Alexey
02:30
created

NewUnixExtraField   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 201
Duplicated Lines 0 %

Test Coverage

Coverage 12.07%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 49
dl 0
loc 201
ccs 7
cts 58
cp 0.1207
rs 10
c 1
b 0
f 1
wmc 16

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getUid() 0 3 1
A signedByteToUnsignedInt() 0 7 2
A setGid() 0 3 1
A unpackCentralDirData() 0 3 1
A packCentralDirData() 0 3 1
A __construct() 0 5 1
A __toString() 0 7 1
A packLocalFileData() 0 9 1
A readSizeIntegerLE() 0 13 2
A getHeaderId() 0 3 1
A setUid() 0 3 1
A getGid() 0 3 1
A unpackLocalFileData() 0 18 2
1
<?php
2
3
namespace PhpZip\Model\Extra\Fields;
4
5
use PhpZip\Exception\ZipException;
6
use PhpZip\Model\Extra\ZipExtraField;
7
use PhpZip\Model\ZipEntry;
8
9
/**
10
 * Info-ZIP New Unix Extra Field:
11
 * ====================================.
12
 *
13
 * Currently stores Unix UIDs/GIDs up to 32 bits.
14
 * (Last Revision 20080509)
15
 *
16
 * Value         Size        Description
17
 * -----         ----        -----------
18
 * (UnixN) 0x7875        Short       tag for this extra block type ("ux")
19
 * TSize         Short       total data size for this block
20
 * Version       1 byte      version of this extra field, currently 1
21
 * UIDSize       1 byte      Size of UID field
22
 * UID           Variable    UID for this entry
23
 * GIDSize       1 byte      Size of GID field
24
 * GID           Variable    GID for this entry
25
 *
26
 * Currently Version is set to the number 1.  If there is a need
27
 * to change this field, the version will be incremented.  Changes
28
 * may not be backward compatible so this extra field should not be
29
 * used if the version is not recognized.
30
 *
31
 * UIDSize is the size of the UID field in bytes.  This size should
32
 * match the size of the UID field on the target OS.
33
 *
34
 * UID is the UID for this entry in standard little endian format.
35
 *
36
 * GIDSize is the size of the GID field in bytes.  This size should
37
 * match the size of the GID field on the target OS.
38
 *
39
 * GID is the GID for this entry in standard little endian format.
40
 *
41
 * If both the old 16-bit Unix extra field (tag 0x7855, Info-ZIP Unix)
42
 * and this extra field are present, the values in this extra field
43
 * supercede the values in that extra field.
44
 */
45
class NewUnixExtraField implements ZipExtraField
46
{
47
    /** @var int header id */
48
    const HEADER_ID = 0x7875;
49
50
    /** ID of the first non-root user created on a unix system. */
51
    const USER_GID_PID = 1000;
52
53
    /** @var int version of this extra field, currently 1 */
54
    private $version = 1;
55
56
    /** @var int User id */
57
    private $uid;
58
59
    /** @var int Group id */
60
    private $gid;
61
62
    /**
63
     * NewUnixExtraField constructor.
64
     *
65
     * @param int $version
66
     * @param int $uid
67
     * @param int $gid
68
     */
69 2
    public function __construct($version = 1, $uid = self::USER_GID_PID, $gid = self::USER_GID_PID)
70
    {
71 2
        $this->version = (int) $version;
72 2
        $this->uid = (int) $uid;
73 2
        $this->gid = (int) $gid;
74 2
    }
75
76
    /**
77
     * Returns the Header ID (type) of this Extra Field.
78
     * The Header ID is an unsigned short integer (two bytes)
79
     * which must be constant during the life cycle of this object.
80
     *
81
     * @return int
82
     */
83 2
    public function getHeaderId()
84
    {
85 2
        return self::HEADER_ID;
86
    }
87
88
    /**
89
     * Populate data from this array as if it was in local file data.
90
     *
91
     * @param string        $buffer the buffer to read data from
92
     * @param ZipEntry|null $entry
93
     *
94
     * @throws ZipException
95
     *
96
     * @return NewUnixExtraField
97
     */
98
    public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
99
    {
100
        $length = \strlen($buffer);
101
102
        if ($length < 3) {
103
            throw new ZipException(sprintf('X7875_NewUnix length is too short, only %s bytes', $length));
104
        }
105
        $offset = 0;
106
        $data = unpack('Cversion/CuidSize', $buffer);
107
        $offset += 2;
108
        $uidSize = $data['uidSize'];
109
        $gid = self::readSizeIntegerLE(substr($buffer, $offset, $uidSize), $uidSize);
110
        $offset += $uidSize;
111
        $gidSize = unpack('C', $buffer[$offset])[1];
112
        $offset++;
113
        $uid = self::readSizeIntegerLE(substr($buffer, $offset, $gidSize), $gidSize);
114
115
        return new self($data['version'], $gid, $uid);
116
    }
117
118
    /**
119
     * Converts a signed byte into an unsigned integer representation
120
     * (e.g., -1 becomes 255).
121
     *
122
     * @param int $b byte to convert to int
123
     *
124
     * @return int representation of the provided byte
125
     *
126
     * @since 1.5
127
     */
128
    public static function signedByteToUnsignedInt($b)
129
    {
130
        if ($b >= 0) {
131
            return $b;
132
        }
133
134
        return 256 + $b;
135
    }
136
137
    /**
138
     * Populate data from this array as if it was in central directory data.
139
     *
140
     * @param string        $buffer the buffer to read data from
141
     * @param ZipEntry|null $entry
142
     *
143
     * @throws ZipException
144
     *
145
     * @return NewUnixExtraField
146
     */
147
    public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
148
    {
149
        return self::unpackLocalFileData($buffer, $entry);
150
    }
151
152
    /**
153
     * The actual data to put into local file data - without Header-ID
154
     * or length specifier.
155
     *
156
     * @return string the data
157
     */
158
    public function packLocalFileData()
159
    {
160
        return pack(
161
            'CCVCV',
162
            $this->version,
163
            4, // GIDSize
164
            $this->gid,
165
            4, // UIDSize
166
            $this->uid
167
        );
168
    }
169
170
    /**
171
     * The actual data to put into central directory - without Header-ID or
172
     * length specifier.
173
     *
174
     * @return string the data
175
     */
176
    public function packCentralDirData()
177
    {
178
        return $this->packLocalFileData();
179
    }
180
181
    /**
182
     * @param string $data
183
     * @param int    $size
184
     *
185
     * @throws ZipException
186
     *
187
     * @return int
188
     */
189
    private static function readSizeIntegerLE($data, $size)
190
    {
191
        $format = [
192
            1 => 'C', // unsigned byte
193
            2 => 'v', // unsigned short LE
194
            4 => 'V', // unsigned int LE
195
        ];
196
197
        if (!isset($format[$size])) {
198
            throw new ZipException(sprintf('Invalid size bytes: %d', $size));
199
        }
200
201
        return unpack($format[$size], $data)[1];
202
    }
203
204
    /**
205
     * @return int
206
     */
207
    public function getUid()
208
    {
209
        return $this->uid;
210
    }
211
212
    /**
213
     * @param int $uid
214
     */
215
    public function setUid($uid)
216
    {
217
        $this->uid = $uid & 0xffffffff;
218
    }
219
220
    /**
221
     * @return int
222
     */
223
    public function getGid()
224
    {
225
        return $this->gid;
226
    }
227
228
    /**
229
     * @param int $gid
230
     */
231
    public function setGid($gid)
232
    {
233
        $this->gid = $gid & 0xffffffff;
234
    }
235
236
    /**
237
     * @return string
238
     */
239
    public function __toString()
240
    {
241
        return sprintf(
242
            '0x%04x NewUnix: UID=%d GID=%d',
243
            self::HEADER_ID,
244
            $this->uid,
245
            $this->gid
246
        );
247
    }
248
}
249