Completed
Pull Request — master (#76)
by
unknown
02:33
created

StreamReader::pos()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
namespace Cassandra\Response;
3
4
use Cassandra\Type;
5
6
class StreamReader {
7
8
    /**
9
     * @var string
10
     */
11
    protected $data;
12
    
13
    /**
14
     * @var int
15
     */
16
    protected $offset = 0;
17
18
    public function __construct($data){
19
        $this->data = $data;
20
    }
21
22
    /**
23
     * Read data from stream.
24
     * 
25
     * NOTICE When $this->offset == strlen($this->data), substr() will return false.  You'd better avoid call read() when $length == 0.
26
     *
27
     * @param int $length  $length should be > 0.
28
     * @return string
29
     */
30
    protected function read($length) {
31
        $output = substr($this->data, $this->offset, $length);
32
        $this->offset += $length;
33
        return $output;
34
    }
35
36
    public function offset($offset){
37
        $this->offset = $offset;
38
    }
39
    
40
    public function reset(){
41
        $this->offset = 0;
42
    }
43
    
44
    /**
45
     * Get the current position of this stream
46
     * 
47
     * @return int
48
     */
49
    public function pos() {
50
        return $this->offset;
51
    }
52
53
    /**
54
     * Read single character.
55
     *
56
     * @return int
57
     */
58
    public function readChar() {
59
        return unpack('C', $this->read(1))[1];
60
    }
61
62
    /**
63
     * Read unsigned short.
64
     *
65
     * @return int
66
     */
67
    public function readShort() {
68
        return unpack('n', $this->read(2))[1];
69
    }
70
71
    /**
72
     * Read unsigned int.
73
     *
74
     * @return int
75
     */
76
    public function readInt() {
77
        return unpack('N', $this->read(4))[1];
78
    }
79
80
    /**
81
     * Read string.
82
     *
83
     * @return string
84
     */
85
    public function readString() {
86
        $length = unpack('n', $this->read(2))[1];
87
        return $length === 0 ? '' : $this->read($length);
88
    }
89
90
    /**
91
     * Read long string.
92
     *
93
     * @return string
94
     */
95
    public function readLongString() {
96
        $length = unpack('N', $this->read(4))[1];
97
        return $length === 0 ? '' : $this->read($length);
98
    }
99
100
    /**
101
     * Read bytes.
102
     *
103
     * @return string
104
     */
105
    public function readBytes() {
106
        $binaryLength = $this->read(4);
107
        if ($binaryLength === "\xff\xff\xff\xff")
108
            return null;
109
110
        $length = unpack('N', $binaryLength)[1];
111
        return $length === 0 ? '' : $this->read($length);
112
    }
113
114
    /**
115
     * Read uuid.
116
     *
117
     * @return string
118
     */
119
    public function readUuid() {
120
        $data = unpack('n8', $this->read(16));
121
122
        return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', $data[1], $data[2], $data[3], $data[4], $data[5], $data[6], $data[7], $data[8]);
123
    }
124
125
    /**
126
     * Read list.
127
     *
128
     * @param array $definition [$valueType]
129
     * @return array
130
     */
131 View Code Duplication
    public function readList(array $definition) {
0 ignored issues
show
Duplication introduced by
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...
132
        list($valueType) = $definition;
133
        $list = [];
134
        $count = $this->readInt();
135
        for ($i = 0; $i < $count; ++$i) {
136
            $list[] = $this->readValue($valueType);
137
        }
138
        return $list;
139
    }
140
141
    /**
142
     * Read map.
143
     *
144
     * @param array $definition [$keyType, $valueType]
145
     * @return array
146
     */
147 View Code Duplication
    public function readMap(array $definition) {
0 ignored issues
show
Duplication introduced by
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...
148
        list($keyType, $valueType) = $definition;
149
        $map = [];
150
        $count = $this->readInt();
151
        for ($i = 0; $i < $count; ++$i) {
152
            $map[$this->readValue($keyType)] = $this->readValue($valueType);
153
        }
154
        return $map;
155
    }
156
157
    /**
158
     * 
159
     * @param array $definition ['key1'=>$valueType1, 'key2'=>$valueType2, ...]
160
     * @return array
161
     */
162
    public function readTuple(array $definition) {
163
        $tuple = [];
164
        $dataLength = strlen($this->data);
165
        foreach ($definition as $key => $type) {
166
            if ($this->offset < $dataLength)
167
                $tuple[$key] = $this->readValue($type);
168
            else
169
                $tuple[$key] = null;
170
        }
171
        return $tuple;
172
    }
173
174
    /**
175
     * Read float.
176
     *
177
     * @return float
178
     */
179
    public function readFloat() {
180
        return unpack('f', strrev($this->read(4)))[1];
181
    }
182
183
    /**
184
     * Read double.
185
     *
186
     * @return double
187
     */
188
    public function readDouble() {
189
        return unpack('d', strrev($this->read(8)))[1];
190
    }
191
192
    /**
193
     * Read boolean.
194
     *
195
     * @return bool
196
     */
197
    public function readBoolean() {
198
        return (bool)$this->readChar();
199
    }
200
201
    /**
202
     * Read inet.
203
     *
204
     * @return string
205
     */
206
    public function readInet() {
207
        return inet_ntop($this->data);
208
    }
209
210
    /**
211
     * Read variable length integer.
212
     *
213
     * @return string
214
     */
215
    public function readVarint() {
216
        list($higher, $lower) = array_values(unpack('N2', $this->data));
217
        return $higher << 32 | $lower;
218
    }
219
220
    /**
221
     * Read variable length decimal.
222
     *
223
     * @return string
224
     */
225
    public function readDecimal() {
226
        $scale = $this->readInt();
227
        $value = $this->readVarint();
228
        $len = strlen($value);
229
        return substr($value, 0, $len - $scale) . '.' . substr($value, $len - $scale);
230
    }
231
    
232
    public function readStringMultimap(){
233
        $map = [];
234
        $count = $this->readShort();
235
        for($i = 0; $i < $count; $i++){
236
            $key = $this->readString();
237
                
238
            $listLength = $this->readShort();
239
            $list = [];
240
            for($j = 0; $j < $listLength; $j++)
241
                $list[] = $this->readString();
242
                    
243
            $map[$key] = $list;
244
        }
245
        return $map;
246
    }
247
248
    /**
249
     * alias of readValue()
250
     * @deprecated
251
     * 
252
     * @param int|array $type
253
     * @return mixed
254
     */
255
    public function readBytesAndConvertToType($type){
256
        return $this->readValue($type);
257
    }
258
    
259
    /**
260
     * read a [bytes] and read by type
261
     *
262
     * @param int|array $type
263
     * @return mixed
264
     */
265
    public function readValue($type){
266
        $binaryLength = substr($this->data, $this->offset, 4);
267
        $this->offset += 4;
268
269
        if ($binaryLength === "\xff\xff\xff\xff")
270
            return null;
271
272
        $length = unpack('N', $binaryLength)[1];
273
274
        // do not use $this->read() for performance
275
        // substr() returns FALSE when OFFSET is equal to the length of data
276
        $data = ($length == 0) ? '' : substr($this->data, $this->offset, $length);
277
        $this->offset += $length;
278
        if(!is_array($type)){
279
            $class = Type\Base::$typeClassMap[$type];
280
            return $class::parse($data);
281
        }
282
        else{
283
            if (!isset(Type\Base::$typeClassMap[$type['type']]))
284
                throw new Type\Exception('Unknown type ' . var_export($type, true));
285
            $class = Type\Base::$typeClassMap[$type['type']];
286
            return $class::parse($data, $type['definition']);
287
        }
288
    }
289
290
    /**
291
     * @return int|array
292
     */
293
    public function readType(){
294
        $type = $this->readShort();
295
        switch ($type) {
296
            case Type\Base::CUSTOM:
297
                return [
298
                    'type'    => $type,
299
                    'definition'=> [$this->readString()],
300
                ];
301
            case Type\Base::COLLECTION_LIST:
302
            case Type\Base::COLLECTION_SET:
303
                return [
304
                    'type'    => $type,
305
                    'definition'    => [$this->readType()],
306
                ];
307
            case Type\Base::COLLECTION_MAP:
308
                return [
309
                    'type'    => $type,
310
                    'definition'=> [$this->readType(), $this->readType()],
311
                ];
312
            case Type\Base::UDT:
313
                $data = [
314
                    'type'        => $type,
315
                    'keyspace'    => $this->readString(),
316
                    'name'        => $this->readString(),
317
                    'definition'    => [],
318
                ];
319
                $length = $this->readShort();
320 View Code Duplication
                for($i = 0; $i < $length; ++$i){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
321
                    $key = $this->readString();
322
                    $data['definition'][$key] = $this->readType();
323
                }
324
                return $data;
325
            case Type\Base::TUPLE:
326
                $data = [
327
                    'type'    => $type,
328
                    'definition'    =>    [],
329
                ];
330
                $length = $this->readShort();
331 View Code Duplication
                for($i = 0; $i < $length; ++$i){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
332
                    $data['definition'][] = $this->readType();
333
                }
334
                return $data;
335
            default:
336
                return $type;
337
        }
338
    }
339
}
340