This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
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\AMQPOutOfBoundsException; |
||
6 | |||
7 | class AMQPWriter extends AbstractClient |
||
8 | { |
||
9 | /** @var string */ |
||
10 | protected $out; |
||
11 | |||
12 | /** @var array */ |
||
13 | protected $bits; |
||
14 | |||
15 | /** @var int */ |
||
16 | protected $bitcount; |
||
17 | |||
18 | 435 | public function __construct() |
|
19 | { |
||
20 | 435 | parent::__construct(); |
|
21 | |||
22 | 435 | $this->out = ''; |
|
23 | 435 | $this->bits = array(); |
|
24 | 435 | $this->bitcount = 0; |
|
25 | 435 | } |
|
26 | |||
27 | /** |
||
28 | * Packs integer into raw byte string in big-endian order |
||
29 | * Supports positive and negative ints represented as PHP int or string (except scientific notation) |
||
30 | * |
||
31 | * Floats has some precision issues and so intentionally not supported. |
||
32 | * Beware that floats out of PHP_INT_MAX range will be represented in scientific (exponential) notation when casted to string |
||
33 | * |
||
34 | * @param int|string $x Value to pack |
||
35 | * @param int $bytes Must be multiply of 2 |
||
36 | * @return string |
||
37 | */ |
||
38 | 10 | private static function packBigEndian($x, $bytes) |
|
39 | { |
||
40 | 10 | if (($bytes <= 0) || ($bytes % 2)) { |
|
41 | throw new AMQPInvalidArgumentException(sprintf('Expected bytes count must be multiply of 2, %s given', $bytes)); |
||
42 | } |
||
43 | |||
44 | 10 | $ox = $x; //purely for dbg purposes (overflow exception) |
|
45 | 10 | $isNeg = false; |
|
46 | |||
47 | 10 | if (is_int($x)) { |
|
48 | if ($x < 0) { |
||
49 | $isNeg = true; |
||
50 | $x = abs($x); |
||
51 | } |
||
52 | 10 | } elseif (is_string($x)) { |
|
53 | 10 | if (!is_numeric($x)) { |
|
54 | throw new AMQPInvalidArgumentException(sprintf('Unknown numeric string format: %s', $x)); |
||
55 | } |
||
56 | 10 | $x = preg_replace('/^-/', '', $x, 1, $isNeg); |
|
57 | 8 | } else { |
|
58 | throw new AMQPInvalidArgumentException('Only integer and numeric string values are supported'); |
||
59 | } |
||
60 | |||
61 | 10 | if ($isNeg) { |
|
62 | $x = bcadd($x, -1, 0); |
||
63 | } //in negative domain starting point is -1, not 0 |
||
64 | |||
65 | 10 | $res = array(); |
|
66 | 10 | for ($b = 0; $b < $bytes; $b += 2) { |
|
67 | 10 | $chnk = (int) bcmod($x, 65536); |
|
68 | 10 | $x = bcdiv($x, 65536, 0); |
|
69 | 10 | $res[] = pack('n', $isNeg ? ~$chnk : $chnk); |
|
70 | 8 | } |
|
71 | |||
72 | 10 | if ($x || ($isNeg && ($chnk & 0x8000))) { |
|
0 ignored issues
–
show
|
|||
73 | 5 | throw new AMQPOutOfBoundsException(sprintf('Overflow detected while attempting to pack %s into %s bytes', $ox, $bytes)); |
|
74 | } |
||
75 | |||
76 | 5 | return implode(array_reverse($res)); |
|
77 | } |
||
78 | |||
79 | 5 | private function flushbits() |
|
80 | { |
||
81 | 5 | if (!empty($this->bits)) { |
|
82 | 5 | $this->out .= implode('', array_map('chr', $this->bits)); |
|
83 | 5 | $this->bits = array(); |
|
84 | 5 | $this->bitcount = 0; |
|
85 | 4 | } |
|
86 | 5 | } |
|
87 | |||
88 | /** |
||
89 | * Get what's been encoded so far. |
||
90 | */ |
||
91 | 340 | public function getvalue() |
|
92 | { |
||
93 | /* temporarily needed for compatibility with write_bit unit tests */ |
||
94 | 340 | if ($this->bitcount) { |
|
95 | 5 | $this->flushbits(); |
|
96 | 4 | } |
|
97 | |||
98 | 340 | return $this->out; |
|
99 | } |
||
100 | |||
101 | /** |
||
102 | * Write a plain PHP string, with no special encoding. |
||
103 | */ |
||
104 | 210 | public function write($s) |
|
105 | { |
||
106 | 210 | $this->out .= $s; |
|
107 | |||
108 | 210 | return $this; |
|
109 | } |
||
110 | |||
111 | /** |
||
112 | * Write a boolean value. |
||
113 | * (deprecated, use write_bits instead) |
||
114 | * |
||
115 | * @deprecated |
||
116 | * @param $b |
||
117 | * @return $this |
||
118 | */ |
||
119 | 5 | public function write_bit($b) |
|
120 | { |
||
121 | 5 | $b = $b ? 1 : 0; |
|
122 | 5 | $shift = $this->bitcount % 8; |
|
123 | 5 | $last = $shift === 0 ? 0 : array_pop($this->bits); |
|
124 | 5 | $last |= ($b << $shift); |
|
125 | 5 | array_push($this->bits, $last); |
|
126 | 5 | $this->bitcount += 1; |
|
127 | |||
128 | 5 | return $this; |
|
129 | } |
||
130 | |||
131 | /** |
||
132 | * Write multiple bits as an octet |
||
133 | * |
||
134 | * @param $bits |
||
135 | * @return $this |
||
136 | */ |
||
137 | 150 | public function write_bits($bits) |
|
138 | { |
||
139 | 150 | $value = 0; |
|
140 | |||
141 | 150 | foreach ($bits as $n => $bit) { |
|
142 | 150 | $bit = $bit ? 1 : 0; |
|
143 | 150 | $value |= ($bit << $n); |
|
144 | 120 | } |
|
145 | |||
146 | 150 | $this->out .= chr($value); |
|
147 | |||
148 | 150 | return $this; |
|
149 | } |
||
150 | |||
151 | /** |
||
152 | * Write an integer as an unsigned 8-bit value |
||
153 | * |
||
154 | * @param $n |
||
155 | * @return $this |
||
156 | * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException |
||
157 | */ |
||
158 | 275 | View Code Duplication | public function write_octet($n) |
0 ignored issues
–
show
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. ![]() |
|||
159 | { |
||
160 | 275 | if ($n < 0 || $n > 255) { |
|
161 | 10 | throw new AMQPInvalidArgumentException('Octet out of range: ' . $n); |
|
162 | } |
||
163 | |||
164 | 265 | $this->out .= chr($n); |
|
165 | |||
166 | 265 | return $this; |
|
167 | } |
||
168 | |||
169 | 25 | View Code Duplication | public function write_signed_octet($n) |
0 ignored issues
–
show
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. ![]() |
|||
170 | { |
||
171 | 25 | if (($n < -128) || ($n > 127)) { |
|
172 | 10 | throw new AMQPInvalidArgumentException('Signed octet out of range: ' . $n); |
|
173 | } |
||
174 | |||
175 | 15 | $this->out .= pack('c', $n); |
|
176 | |||
177 | 15 | return $this; |
|
178 | } |
||
179 | |||
180 | /** |
||
181 | * Write an integer as an unsigned 16-bit value |
||
182 | * |
||
183 | * @param $n |
||
184 | * @return $this |
||
185 | * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException |
||
186 | */ |
||
187 | 230 | View Code Duplication | public function write_short($n) |
0 ignored issues
–
show
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. ![]() |
|||
188 | { |
||
189 | 230 | if ($n < 0 || $n > 65535) { |
|
190 | 10 | throw new AMQPInvalidArgumentException('Short out of range: ' . $n); |
|
191 | } |
||
192 | |||
193 | 220 | $this->out .= pack('n', $n); |
|
194 | |||
195 | 220 | return $this; |
|
196 | } |
||
197 | |||
198 | 25 | View Code Duplication | public function write_signed_short($n) |
0 ignored issues
–
show
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. ![]() |
|||
199 | { |
||
200 | 25 | if (($n < -32768) || ($n > 32767)) { |
|
201 | 10 | throw new AMQPInvalidArgumentException('Signed short out of range: ' . $n); |
|
202 | } |
||
203 | |||
204 | 15 | $this->out .= $this->correctEndianness(pack('s', $n)); |
|
205 | |||
206 | 15 | return $this; |
|
207 | } |
||
208 | |||
209 | /** |
||
210 | * Write an integer as an unsigned 32-bit value |
||
211 | * |
||
212 | * @param $n |
||
213 | * @return $this |
||
214 | */ |
||
215 | 195 | public function write_long($n) |
|
216 | { |
||
217 | 195 | if (($n < 0) || ($n > 4294967295)) { |
|
218 | 10 | throw new AMQPInvalidArgumentException('Long out of range: ' . $n); |
|
219 | } |
||
220 | |||
221 | //Numeric strings >PHP_INT_MAX on 32bit are casted to PHP_INT_MAX, damn PHP |
||
222 | 185 | if (!$this->is64bits && is_string($n)) { |
|
223 | $n = (float) $n; |
||
224 | } |
||
225 | 185 | $this->out .= pack('N', $n); |
|
226 | |||
227 | 185 | return $this; |
|
228 | } |
||
229 | |||
230 | /** |
||
231 | * @param $n |
||
232 | * @return $this |
||
233 | */ |
||
234 | 70 | View Code Duplication | private function write_signed_long($n) |
0 ignored issues
–
show
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. ![]() |
|||
235 | { |
||
236 | 70 | if (($n < -2147483648) || ($n > 2147483647)) { |
|
237 | 10 | throw new AMQPInvalidArgumentException('Signed long out of range: ' . $n); |
|
238 | } |
||
239 | |||
240 | //on my 64bit debian this approach is slightly faster than splitIntoQuads() |
||
241 | 60 | $this->out .= $this->correctEndianness(pack('l', $n)); |
|
242 | |||
243 | 60 | return $this; |
|
244 | } |
||
245 | |||
246 | /** |
||
247 | * Write an integer as an unsigned 64-bit value |
||
248 | * |
||
249 | * @param $n |
||
250 | * @return $this |
||
251 | */ |
||
252 | 85 | public function write_longlong($n) |
|
253 | { |
||
254 | 85 | if ($n < 0) { |
|
255 | 5 | throw new AMQPInvalidArgumentException('Longlong out of range: ' . $n); |
|
256 | } |
||
257 | |||
258 | // if PHP_INT_MAX is big enough for that |
||
259 | // direct $n<=PHP_INT_MAX check is unreliable on 64bit (values close to max) due to limited float precision |
||
260 | 80 | if (bcadd($n, -PHP_INT_MAX, 0) <= 0) { |
|
261 | // trick explained in http://www.php.net/manual/fr/function.pack.php#109328 |
||
262 | 75 | View Code Duplication | if ($this->is64bits) { |
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. ![]() |
|||
263 | 75 | list($hi, $lo) = $this->splitIntoQuads($n); |
|
264 | 60 | } else { |
|
265 | $hi = 0; |
||
266 | $lo = $n; |
||
267 | } //on 32bits hi quad is 0 a priori |
||
268 | 75 | $this->out .= pack('NN', $hi, $lo); |
|
269 | 60 | } else { |
|
270 | try { |
||
271 | 10 | $this->out .= self::packBigEndian($n, 8); |
|
272 | 9 | } catch (AMQPOutOfBoundsException $ex) { |
|
273 | 5 | throw new AMQPInvalidArgumentException('Longlong out of range: ' . $n, 0, $ex); |
|
274 | } |
||
275 | } |
||
276 | |||
277 | 75 | return $this; |
|
278 | } |
||
279 | |||
280 | 30 | public function write_signed_longlong($n) |
|
281 | { |
||
282 | 30 | if ((bcadd($n, PHP_INT_MAX, 0) >= -1) && (bcadd($n, -PHP_INT_MAX, 0) <= 0)) { |
|
283 | 20 | View Code Duplication | if ($this->is64bits) { |
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. ![]() |
|||
284 | 20 | list($hi, $lo) = $this->splitIntoQuads($n); |
|
285 | 16 | } else { |
|
286 | $hi = $n < 0 ? -1 : 0; |
||
287 | $lo = $n; |
||
288 | } //0xffffffff for negatives |
||
289 | 20 | $this->out .= pack('NN', $hi, $lo); |
|
290 | 26 | } elseif ($this->is64bits) { |
|
291 | 10 | throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n); |
|
292 | } else { |
||
293 | if (bcadd($n, '-9223372036854775807', 0) > 0) { |
||
294 | throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n); |
||
295 | } |
||
296 | try { |
||
297 | //will catch only negative overflow, as values >9223372036854775807 are valid for 8bytes range (unsigned) |
||
298 | $this->out .= self::packBigEndian($n, 8); |
||
299 | } catch (AMQPOutOfBoundsException $ex) { |
||
300 | throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n, 0, $ex); |
||
301 | } |
||
302 | } |
||
303 | |||
304 | 20 | return $this; |
|
305 | } |
||
306 | |||
307 | /** |
||
308 | * @param int $n |
||
309 | * @return array |
||
310 | */ |
||
311 | 95 | private function splitIntoQuads($n) |
|
312 | { |
||
313 | 95 | $n = (int) $n; |
|
314 | |||
315 | 95 | return array($n >> 32, $n & 0x00000000ffffffff); |
|
316 | } |
||
317 | |||
318 | /** |
||
319 | * Write a string up to 255 bytes long after encoding. |
||
320 | * Assume UTF-8 encoding |
||
321 | * |
||
322 | * @param $s |
||
323 | * @return $this |
||
324 | * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException |
||
325 | */ |
||
326 | 235 | public function write_shortstr($s) |
|
327 | { |
||
328 | 235 | $len = mb_strlen($s, 'ASCII'); |
|
329 | 235 | if ($len > 255) { |
|
330 | 10 | throw new AMQPInvalidArgumentException('String too long'); |
|
331 | } |
||
332 | |||
333 | 225 | $this->write_octet($len); |
|
334 | 225 | $this->out .= $s; |
|
335 | |||
336 | 225 | return $this; |
|
337 | } |
||
338 | |||
339 | /** |
||
340 | * Write a string up to 2**32 bytes long. Assume UTF-8 encoding |
||
341 | * |
||
342 | * @param $s |
||
343 | * @return $this |
||
344 | */ |
||
345 | 115 | public function write_longstr($s) |
|
346 | { |
||
347 | 115 | $this->write_long(mb_strlen($s, 'ASCII')); |
|
348 | 115 | $this->out .= $s; |
|
349 | |||
350 | 115 | return $this; |
|
351 | } |
||
352 | |||
353 | /** |
||
354 | * Supports the writing of Array types, so that you can implement |
||
355 | * array methods, like Rabbitmq's HA parameters |
||
356 | * |
||
357 | * @param AMQPArray|array $a Instance of AMQPArray or PHP array WITHOUT format hints (unlike write_table()) |
||
358 | * @return self |
||
359 | */ |
||
360 | 50 | public function write_array($a) |
|
361 | { |
||
362 | 50 | if (!($a instanceof AMQPArray)) { |
|
363 | 30 | $a = new AMQPArray($a); |
|
364 | 24 | } |
|
365 | 50 | $data = new AMQPWriter(); |
|
366 | |||
367 | 50 | foreach ($a as $v) { |
|
368 | 45 | $data->write_value($v[0], $v[1]); |
|
369 | 40 | } |
|
370 | |||
371 | 50 | $data = $data->getvalue(); |
|
372 | 50 | $this->write_long(mb_strlen($data, 'ASCII')); |
|
373 | 50 | $this->write($data); |
|
374 | |||
375 | 50 | return $this; |
|
376 | } |
||
377 | |||
378 | /** |
||
379 | * Write unix time_t value as 64 bit timestamp |
||
380 | * |
||
381 | * @param $v |
||
382 | * @return $this |
||
383 | */ |
||
384 | 5 | public function write_timestamp($v) |
|
385 | { |
||
386 | 5 | $this->write_longlong($v); |
|
387 | |||
388 | 5 | return $this; |
|
389 | } |
||
390 | |||
391 | /** |
||
392 | * Write PHP array, as table. Input array format: keys are strings, |
||
393 | * values are (type,value) tuples. |
||
394 | * |
||
395 | * @param AMQPTable|array $d Instance of AMQPTable or PHP array WITH format hints (unlike write_array()) |
||
396 | * @return self |
||
397 | * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException |
||
398 | */ |
||
399 | 160 | public function write_table($d) |
|
400 | { |
||
401 | 160 | $typeIsSym = !($d instanceof AMQPTable); //purely for back-compat purposes |
|
402 | |||
403 | 160 | $table_data = new AMQPWriter(); |
|
404 | 160 | foreach ($d as $k => $va) { |
|
405 | 125 | list($ftype, $v) = $va; |
|
406 | 125 | $table_data->write_shortstr($k); |
|
407 | 125 | $table_data->write_value($typeIsSym ? AMQPAbstractCollection::getDataTypeForSymbol($ftype) : $ftype, $v); |
|
408 | 124 | } |
|
409 | |||
410 | 155 | $table_data = $table_data->getvalue(); |
|
411 | 155 | $this->write_long(mb_strlen($table_data, 'ASCII')); |
|
412 | 155 | $this->write($table_data); |
|
413 | |||
414 | 155 | return $this; |
|
415 | } |
||
416 | |||
417 | /** |
||
418 | * for compat with method mapping used by AMQPMessage |
||
419 | */ |
||
420 | 40 | public function write_table_object($d) |
|
421 | { |
||
422 | 40 | return $this->write_table($d); |
|
423 | } |
||
424 | |||
425 | /** |
||
426 | * @param int $type One of AMQPAbstractCollection::T_* constants |
||
427 | * @param mixed $val |
||
428 | */ |
||
429 | 135 | private function write_value($type, $val) |
|
430 | { |
||
431 | //This will find appropriate symbol for given data type for currently selected protocol |
||
432 | //Also will raise an exception on unknown type |
||
433 | 135 | $this->write(AMQPAbstractCollection::getSymbolForDataType($type)); |
|
434 | |||
435 | switch ($type) { |
||
436 | 135 | case AMQPAbstractCollection::T_INT_SHORTSHORT: |
|
437 | 10 | $this->write_signed_octet($val); |
|
438 | 10 | break; |
|
439 | 135 | case AMQPAbstractCollection::T_INT_SHORTSHORT_U: |
|
440 | 10 | $this->write_octet($val); |
|
441 | 10 | break; |
|
442 | 135 | case AMQPAbstractCollection::T_INT_SHORT: |
|
443 | 10 | $this->write_signed_short($val); |
|
444 | 10 | break; |
|
445 | 135 | case AMQPAbstractCollection::T_INT_SHORT_U: |
|
446 | 10 | $this->write_short($val); |
|
447 | 10 | break; |
|
448 | 135 | case AMQPAbstractCollection::T_INT_LONG: |
|
449 | 55 | $this->write_signed_long($val); |
|
450 | 55 | break; |
|
451 | 120 | case AMQPAbstractCollection::T_INT_LONG_U: |
|
452 | $this->write_long($val); |
||
453 | break; |
||
454 | 120 | case AMQPAbstractCollection::T_INT_LONGLONG: |
|
455 | 15 | $this->write_signed_longlong($val); |
|
456 | 15 | break; |
|
457 | 120 | case AMQPAbstractCollection::T_INT_LONGLONG_U: |
|
458 | $this->write_longlong($val); |
||
459 | break; |
||
460 | 120 | case AMQPAbstractCollection::T_DECIMAL: |
|
461 | $this->write_octet($val->getE()); |
||
462 | $this->write_signed_long($val->getN()); |
||
463 | break; |
||
464 | 120 | case AMQPAbstractCollection::T_TIMESTAMP: |
|
465 | $this->write_timestamp($val); |
||
466 | break; |
||
467 | 120 | case AMQPAbstractCollection::T_BOOL: |
|
468 | 100 | $this->write_octet($val ? 1 : 0); |
|
469 | 100 | break; |
|
470 | 120 | case AMQPAbstractCollection::T_STRING_SHORT: |
|
471 | 10 | $this->write_shortstr($val); |
|
472 | 10 | break; |
|
473 | 120 | case AMQPAbstractCollection::T_STRING_LONG: |
|
474 | 110 | $this->write_longstr($val); |
|
475 | 110 | break; |
|
476 | 100 | case AMQPAbstractCollection::T_ARRAY: |
|
477 | 40 | $this->write_array($val); |
|
478 | 40 | break; |
|
479 | 80 | case AMQPAbstractCollection::T_TABLE: |
|
480 | 75 | $this->write_table($val); |
|
481 | 75 | break; |
|
482 | 5 | case AMQPAbstractCollection::T_VOID: |
|
483 | 5 | break; |
|
484 | default: |
||
485 | throw new AMQPInvalidArgumentException(sprintf( |
||
486 | 'Unsupported type "%s"', |
||
487 | $type |
||
488 | )); |
||
489 | } |
||
490 | 135 | } |
|
491 | } |
||
492 |
If you define a variable conditionally, it can happen that it is not defined for all execution paths.
Let’s take a look at an example:
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.
Available Fixes
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: