|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace PHPDaemon\Clients\AMQP\Driver\Protocol\v091\Parser; |
|
4
|
|
|
|
|
5
|
|
|
use PHPDaemon\Clients\AMQP\Driver\Protocol\Exception\AMQPProtocolException; |
|
6
|
|
|
|
|
7
|
|
|
/** |
|
8
|
|
|
* Parse an AMQP table from a string buffer. |
|
9
|
|
|
* |
|
10
|
|
|
* This implementation uses the field types as discussed in the AMQP 0.9.1 SIG, |
|
11
|
|
|
* (*NOT* the specification) along with Qpid's extensions. This parser is suitable |
|
12
|
|
|
* for use with RabbitMQ and Qpid. |
|
13
|
|
|
* |
|
14
|
|
|
* @see https://www.rabbitmq.com/amqp-0-9-1-errata.html#section_3 |
|
15
|
|
|
* |
|
16
|
|
|
* @see SpecTableParser for an implementation based on the AMQP 0.9.1 specification. |
|
17
|
|
|
* |
|
18
|
|
|
* Class Table |
|
19
|
|
|
* @author Aleksey I. Kuleshov YOU GLOBAL LIMITED |
|
20
|
|
|
* @package PHPDaemon\Clients\AMQP\Driver\Protocol\v091\Parser |
|
21
|
|
|
*/ |
|
22
|
|
|
class Table implements TableInterface |
|
23
|
|
|
{ |
|
24
|
|
|
use ScalarParserTrait; |
|
25
|
|
|
|
|
26
|
|
|
/** |
|
27
|
|
|
* @var string A buffer containing the table data. |
|
28
|
|
|
*/ |
|
29
|
|
|
private $buffer = ''; |
|
30
|
|
|
|
|
31
|
|
|
/** |
|
32
|
|
|
* Retrieve the next frame from the internal buffer. |
|
33
|
|
|
* |
|
34
|
|
|
* @param string &$buffer Binary data containing the table. |
|
35
|
|
|
* |
|
36
|
|
|
* @throws AMQPProtocolException if the incoming data does not conform to the |
|
37
|
|
|
* AMQP specification. |
|
38
|
|
|
* @return array |
|
39
|
|
|
*/ |
|
40
|
|
|
public function parse(&$buffer) |
|
41
|
|
|
{ |
|
42
|
|
|
$this->buffer = &$buffer; |
|
43
|
|
|
|
|
44
|
|
|
return $this->parseTable(); |
|
45
|
|
|
} |
|
46
|
|
|
|
|
47
|
|
|
/** |
|
48
|
|
|
* Parse an AMQP table from the head of the buffer. |
|
49
|
|
|
* @return array |
|
50
|
|
|
* @throws \PHPDaemon\Clients\AMQP\Driver\Protocol\Exception\AMQPProtocolException |
|
51
|
|
|
*/ |
|
52
|
|
View Code Duplication |
private function parseTable() |
|
|
|
|
|
|
53
|
|
|
{ |
|
54
|
|
|
$length = $this->parseUnsignedInt32(); |
|
55
|
|
|
$stopAt = strlen($this->buffer) - $length; |
|
56
|
|
|
$table = []; |
|
57
|
|
|
|
|
58
|
|
|
while (strlen($this->buffer) > $stopAt) { |
|
59
|
|
|
$table[$this->parseShortString()] = $this->parseField(); |
|
60
|
|
|
} |
|
61
|
|
|
|
|
62
|
|
|
return $table; |
|
63
|
|
|
} |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* Parse a table or array field. |
|
67
|
|
|
* |
|
68
|
|
|
* @return integer|array|boolean|double|string|null |
|
69
|
|
|
* @throws AMQPProtocolException |
|
70
|
|
|
*/ |
|
71
|
|
|
private function parseField() |
|
72
|
|
|
{ |
|
73
|
|
|
$type = $this->buffer[0]; |
|
74
|
|
|
$this->buffer = substr($this->buffer, 1); |
|
75
|
|
|
|
|
76
|
|
|
// @todo bench switch vs if vs method map |
|
|
|
|
|
|
77
|
|
|
switch ($type) { |
|
78
|
|
|
case 's': |
|
79
|
|
|
return $this->parseSignedInt16(); |
|
80
|
|
|
case 'l': |
|
81
|
|
|
return $this->parseSignedInt64(); |
|
82
|
|
|
case 'x': |
|
83
|
|
|
return $this->parseByteArray(); |
|
84
|
|
|
case 't': |
|
85
|
|
|
return $this->parseUnsignedInt8() !== 0; |
|
86
|
|
|
case 'b': |
|
87
|
|
|
return $this->parseSignedInt8(); |
|
88
|
|
|
case 'I': |
|
89
|
|
|
return $this->parseSignedInt32(); |
|
90
|
|
|
case 'f': |
|
91
|
|
|
return $this->parseFloat(); |
|
92
|
|
|
case 'd': |
|
93
|
|
|
return $this->parseDouble(); |
|
94
|
|
|
case 'D': |
|
95
|
|
|
return $this->parseDecimal(); |
|
96
|
|
|
case 'S': |
|
97
|
|
|
return $this->parseLongString(); |
|
98
|
|
|
case 'A': |
|
99
|
|
|
return $this->parseArray(); |
|
100
|
|
|
case 'T': |
|
101
|
|
|
return $this->parseUnsignedInt64(); |
|
102
|
|
|
case 'F': |
|
103
|
|
|
return $this->parseTable(); |
|
104
|
|
|
case 'V': |
|
105
|
|
|
return null; |
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
throw new AMQPProtocolException( |
|
109
|
|
|
sprintf( |
|
110
|
|
|
'table value type (0x%02x) is invalid or unrecognised.', |
|
111
|
|
|
ord($type) |
|
112
|
|
|
) |
|
113
|
|
|
); |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
/** |
|
117
|
|
|
* Parse an AMQP decimal from the head of the buffer. |
|
118
|
|
|
* @return string |
|
119
|
|
|
*/ |
|
120
|
|
|
public function parseDecimal() |
|
121
|
|
|
{ |
|
122
|
|
|
$scale = $this->parseUnsignedInt8(); |
|
123
|
|
|
$value = $this->parseSignedInt32(); |
|
124
|
|
|
|
|
125
|
|
|
if (0 === $scale) { |
|
126
|
|
|
return (string)$value; |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
if ($value >= 0) { |
|
130
|
|
|
$sign = ''; |
|
131
|
|
|
$value = (string)$value; |
|
132
|
|
|
} else { |
|
133
|
|
|
$sign = '-'; |
|
134
|
|
|
$value = (string)-$value; |
|
135
|
|
|
} |
|
136
|
|
|
|
|
137
|
|
|
$length = strlen($value); |
|
138
|
|
|
|
|
139
|
|
|
if ($length === $scale) { |
|
140
|
|
|
return $sign . '0.' . $value; |
|
141
|
|
|
} |
|
142
|
|
|
|
|
143
|
|
|
if ($length < $scale) { |
|
144
|
|
|
return $sign . '0.' . str_repeat('0', $scale - $length) . $value; |
|
145
|
|
|
} |
|
146
|
|
|
|
|
147
|
|
|
return $sign . substr($value, 0, -$scale) . '.' . substr($value, -$scale); |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
/** |
|
151
|
|
|
* Parse an AMQP field-array value. |
|
152
|
|
|
* @return array |
|
153
|
|
|
* @throws \PHPDaemon\Clients\AMQP\Driver\Protocol\Exception\AMQPProtocolException |
|
154
|
|
|
*/ |
|
155
|
|
View Code Duplication |
private function parseArray() |
|
|
|
|
|
|
156
|
|
|
{ |
|
157
|
|
|
$length = $this->parseUnsignedInt32(); |
|
158
|
|
|
$stopAt = strlen($this->buffer) - $length; |
|
159
|
|
|
$array = []; |
|
160
|
|
|
|
|
161
|
|
|
while (strlen($this->buffer) > $stopAt) { |
|
162
|
|
|
$array[] = $this->parseField(); |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
return $array; |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
/** |
|
169
|
|
|
* Parse an AMQP byte-array value. |
|
170
|
|
|
* @return array |
|
|
|
|
|
|
171
|
|
|
*/ |
|
172
|
|
View Code Duplication |
private function parseByteArray() |
|
|
|
|
|
|
173
|
|
|
{ |
|
174
|
|
|
list(, $length) = unpack('N', $this->buffer); |
|
175
|
|
|
|
|
176
|
|
|
try { |
|
177
|
|
|
return substr($this->buffer, 4, $length); |
|
178
|
|
|
} finally { |
|
179
|
|
|
$this->buffer = substr($this->buffer, $length + 4); |
|
180
|
|
|
} |
|
181
|
|
|
} |
|
182
|
|
|
} |
|
183
|
|
|
|
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.