Completed
Push — master ( 89947a...634c62 )
by Ramūnas
19s queued 11s
created

PhpAmqpLib/Wire/AMQPWriter.php (2 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
namespace PhpAmqpLib\Wire;
3
4
use PhpAmqpLib\Exception\AMQPInvalidArgumentException;
5
use PhpAmqpLib\Exception\AMQPOutOfRangeException;
6
use phpseclib\Math\BigInteger;
7
8
class AMQPWriter extends AbstractClient
9
{
10
    /** @var string */
11
    protected $out = '';
12
13
    /** @var array */
14
    protected $bits = array();
15
16
    /** @var int */
17
    protected $bitcount = 0;
18
19 2
    private function flushbits()
20
    {
21 2
        if (!empty($this->bits)) {
22 2
            $this->out .= implode('', array_map('chr', $this->bits));
23 2
            $this->bits = array();
24 2
            $this->bitcount = 0;
25
        }
26 2
    }
27
28
    /**
29
     * Get what's been encoded so far.
30
     *
31
     * @return string
32
     */
33 706
    public function getvalue()
34
    {
35
        /* temporarily needed for compatibility with write_bit unit tests */
36 706
        if ($this->bitcount) {
37 2
            $this->flushbits();
38
        }
39
40 706
        return $this->out;
41
    }
42
43
    /**
44
     * Write a plain PHP string, with no special encoding.
45
     *
46
     * @param string $s
47
     *
48
     * @return $this
49
     */
50 72
    public function write($s)
51
    {
52 72
        $this->out .= $s;
53
54 72
        return $this;
55
    }
56
57
    /**
58
     * Write a boolean value.
59
     * (deprecated, use write_bits instead)
60
     *
61
     * @deprecated
62
     * @param bool $b
63
     * @return $this
64
     */
65 2
    public function write_bit($b)
66
    {
67 2
        $b = $b ? 1 : 0;
68 2
        $shift = $this->bitcount % 8;
69 2
        $last = $shift === 0 ? 0 : array_pop($this->bits);
70 2
        $last |= ($b << $shift);
71 2
        $this->bits[] = $last;
72 2
        $this->bitcount++;
73
74 2
        return $this;
75
    }
76
77
    /**
78
     * Write multiple bits as an octet
79
     *
80
     * @param bool[] $bits
81
     * @return $this
82
     */
83 63
    public function write_bits($bits)
84
    {
85 63
        $value = 0;
86
87 63
        foreach ($bits as $n => $bit) {
88 63
            $bit = $bit ? 1 : 0;
89 63
            $value |= ($bit << $n);
90
        }
91
92 63
        $this->out .= chr($value);
93
94 63
        return $this;
95
    }
96
97
    /**
98
     * Write an integer as an unsigned 8-bit value
99
     *
100
     * @param int $n
101
     * @return $this
102
     * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
103
     */
104 344 View Code Duplication
    public function write_octet($n)
105
    {
106 344
        if ($n < 0 || $n > 255) {
107 2
            throw new AMQPInvalidArgumentException('Octet out of range: ' . $n);
108
        }
109
110 342
        $this->out .= chr($n);
111
112 342
        return $this;
113
    }
114
115
    /**
116
     * @param int $n
117
     * @return $this
118
     */
119 260 View Code Duplication
    public function write_signed_octet($n)
120
    {
121 260
        if (($n < -128) || ($n > 127)) {
122 2
            throw new AMQPInvalidArgumentException('Signed octet out of range: ' . $n);
123
        }
124
125 258
        $this->out .= pack('c', $n);
126
127 258
        return $this;
128
    }
129
130
    /**
131
     * Write an integer as an unsigned 16-bit value
132
     *
133
     * @param int $n
134
     * @return $this
135
     * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
136
     */
137 76 View Code Duplication
    public function write_short($n)
138
    {
139 76
        if ($n < 0 || $n > 65535) {
140 3
            throw new AMQPInvalidArgumentException('Short out of range: ' . $n);
141
        }
142
143 73
        $this->out .= pack('n', $n);
144
145 73
        return $this;
146
    }
147
148
    /**
149
     * @param int $n
150
     * @return $this
151
     */
152 8 View Code Duplication
    public function write_signed_short($n)
153
    {
154 8
        if (($n < -32768) || ($n > 32767)) {
155 2
            throw new AMQPInvalidArgumentException('Signed short out of range: ' . $n);
156
        }
157
158 6
        $this->out .= $this->correctEndianness(pack('s', $n));
159
160 6
        return $this;
161
    }
162
163
    /**
164
     * Write an integer as an unsigned 32-bit value
165
     *
166
     * @param int|string $n
167
     * @return $this
168
     */
169 87
    public function write_long($n)
170
    {
171 87
        if (($n < 0) || ($n > 4294967295)) {
172 2
            throw new AMQPInvalidArgumentException('Long out of range: ' . $n);
173
        }
174
175
        //Numeric strings >PHP_INT_MAX on 32bit are casted to PHP_INT_MAX, damn PHP
176 85
        if (!$this->is64bits && is_string($n)) {
177
            $n = (float) $n;
178
        }
179 85
        $this->out .= pack('N', $n);
180
181 85
        return $this;
182
    }
183
184
    /**
185
     * @param int $n
186
     * @return $this
187
     */
188 33 View Code Duplication
    private function write_signed_long($n)
189
    {
190 33
        if (($n < -2147483648) || ($n > 2147483647)) {
191 2
            throw new AMQPInvalidArgumentException('Signed long out of range: ' . $n);
192
        }
193
194
        //on my 64bit debian this approach is slightly faster than splitIntoQuads()
195 31
        $this->out .= $this->correctEndianness(pack('l', $n));
196
197 31
        return $this;
198
    }
199
200
    /**
201
     * Write a numeric value as an unsigned 64-bit value
202
     *
203
     * @param int|string $n
204
     * @return $this
205
     * @throws AMQPOutOfRangeException
206
     */
207 40
    public function write_longlong($n)
208
    {
209 40
        if (is_int($n)) {
210 23
            if ($n < 0) {
211
                throw new AMQPOutOfRangeException('Longlong out of range: ' . $n);
212
            }
213
214 23
            if ($this->is64bits) {
215 23
                $res = pack('J', $n);
216 23
                $this->out .= $res;
217
            } else {
218
                $this->out .= pack('NN', 0, $n);
219
            }
220
221 23
            return $this;
222
        }
223
224 17
        $value = new BigInteger($n);
225 17 View Code Duplication
        if ($value->compare(self::getBigInteger('0')) < 0 || $value->compare(self::getBigInteger('FFFFFFFFFFFFFFFF', 16)) > 0) {
0 ignored issues
show
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...
226 2
            throw new AMQPInvalidArgumentException('Longlong out of range: ' . $n);
227
        }
228
229 15
        $value->setPrecision(64);
230 15
        $this->out .= $value->toBytes();
231
232 15
        return $this;
233
    }
234
235
    /**
236
     * @param int|string $n
237
     * @return $this
238
     */
239 38
    public function write_signed_longlong($n)
240
    {
241 38
        if (is_int($n)) {
242 12
            if ($this->is64bits) {
243
                // q is for 64-bit signed machine byte order
244 12
                $packed = pack('q', $n);
245 12
                if (self::isLittleEndian()) {
246 12
                    $packed = $this->convertByteOrder($packed);
247
                }
248 12
                $this->out .= $packed;
249
            } else {
250
                $hi = $n < 0 ? -1 : 0;
251
                $lo = $n;
252
                $this->out .= pack('NN', $hi, $lo);
253
            }
254
255 12
            return $this;
256
        }
257
258 26
        $value = new BigInteger($n);
259 26 View Code Duplication
        if ($value->compare(self::getBigInteger('-8000000000000000', 16)) < 0 || $value->compare(self::getBigInteger('7FFFFFFFFFFFFFFF', 16)) > 0) {
0 ignored issues
show
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...
260 2
            throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n);
261
        }
262
263 24
        $value->setPrecision(64);
264 24
        $this->out .= substr($value->toBytes(true), -8);
265
266 24
        return $this;
267
    }
268
269
    /**
270
     * Write a string up to 255 bytes long after encoding.
271
     * Assume UTF-8 encoding
272
     *
273
     * @param string $s
274
     * @return $this
275
     * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
276
     */
277 81
    public function write_shortstr($s)
278
    {
279 81
        $len = mb_strlen($s, 'ASCII');
280 81
        if ($len > 255) {
281 2
            throw new AMQPInvalidArgumentException('String too long');
282
        }
283
284 79
        $this->write_octet($len);
285 79
        $this->out .= $s;
286
287 79
        return $this;
288
    }
289
290
    /**
291
     * Write a string up to 2**32 bytes long.  Assume UTF-8 encoding
292
     *
293
     * @param string $s
294
     * @return $this
295
     */
296 55
    public function write_longstr($s)
297
    {
298 55
        $this->write_long(mb_strlen($s, 'ASCII'));
299 55
        $this->out .= $s;
300
301 55
        return $this;
302
    }
303
304
    /**
305
     * Supports the writing of Array types, so that you can implement
306
     * array methods, like Rabbitmq's HA parameters
307
     *
308
     * @param AMQPArray|array $a Instance of AMQPArray or PHP array WITHOUT format hints (unlike write_table())
309
     * @return self
310
     */
311 10
    public function write_array($a)
312
    {
313 10
        if (!($a instanceof AMQPArray)) {
314 6
            $a = new AMQPArray($a);
315
        }
316 10
        $data = new self();
317
318 10
        foreach ($a as $v) {
319 9
            $data->write_value($v[0], $v[1]);
320
        }
321
322 10
        $data = $data->getvalue();
323 10
        $this->write_long(mb_strlen($data, 'ASCII'));
324 10
        $this->write($data);
325
326 10
        return $this;
327
    }
328
329
    /**
330
     * Write unix time_t value as 64 bit timestamp
331
     *
332
     * @param int $v
333
     * @return $this
334
     */
335 1
    public function write_timestamp($v)
336
    {
337 1
        $this->write_longlong($v);
338
339 1
        return $this;
340
    }
341
342
    /**
343
     * Write PHP array, as table. Input array format: keys are strings,
344
     * values are (type,value) tuples.
345
     *
346
     * @param AMQPTable|array $d Instance of AMQPTable or PHP array WITH format hints (unlike write_array())
347
     * @return $this
348
     * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
349
     */
350 62
    public function write_table($d)
351
    {
352 62
        $typeIsSym = !($d instanceof AMQPTable); //purely for back-compat purposes
353
354 62
        $table_data = new AMQPWriter();
355 62
        foreach ($d as $k => $va) {
356 55
            list($ftype, $v) = $va;
357 55
            $table_data->write_shortstr($k);
358 55
            $table_data->write_value($typeIsSym ? AMQPAbstractCollection::getDataTypeForSymbol($ftype) : $ftype, $v);
359
        }
360
361 61
        $table_data = $table_data->getvalue();
362 61
        $this->write_long(mb_strlen($table_data, 'ASCII'));
363 61
        $this->write($table_data);
364
365 61
        return $this;
366
    }
367
368
    /**
369
     * for compat with method mapping used by AMQPMessage
370
     *
371
     * @param AMQPTable|array $d
372
     * @return $this
373
     */
374 8
    public function write_table_object($d)
375
    {
376 8
        return $this->write_table($d);
377
    }
378
379
    /**
380
     * @param int $type One of AMQPAbstractCollection::T_* constants
381
     * @param mixed $val
382
     */
383 57
    private function write_value($type, $val)
384
    {
385
        //This will find appropriate symbol for given data type for currently selected protocol
386
        //Also will raise an exception on unknown type
387 57
        $this->write(AMQPAbstractCollection::getSymbolForDataType($type));
388
389
        switch ($type) {
390 57
            case AMQPAbstractCollection::T_INT_SHORTSHORT:
391 2
                $this->write_signed_octet($val);
392 2
                break;
393 57
            case AMQPAbstractCollection::T_INT_SHORTSHORT_U:
394 2
                $this->write_octet($val);
395 2
                break;
396 57
            case AMQPAbstractCollection::T_INT_SHORT:
397 2
                $this->write_signed_short($val);
398 2
                break;
399 57
            case AMQPAbstractCollection::T_INT_SHORT_U:
400 2
                $this->write_short($val);
401 2
                break;
402 57
            case AMQPAbstractCollection::T_INT_LONG:
403 11
                $this->write_signed_long($val);
404 11
                break;
405 54
            case AMQPAbstractCollection::T_INT_LONG_U:
406
                $this->write_long($val);
407
                break;
408 54
            case AMQPAbstractCollection::T_INT_LONGLONG:
409 3
                $this->write_signed_longlong($val);
410 3
                break;
411 54
            case AMQPAbstractCollection::T_INT_LONGLONG_U:
412
                $this->write_longlong($val);
413
                break;
414 54
            case AMQPAbstractCollection::T_DECIMAL:
415
                $this->write_octet($val->getE());
416
                $this->write_signed_long($val->getN());
417
                break;
418 54
            case AMQPAbstractCollection::T_TIMESTAMP:
419
                $this->write_timestamp($val);
420
                break;
421 54
            case AMQPAbstractCollection::T_BOOL:
422 49
                $this->write_octet($val ? 1 : 0);
423 49
                break;
424 54
            case AMQPAbstractCollection::T_STRING_SHORT:
425 2
                $this->write_shortstr($val);
426 2
                break;
427 54
            case AMQPAbstractCollection::T_STRING_LONG:
428 52
                $this->write_longstr($val);
429 52
                break;
430 49
            case AMQPAbstractCollection::T_ARRAY:
431 8
                $this->write_array($val);
432 8
                break;
433 46
            case AMQPAbstractCollection::T_TABLE:
434 44
                $this->write_table($val);
435 44
                break;
436 2
            case AMQPAbstractCollection::T_VOID:
437 1
                break;
438 1
            case AMQPAbstractCollection::T_BYTES:
439 1
                $this->write_longstr($val);
440 1
                break;
441
            default:
442
                throw new AMQPInvalidArgumentException(sprintf(
443
                    'Unsupported type "%s"',
444
                    $type
445
                ));
446
        }
447 57
    }
448
}
449