├── CHANGELOG.md
├── History.md
├── LICENSE
├── PhpAmqpLib
├── Channel
│ ├── AMQPChannel.php
│ ├── AbstractChannel.php
│ ├── Frame.php
│ └── Method.php
├── Connection
│ ├── AMQPConnectionConfig.php
│ ├── AMQPConnectionFactory.php
│ ├── AMQPLazyConnection.php
│ ├── AMQPLazySSLConnection.php
│ ├── AMQPLazySocketConnection.php
│ ├── AMQPSSLConnection.php
│ ├── AMQPSocketConnection.php
│ ├── AMQPStreamConnection.php
│ ├── AbstractConnection.php
│ └── Heartbeat
│ │ ├── AbstractSignalHeartbeatSender.php
│ │ ├── PCNTLHeartbeatSender.php
│ │ └── SIGHeartbeatSender.php
├── Exception
│ ├── AMQPBasicCancelException.php
│ ├── AMQPChannelClosedException.php
│ ├── AMQPConnectionBlockedException.php
│ ├── AMQPConnectionClosedException.php
│ ├── AMQPDataReadException.php
│ ├── AMQPEmptyDeliveryTagException.php
│ ├── AMQPExceptionInterface.php
│ ├── AMQPHeartbeatMissedException.php
│ ├── AMQPIOException.php
│ ├── AMQPIOWaitException.php
│ ├── AMQPInvalidArgumentException.php
│ ├── AMQPInvalidFrameException.php
│ ├── AMQPLogicException.php
│ ├── AMQPNoDataException.php
│ ├── AMQPNotImplementedException.php
│ ├── AMQPOutOfBoundsException.php
│ ├── AMQPOutOfRangeException.php
│ ├── AMQPProtocolChannelException.php
│ ├── AMQPProtocolException.php
│ ├── AMQPRuntimeException.php
│ ├── AMQPSocketException.php
│ └── AMQPTimeoutException.php
├── Exchange
│ └── AMQPExchangeType.php
├── Helper
│ ├── Assert.php
│ ├── BigInteger.php
│ ├── DebugHelper.php
│ ├── MiscHelper.php
│ ├── Protocol
│ │ ├── MethodMap080.php
│ │ ├── MethodMap091.php
│ │ ├── Protocol080.php
│ │ ├── Protocol091.php
│ │ ├── Wait080.php
│ │ └── Wait091.php
│ └── SocketConstants.php
├── Message
│ └── AMQPMessage.php
├── Package.php
└── Wire
│ ├── AMQPAbstractCollection.php
│ ├── AMQPArray.php
│ ├── AMQPBufferReader.php
│ ├── AMQPByteStream.php
│ ├── AMQPDecimal.php
│ ├── AMQPIOReader.php
│ ├── AMQPReader.php
│ ├── AMQPTable.php
│ ├── AMQPWriter.php
│ ├── Constants.php
│ ├── Constants080.php
│ ├── Constants091.php
│ └── IO
│ ├── AbstractIO.php
│ ├── SocketIO.php
│ └── StreamIO.php
├── README.md
├── codecov.yml
├── composer.json
├── phpcs.xml.dist
└── test.env
/History.md:
--------------------------------------------------------------------------------
1 | # Previous releases
2 |
3 | ## 2.7.2 - 2018-02-11
4 |
5 | [GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/5?closed=1)
6 |
7 | - PHP `5.3` compatibility [PR](https://github.com/php-amqplib/php-amqplib/issues/539)
8 |
9 | ## 2.7.1 - 2018-02-01
10 |
11 | - Support PHPUnit 6 [PR](https://github.com/php-amqplib/php-amqplib/pull/530)
12 | - Use `tcp_nodelay` for `StreamIO` [PR](https://github.com/php-amqplib/php-amqplib/pull/517)
13 | - Pass connection timeout to `wait` method [PR](https://github.com/php-amqplib/php-amqplib/pull/512)
14 | - Fix possible indefinite waiting for data in StreamIO [PR](https://github.com/php-amqplib/php-amqplib/pull/423), [PR](https://github.com/php-amqplib/php-amqplib/pull/534)
15 | - Change protected method check_heartbeat to public [PR](https://github.com/php-amqplib/php-amqplib/pull/520)
16 | - Ensure access levels are consistent for calling `check_heartbeat` [PR](https://github.com/php-amqplib/php-amqplib/pull/535)
17 |
18 | ## 2.7.0 - 2017-09-20
19 |
20 | ### Added
21 | - Increased overall test coverage
22 | - Bring heartbeat support to socket connection
23 | - Add message delivery tag for publisher confirms
24 | - Add support for serializing DateTimeImmutable objects
25 |
26 | ### Fixed
27 | - Fixed infinite loop on reconnect - check_heartbeat
28 | - Fixed signal handling exit example
29 | - Fixed exchange_unbind arguments
30 | - Fixed invalid annotation for channel_id
31 | - Fixed socket null error on php 5.3 version
32 | - Fixed timeout parameters on HHVM before calling stream_select
33 |
34 | ### Changed
35 | - declare(ticks=1) no longer needed after PHP5.3 / amqplib 2.4.1
36 | - Minor DebugHelper improvements
37 |
38 | ### Enhancements
39 | - Add extensions requirements to README.md
40 | - Add PHP 7.1 to Travis build
41 | - Reduce memory usage in StreamIO::write()
42 | - Re-enable heartbeats after reconnection
43 |
44 | ## 2.6.3 - 2016-04-11
45 |
46 | ### Added
47 | - Added the ability to set timeout as float
48 |
49 | ### Fixed
50 | - Fixed restoring of error_handler on connection error
51 |
52 | ### Enhancements
53 | - Verify read_write_timeout is at least 2x the heartbeat (if set)
54 | - Many PHPDoc fixes
55 | - Throw exception when trying to create an exchange on a closed connection
56 |
57 | ## 2.6.2 - 2016-03-02
58 |
59 | ### Added
60 | - Added AMQPLazySocketConnection
61 | - AbstractConnection::getServerProperties method to retrieve server properties.
62 | - AMQPReader::wait() will throw IOWaitException on stream_select failure
63 | - Add PHPDocs to Auto-generated Protocol Classes
64 |
65 | ### Fixed
66 | - Disable heartbeat when closing connection
67 | - Fix for when the default error handler is not restored in StreamIO
68 |
69 | ### Enhancements
70 | - Cleanup tests and improve testing performance
71 | - Confirm received valid frame type on wait_frame in AbstractConnection
72 | - Update DEMO files closer to PSR-2 standards
73 |
74 | ## 2.6.1 - 2016-02-12
75 |
76 | ### Added
77 | - Add constants for delivery modes to AMQPMessage
78 |
79 | ### Fixed
80 | - Fix some PHPDoc problems
81 | - AbstractCollection value de/encoding on PHP7
82 | - StreamIO: fix "bad write retry" in SSL mode
83 |
84 | ### Enhancements
85 | - Update PHPUnit configuration
86 | - Add scrutinizer-ci configuration
87 | - Organizational changes from videlalvaro to php-amqplib org
88 | - Minor complexity optimizations, code organization, and code cleanup
89 |
90 | ## 2.6.0 - 2015-09-23
91 |
92 | ### BC Breaking Changes
93 | - The `AMQPStreamConnection` class now throws `ErrorExceptions` when errors happen while reading/writing to the network.
94 |
95 | ### Added
96 | - Heartbeat frames will decrease the timeout used when calling wait_channel - heartbeat frames do not reset the timeout
97 |
98 | ### Fixed
99 | - Declared the class AbstractChannel as being an abstract class
100 | - Reads, writes and signals respond immediately instead of waiting for a timeout
101 | - Fatal error in some cases on Channel.wait with timeout when RabbitMQ restarted
102 | - Remove warning when trying to push a deferred frame
103 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Channel/Frame.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Channel;
4 | use PhpAmqpLib\Wire\AMQPReader;
5 |
6 | /**
7 | * @link https://livebook.manning.com/book/rabbitmq-in-depth/chapter-2/v-13/22
8 | * @link https://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf 4.2.6 Content Framing
9 | */
10 | final class Frame
11 | {
12 | public const FRAME_HEADER_SIZE = AMQPReader::OCTET + AMQPReader::SHORT + AMQPReader::LONG;
13 | public const END = 0xCE;
14 |
15 | public const TYPE_METHOD = 1;
16 | public const TYPE_HEADER = 2;
17 | public const TYPE_BODY = 3;
18 | public const TYPE_HEARTBEAT = 8;
19 |
20 | /** @var int */
21 | private $type;
22 |
23 | /** @var int */
24 | private $channel;
25 |
26 | /** @var int */
27 | private $size;
28 |
29 | /** @var string|null */
30 | private $payload;
31 |
32 | public function __construct(int $type, int $channel, int $size, ?string $payload = null)
33 | {
34 | $this->type = $type;
35 | $this->channel = $channel;
36 | $this->size = $size;
37 | $this->payload = $payload;
38 | }
39 |
40 | /**
41 | * @return int
42 | */
43 | public function getType(): int
44 | {
45 | return $this->type;
46 | }
47 |
48 | /**
49 | * @return int
50 | */
51 | public function getChannel(): int
52 | {
53 | return $this->channel;
54 | }
55 |
56 | /**
57 | * @return int
58 | */
59 | public function getSize(): int
60 | {
61 | return $this->size;
62 | }
63 |
64 | public function getPayload(): ?string
65 | {
66 | return $this->payload;
67 | }
68 |
69 | public function isMethod(): bool
70 | {
71 | return $this->type === self::TYPE_METHOD;
72 | }
73 |
74 | public function isHeartbeat(): bool
75 | {
76 | return $this->type === self::TYPE_HEARTBEAT;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Channel/Method.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Channel;
4 |
5 | final class Method
6 | {
7 | /** @var int */
8 | private $class;
9 |
10 | /** @var int */
11 | private $method;
12 |
13 | /** @var string */
14 | private $arguments;
15 |
16 | public function __construct(int $class, int $method, string $arguments)
17 | {
18 | $this->class = $class;
19 | $this->method = $method;
20 | $this->arguments = $arguments;
21 | }
22 |
23 | public function getClass(): int
24 | {
25 | return $this->class;
26 | }
27 |
28 | public function getMethod(): int
29 | {
30 | return $this->method;
31 | }
32 |
33 | public function getArguments(): string
34 | {
35 | return $this->arguments;
36 | }
37 |
38 | public function getSignature(): string
39 | {
40 | return $this->class . ',' . $this->method;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/AMQPConnectionConfig.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Connection;
4 |
5 | use InvalidArgumentException;
6 | use PhpAmqpLib\Wire;
7 |
8 | /**
9 | * @since 3.2.0
10 | */
11 | final class AMQPConnectionConfig
12 | {
13 | public const AUTH_PLAIN = 'PLAIN';
14 | public const AUTH_AMQPPLAIN = 'AMQPLAIN';
15 | public const AUTH_EXTERNAL = 'EXTERNAL';
16 | public const IO_TYPE_STREAM = 'stream';
17 | public const IO_TYPE_SOCKET = 'socket';
18 |
19 | /** @var string */
20 | private $ioType = self::IO_TYPE_STREAM;
21 |
22 | /** @var bool */
23 | private $isLazy = false;
24 |
25 | /** @var string */
26 | private $host = '127.0.0.1';
27 |
28 | /** @var int */
29 | private $port = 5672;
30 |
31 | /** @var string */
32 | private $user = 'guest';
33 |
34 | /** @var string */
35 | private $password = 'guest';
36 |
37 | /** @var string */
38 | private $vhost = '/';
39 |
40 | /** @var bool */
41 | private $insist = false;
42 |
43 | /** @var string */
44 | private $loginMethod = self::AUTH_AMQPPLAIN;
45 |
46 | /** @var string|null */
47 | private $loginResponse;
48 |
49 | /** @var string */
50 | private $locale = 'en_US';
51 |
52 | /** @var float */
53 | private $connectionTimeout = 3.0;
54 |
55 | /** @var float */
56 | private $readTimeout = 3.0;
57 |
58 | /** @var float */
59 | private $writeTimeout = 3.0;
60 |
61 | /** @var float */
62 | private $channelRPCTimeout = 0.0;
63 |
64 | /** @var int */
65 | private $heartbeat = 0;
66 |
67 | /** @var bool */
68 | private $keepalive = false;
69 |
70 | /** @var bool */
71 | private $isSecure = false;
72 |
73 | /**
74 | * @deprecated Use sslCryptoMethod
75 | * @var string
76 | */
77 | private $networkProtocol = 'tcp';
78 |
79 | /** @var resource|null */
80 | private $streamContext;
81 |
82 | /** @var int */
83 | private $sendBufferSize = 0;
84 |
85 | /** @var bool */
86 | private $dispatchSignals = true;
87 |
88 | /**
89 | * @var string
90 | * @deprecated
91 | */
92 | private $amqpProtocol = Wire\Constants091::VERSION;
93 |
94 | /**
95 | * Whether to use strict AMQP0.9.1 field types. RabbitMQ does not support that.
96 | * @var bool
97 | */
98 | private $protocolStrictFields = false;
99 |
100 | /** @var string|null */
101 | private $sslCaCert;
102 |
103 | /**
104 | * @var string|null
105 | */
106 | private $sslCaPath;
107 |
108 | /** @var string|null */
109 | private $sslCert;
110 |
111 | /** @var string|null */
112 | private $sslKey;
113 |
114 | /** @var bool|null */
115 | private $sslVerify;
116 |
117 | /** @var bool|null */
118 | private $sslVerifyName;
119 |
120 | /** @var string|null */
121 | private $sslPassPhrase;
122 |
123 | /** @var string|null */
124 | private $sslCiphers;
125 |
126 | /** @var int|null */
127 | private $sslSecurityLevel;
128 |
129 | /** @var int|null */
130 | private $sslCryptoMethod;
131 |
132 | /** @var string */
133 | private $connectionName = '';
134 |
135 | /**
136 | * Output all networks packets for debug purposes.
137 | * @var bool
138 | */
139 | private $debugPackets = false;
140 |
141 | public function getIoType(): string
142 | {
143 | return $this->ioType;
144 | }
145 |
146 | /**
147 | * Set which IO type will be used, stream or socket.
148 | * @param string $ioType
149 | */
150 | public function setIoType(string $ioType): void
151 | {
152 | if ($ioType !== self::IO_TYPE_STREAM && $ioType !== self::IO_TYPE_SOCKET) {
153 | throw new InvalidArgumentException('IO type can be either "stream" or "socket"');
154 | }
155 | $this->ioType = $ioType;
156 | }
157 |
158 | public function isLazy(): bool
159 | {
160 | return $this->isLazy;
161 | }
162 |
163 | public function setIsLazy(bool $isLazy): void
164 | {
165 | $this->isLazy = $isLazy;
166 | }
167 |
168 | public function getHost(): string
169 | {
170 | return $this->host;
171 | }
172 |
173 | public function setHost(string $host): void
174 | {
175 | $this->host = $host;
176 | }
177 |
178 | public function getPort(): int
179 | {
180 | return $this->port;
181 | }
182 |
183 | public function setPort(int $port): void
184 | {
185 | if ($port <= 0) {
186 | throw new InvalidArgumentException('Port number must be greater than 0');
187 | }
188 | $this->port = $port;
189 | }
190 |
191 | public function getUser(): string
192 | {
193 | return $this->user;
194 | }
195 |
196 | public function setUser(string $user): void
197 | {
198 | $this->user = $user;
199 | }
200 |
201 | public function getPassword(): string
202 | {
203 | return $this->password;
204 | }
205 |
206 | public function setPassword(string $password): void
207 | {
208 | $this->password = $password;
209 | }
210 |
211 | public function getVhost(): string
212 | {
213 | return $this->vhost;
214 | }
215 |
216 | public function setVhost(string $vhost): void
217 | {
218 | self::assertStringNotEmpty($vhost, 'vhost');
219 | $this->vhost = $vhost;
220 | }
221 |
222 | public function isInsist(): bool
223 | {
224 | return $this->insist;
225 | }
226 |
227 | public function setInsist(bool $insist): void
228 | {
229 | $this->insist = $insist;
230 | }
231 |
232 | public function getLoginMethod(): string
233 | {
234 | return $this->loginMethod;
235 | }
236 |
237 | public function setLoginMethod(string $loginMethod): void
238 | {
239 | if (
240 | $loginMethod !== self::AUTH_PLAIN
241 | && $loginMethod !== self::AUTH_AMQPPLAIN
242 | && $loginMethod !== self::AUTH_EXTERNAL
243 | ) {
244 | throw new InvalidArgumentException('Unknown login method: ' . $loginMethod);
245 | }
246 | if ($loginMethod === self::AUTH_EXTERNAL && (!empty($this->user) || !empty($this->password))) {
247 | throw new InvalidArgumentException('External auth method cannot be used together with user credentials.');
248 | }
249 | $this->loginMethod = $loginMethod;
250 | }
251 |
252 | public function getLoginResponse(): ?string
253 | {
254 | return $this->loginResponse;
255 | }
256 |
257 | public function setLoginResponse(string $loginResponse): void
258 | {
259 | $this->loginResponse = $loginResponse;
260 | }
261 |
262 | public function getLocale(): string
263 | {
264 | return $this->locale;
265 | }
266 |
267 | public function setLocale(string $locale): void
268 | {
269 | self::assertStringNotEmpty($locale, 'locale');
270 | $this->locale = $locale;
271 | }
272 |
273 | public function getConnectionTimeout(): float
274 | {
275 | return $this->connectionTimeout;
276 | }
277 |
278 | public function setConnectionTimeout(float $connectionTimeout): void
279 | {
280 | $this->connectionTimeout = $connectionTimeout;
281 | }
282 |
283 | public function getReadTimeout(): float
284 | {
285 | return $this->readTimeout;
286 | }
287 |
288 | public function setReadTimeout(float $readTimeout): void
289 | {
290 | self::assertGreaterOrEq($readTimeout, 0, 'read timeout');
291 | $this->readTimeout = $readTimeout;
292 | }
293 |
294 | public function getWriteTimeout(): float
295 | {
296 | return $this->writeTimeout;
297 | }
298 |
299 | public function setWriteTimeout(float $writeTimeout): void
300 | {
301 | self::assertGreaterOrEq($writeTimeout, 0, 'write timeout');
302 | $this->writeTimeout = $writeTimeout;
303 | }
304 |
305 | public function getChannelRPCTimeout(): float
306 | {
307 | return $this->channelRPCTimeout;
308 | }
309 |
310 | public function setChannelRPCTimeout(float $channelRPCTimeout): void
311 | {
312 | self::assertGreaterOrEq($channelRPCTimeout, 0, 'channel RPC timeout');
313 | $this->channelRPCTimeout = $channelRPCTimeout;
314 | }
315 |
316 | public function getHeartbeat(): int
317 | {
318 | return $this->heartbeat;
319 | }
320 |
321 | public function setHeartbeat(int $heartbeat): void
322 | {
323 | self::assertGreaterOrEq($heartbeat, 0, 'heartbeat');
324 | $this->heartbeat = $heartbeat;
325 | }
326 |
327 | public function isKeepalive(): bool
328 | {
329 | return $this->keepalive;
330 | }
331 |
332 | public function setKeepalive(bool $keepalive): void
333 | {
334 | $this->keepalive = $keepalive;
335 | }
336 |
337 | public function isSecure(): bool
338 | {
339 | return $this->isSecure;
340 | }
341 |
342 | public function setIsSecure(bool $isSecure): void
343 | {
344 | $this->isSecure = $isSecure;
345 |
346 | if ($this->isSecure) {
347 | $this->networkProtocol = 'tls';
348 | $this->sslCryptoMethod = STREAM_CRYPTO_METHOD_ANY_CLIENT;
349 | } else {
350 | $this->networkProtocol = 'tcp';
351 | $this->sslCryptoMethod = null;
352 | }
353 | }
354 |
355 | /**
356 | * @deprecated Use getSslCryptoMethod()
357 | */
358 | public function getNetworkProtocol(): string
359 | {
360 | return $this->networkProtocol;
361 | }
362 |
363 | /**
364 | * @deprecated Use setIsSecure() and setSslCryptoMethod()
365 | */
366 | public function setNetworkProtocol(string $networkProtocol): void
367 | {
368 | self::assertStringNotEmpty($networkProtocol, 'network protocol');
369 | $this->networkProtocol = $networkProtocol;
370 | }
371 |
372 | /**
373 | * @return resource|null
374 | */
375 | public function getStreamContext()
376 | {
377 | return $this->streamContext;
378 | }
379 |
380 | /**
381 | * @param resource|null $streamContext
382 | */
383 | public function setStreamContext($streamContext): void
384 | {
385 | if ($streamContext === null) {
386 | $this->streamContext = null;
387 | return;
388 | }
389 |
390 | if (!is_resource($streamContext) || get_resource_type($streamContext) !== 'stream-context') {
391 | throw new InvalidArgumentException('Resource must be valid stream context');
392 | }
393 | $this->streamContext = $streamContext;
394 | }
395 |
396 | /**
397 | * @return int
398 | * @since 3.2.1
399 | */
400 | public function getSendBufferSize(): int
401 | {
402 | return $this->sendBufferSize;
403 | }
404 |
405 | /**
406 | * Socket send buffer size. Set 0 to keep system default.
407 | * @param int $sendBufferSize
408 | * @return void
409 | * @since 3.2.1
410 | */
411 | public function setSendBufferSize(int $sendBufferSize): void
412 | {
413 | self::assertGreaterOrEq($sendBufferSize, 0, 'sendBufferSize');
414 | $this->sendBufferSize = $sendBufferSize;
415 | }
416 |
417 | public function isSignalsDispatchEnabled(): bool
418 | {
419 | return $this->dispatchSignals;
420 | }
421 |
422 | public function enableSignalDispatch(bool $dispatchSignals): void
423 | {
424 | $this->dispatchSignals = $dispatchSignals;
425 | }
426 |
427 | /**
428 | * @return string
429 | * @deprecated
430 | */
431 | public function getAMQPProtocol(): string
432 | {
433 | return $this->amqpProtocol;
434 | }
435 |
436 | /**
437 | * @param string $protocol
438 | * @deprecated
439 | */
440 | public function setAMQPProtocol(string $protocol): void
441 | {
442 | if ($protocol !== Wire\Constants091::VERSION && $protocol !== Wire\Constants080::VERSION) {
443 | throw new InvalidArgumentException('AMQP protocol can be either "0.9.1" or "8.0"');
444 | }
445 | $this->amqpProtocol = $protocol;
446 | }
447 |
448 | public function isProtocolStrictFieldsEnabled(): bool
449 | {
450 | return $this->protocolStrictFields;
451 | }
452 |
453 | public function setProtocolStrictFields(bool $protocolStrictFields): void
454 | {
455 | $this->protocolStrictFields = $protocolStrictFields;
456 | }
457 |
458 | public function getSslCaCert(): ?string
459 | {
460 | return $this->sslCaCert;
461 | }
462 |
463 | public function setSslCaCert(?string $sslCaCert): void
464 | {
465 | $this->sslCaCert = $sslCaCert;
466 | }
467 |
468 | public function getSslCaPath(): ?string
469 | {
470 | return $this->sslCaPath;
471 | }
472 |
473 | public function setSslCaPath(?string $sslCaPath): void
474 | {
475 | $this->sslCaPath = $sslCaPath;
476 | }
477 |
478 | public function getSslCert(): ?string
479 | {
480 | return $this->sslCert;
481 | }
482 |
483 | public function setSslCert(?string $sslCert): void
484 | {
485 | $this->sslCert = $sslCert;
486 | }
487 |
488 | public function getSslKey(): ?string
489 | {
490 | return $this->sslKey;
491 | }
492 |
493 | public function setSslKey(?string $sslKey): void
494 | {
495 | $this->sslKey = $sslKey;
496 | }
497 |
498 | public function getSslVerify(): ?bool
499 | {
500 | return $this->sslVerify;
501 | }
502 |
503 | public function setSslVerify(?bool $sslVerify): void
504 | {
505 | $this->sslVerify = $sslVerify;
506 |
507 | if (!$this->sslVerify) {
508 | $this->setSslVerifyName(false);
509 | }
510 | }
511 |
512 | public function getSslVerifyName(): ?bool
513 | {
514 | return $this->sslVerifyName;
515 | }
516 |
517 | public function setSslVerifyName(?bool $sslVerifyName): void
518 | {
519 | $this->sslVerifyName = $sslVerifyName;
520 | }
521 |
522 | public function getSslPassPhrase(): ?string
523 | {
524 | return $this->sslPassPhrase;
525 | }
526 |
527 | public function setSslPassPhrase(?string $sslPassPhrase): void
528 | {
529 | $this->sslPassPhrase = $sslPassPhrase;
530 | }
531 |
532 | public function getSslCiphers(): ?string
533 | {
534 | return $this->sslCiphers;
535 | }
536 |
537 | public function setSslCiphers(?string $sslCiphers): void
538 | {
539 | $this->sslCiphers = $sslCiphers;
540 | }
541 |
542 | public function getSslSecurityLevel(): ?int
543 | {
544 | return $this->sslSecurityLevel;
545 | }
546 |
547 | public function setSslSecurityLevel(?int $sslSecurityLevel): void
548 | {
549 | $this->sslSecurityLevel = $sslSecurityLevel;
550 | }
551 |
552 | public function getSslCryptoMethod(): ?int
553 | {
554 | return $this->sslCryptoMethod;
555 | }
556 |
557 | public function setSslCryptoMethod(?int $sslCryptoMethod): void
558 | {
559 | $this->sslCryptoMethod = $sslCryptoMethod;
560 | }
561 |
562 | public function isDebugPackets(): bool
563 | {
564 | return $this->debugPackets;
565 | }
566 |
567 | public function setDebugPackets(bool $debugPackets): void
568 | {
569 | $this->debugPackets = $debugPackets;
570 | }
571 |
572 | private static function assertStringNotEmpty($value, string $param): void
573 | {
574 | $value = trim($value);
575 | if (empty($value)) {
576 | throw new InvalidArgumentException(sprintf('Parameter "%s" must be non empty string', $param));
577 | }
578 | }
579 |
580 | /**
581 | * @param int|float $value
582 | * @param int $limit
583 | * @param string $param
584 | */
585 | private static function assertGreaterOrEq($value, int $limit, string $param): void
586 | {
587 | if ($value < $limit) {
588 | throw new InvalidArgumentException(sprintf('Parameter "%s" must be greater than zero', $param));
589 | }
590 | }
591 |
592 | /**
593 | * @return string
594 | */
595 | public function getConnectionName(): string
596 | {
597 | return $this->connectionName;
598 | }
599 |
600 | /**
601 | * @param string $connectionName
602 | */
603 | public function setConnectionName(string $connectionName): void
604 | {
605 | $this->connectionName = $connectionName;
606 | }
607 | }
608 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/AMQPConnectionFactory.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Connection;
4 |
5 | use LogicException;
6 |
7 | /**
8 | * @since 3.2.0
9 | */
10 | class AMQPConnectionFactory
11 | {
12 | public static function create(AMQPConnectionConfig $config): AbstractConnection
13 | {
14 | if ($config->getIoType() === AMQPConnectionConfig::IO_TYPE_STREAM) {
15 | $connection = new AMQPStreamConnection(
16 | $config->getHost(),
17 | $config->getPort(),
18 | $config->getUser(),
19 | $config->getPassword(),
20 | $config->getVhost(),
21 | $config->isInsist(),
22 | $config->getLoginMethod(),
23 | $config->getLoginResponse(),
24 | $config->getLocale(),
25 | $config->getConnectionTimeout(),
26 | self::getReadWriteTimeout($config),
27 | self::getStreamContext($config),
28 | $config->isKeepalive(),
29 | $config->getHeartbeat(),
30 | $config->getChannelRPCTimeout(),
31 | $config
32 | );
33 | } else {
34 | if ($config->isSecure()) {
35 | throw new LogicException('The socket connection implementation does not support secure connections.');
36 | }
37 |
38 | $connection = new AMQPSocketConnection(
39 | $config->getHost(),
40 | $config->getPort(),
41 | $config->getUser(),
42 | $config->getPassword(),
43 | $config->getVhost(),
44 | $config->isInsist(),
45 | $config->getLoginMethod(),
46 | $config->getLoginResponse(),
47 | $config->getLocale(),
48 | $config->getReadTimeout(),
49 | $config->isKeepalive(),
50 | $config->getWriteTimeout(),
51 | $config->getHeartbeat(),
52 | $config->getChannelRPCTimeout(),
53 | $config
54 | );
55 | }
56 |
57 | return $connection;
58 | }
59 |
60 | private static function getReadWriteTimeout(AMQPConnectionConfig $config): float
61 | {
62 | return min($config->getReadTimeout(), $config->getWriteTimeout());
63 | }
64 |
65 | /**
66 | * @param AMQPConnectionConfig $config
67 | * @return string[]
68 | */
69 | private static function getSslOptions(AMQPConnectionConfig $config): array
70 | {
71 | return array_filter([
72 | 'cafile' => $config->getSslCaCert(),
73 | 'capath' => $config->getSslCaPath(),
74 | 'local_cert' => $config->getSslCert(),
75 | 'local_pk' => $config->getSslKey(),
76 | 'verify_peer' => $config->getSslVerify(),
77 | 'verify_peer_name' => $config->getSslVerifyName(),
78 | 'passphrase' => $config->getSslPassPhrase(),
79 | 'ciphers' => $config->getSslCiphers(),
80 | 'security_level' => $config->getSslSecurityLevel(),
81 | 'crypto_method' => $config->getSslCryptoMethod(),
82 | ], static function ($value) {
83 | return null !== $value;
84 | });
85 | }
86 |
87 | /**
88 | * @param AMQPConnectionConfig $config
89 | * @return resource|null
90 | */
91 | private static function getStreamContext(AMQPConnectionConfig $config)
92 | {
93 | $context = $config->getStreamContext();
94 |
95 | if ($config->isSecure()) {
96 | if (!$context) {
97 | $context = stream_context_create();
98 | }
99 | $options = self::getSslOptions($config);
100 | foreach ($options as $k => $v) {
101 | // Note: 'ssl' applies to 'tls' as well
102 | // https://www.php.net/manual/en/context.ssl.php
103 | stream_context_set_option($context, 'ssl', $k, $v);
104 | }
105 | }
106 |
107 | return $context;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/AMQPLazyConnection.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Connection;
4 |
5 | /**
6 | * @deprecated AMQPStreamConnection can be lazy too. Use AMQPConnectionFactory with AMQPConnectionConfig::setIsLazy(true)
7 | */
8 | class AMQPLazyConnection extends AMQPStreamConnection
9 | {
10 | /**
11 | * @inheritDoc
12 | */
13 | public function connectOnConstruct(): bool
14 | {
15 | return false;
16 | }
17 |
18 | /**
19 | * @param string[][] $hosts
20 | * @param string[] $options
21 | * @return self
22 | * @throws \Exception
23 | * @deprecated Use ConnectionFactory
24 | */
25 | public static function create_connection($hosts, $options = array())
26 | {
27 | if (count($hosts) > 1) {
28 | throw new \RuntimeException('Lazy connection does not support multiple hosts');
29 | }
30 |
31 | return parent::create_connection($hosts, $options);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/AMQPLazySSLConnection.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Connection;
4 |
5 | /**
6 | * @deprecated AMQPLazySSLConnection can be lazy too. Use AMQPConnectionFactory with AMQPConnectionConfig::setIsLazy(true)
7 | */
8 | class AMQPLazySSLConnection extends AMQPSSLConnection
9 | {
10 | /**
11 | * @inheritDoc
12 | */
13 | public function connectOnConstruct(): bool
14 | {
15 | return false;
16 | }
17 |
18 | /**
19 | * @param string[][] $hosts
20 | * @param string[] $options
21 | * @return self
22 | * @throws \Exception
23 | * @deprecated Use ConnectionFactory
24 | */
25 | public static function create_connection($hosts, $options = array())
26 | {
27 | if (count($hosts) > 1) {
28 | throw new \RuntimeException('Lazy connection does not support multiple hosts');
29 | }
30 |
31 | return parent::create_connection($hosts, $options);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/AMQPLazySocketConnection.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Connection;
4 |
5 | /**
6 | * @deprecated AMQPSocketConnection can be lazy too. Use AMQPConnectionFactory with AMQPConnectionConfig::setIsLazy(true)
7 | */
8 | class AMQPLazySocketConnection extends AMQPSocketConnection
9 | {
10 | /**
11 | * @inheritDoc
12 | */
13 | public function connectOnConstruct(): bool
14 | {
15 | return false;
16 | }
17 |
18 | /**
19 | * @param string[][] $hosts
20 | * @param string[] $options
21 | * @return self
22 | * @throws \Exception
23 | * @deprecated Use ConnectionFactory
24 | */
25 | public static function create_connection($hosts, $options = array())
26 | {
27 | if (count($hosts) > 1) {
28 | throw new \RuntimeException('Lazy connection does not support multiple hosts');
29 | }
30 |
31 | return parent::create_connection($hosts, $options);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/AMQPSSLConnection.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Connection;
4 |
5 | /**
6 | * @deprecated Use AMQPConnectionFactory with AMQPConnectionConfig::setIsSecure(true) and AMQPConnectionConfig::setSsl* methods.
7 | */
8 | class AMQPSSLConnection extends AMQPStreamConnection
9 | {
10 | /**
11 | * @param string $host
12 | * @param int $port
13 | * @param string $user
14 | * @param string $password
15 | * @param string $vhost
16 | * @param array $ssl_options
17 | * @param array $options
18 | * @param AMQPConnectionConfig|null $config
19 | * @throws \Exception
20 | */
21 | public function __construct(
22 | $host,
23 | $port,
24 | $user,
25 | $password,
26 | $vhost = '/',
27 | $ssl_options = array(),
28 | $options = array(),
29 | ?AMQPConnectionConfig $config = null
30 | ) {
31 | trigger_error('AMQPSSLConnection is deprecated and will be removed in version 4 of php-amqplib', E_USER_DEPRECATED);
32 | $ssl_context = null;
33 | if (!empty($ssl_options)) {
34 | $ssl_context = $this->createSslContext($ssl_options);
35 | }
36 |
37 | parent::__construct(
38 | $host,
39 | $port,
40 | $user,
41 | $password,
42 | $vhost,
43 | isset($options['insist']) ? $options['insist'] : false,
44 | isset($options['login_method']) ? $options['login_method'] : 'AMQPLAIN',
45 | isset($options['login_response']) ? $options['login_response'] : null,
46 | isset($options['locale']) ? $options['locale'] : 'en_US',
47 | isset($options['connection_timeout']) ? $options['connection_timeout'] : 3,
48 | isset($options['read_write_timeout']) ? $options['read_write_timeout'] : 130,
49 | $ssl_context,
50 | isset($options['keepalive']) ? $options['keepalive'] : false,
51 | isset($options['heartbeat']) ? $options['heartbeat'] : 0,
52 | isset($options['channel_rpc_timeout']) ? $options['channel_rpc_timeout'] : 0.0,
53 | $config
54 | );
55 | }
56 |
57 | /**
58 | * @deprecated Use AmqpConnectionFactory
59 | * @throws \Exception
60 | */
61 | public static function try_create_connection($host, $port, $user, $password, $vhost, $options)
62 | {
63 | $ssl_options = isset($options['ssl_options']) ? $options['ssl_options'] : [];
64 | return new static($host, $port, $user, $password, $vhost, $ssl_options, $options);
65 | }
66 |
67 | /**
68 | * @param array $options
69 | * @return resource
70 | */
71 | private function createSslContext($options)
72 | {
73 | $ssl_context = stream_context_create();
74 | foreach ($options as $k => $v) {
75 | // Note: 'ssl' applies to 'tls' as well
76 | // https://www.php.net/manual/en/context.ssl.php
77 | stream_context_set_option($ssl_context, 'ssl', $k, $v);
78 | }
79 |
80 | return $ssl_context;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/AMQPSocketConnection.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Connection;
4 |
5 | use PhpAmqpLib\Wire\IO\SocketIO;
6 |
7 | class AMQPSocketConnection extends AbstractConnection
8 | {
9 | /**
10 | * @param string $host
11 | * @param int $port
12 | * @param string $user
13 | * @param string $password
14 | * @param string $vhost
15 | * @param bool $insist
16 | * @param string $login_method
17 | * @param null $login_response @deprecated
18 | * @param string $locale
19 | * @param int|float $read_timeout
20 | * @param bool $keepalive
21 | * @param int $write_timeout
22 | * @param int $heartbeat
23 | * @param float $channel_rpc_timeout
24 | * @param AMQPConnectionConfig|null $config
25 | * @throws \Exception
26 | */
27 | public function __construct(
28 | $host,
29 | $port,
30 | $user,
31 | $password,
32 | $vhost = '/',
33 | $insist = false,
34 | $login_method = 'AMQPLAIN',
35 | $login_response = null,
36 | $locale = 'en_US',
37 | $read_timeout = 3,
38 | $keepalive = false,
39 | $write_timeout = 3,
40 | $heartbeat = 0,
41 | $channel_rpc_timeout = 0.0,
42 | ?AMQPConnectionConfig $config = null
43 | ) {
44 | if ($channel_rpc_timeout > $read_timeout) {
45 | throw new \InvalidArgumentException('channel RPC timeout must not be greater than I/O read timeout');
46 | }
47 |
48 | $io = new SocketIO($host, $port, $read_timeout, $keepalive, $write_timeout, $heartbeat, $config);
49 |
50 | parent::__construct(
51 | $user,
52 | $password,
53 | $vhost,
54 | $insist,
55 | $login_method,
56 | $login_response,
57 | $locale,
58 | $io,
59 | $heartbeat,
60 | max($read_timeout, $write_timeout),
61 | $channel_rpc_timeout,
62 | $config
63 | );
64 | }
65 |
66 | /**
67 | * @deprecated Use AmqpConnectionFactory
68 | * @throws \Exception
69 | */
70 | protected static function try_create_connection($host, $port, $user, $password, $vhost, $options)
71 | {
72 | $insist = isset($options['insist']) ?
73 | $options['insist'] : false;
74 | $login_method = isset($options['login_method']) ?
75 | $options['login_method'] : 'AMQPLAIN';
76 | $login_response = isset($options['login_response']) ?
77 | $options['login_response'] : null;
78 | $locale = isset($options['locale']) ?
79 | $options['locale'] : 'en_US';
80 | $read_timeout = isset($options['read_timeout']) ?
81 | $options['read_timeout'] : 3;
82 | $keepalive = isset($options['keepalive']) ?
83 | $options['keepalive'] : false;
84 | $write_timeout = isset($options['write_timeout']) ?
85 | $options['write_timeout'] : 3;
86 | $heartbeat = isset($options['heartbeat']) ?
87 | $options['heartbeat'] : 0;
88 | $channel_rpc_timeout = isset($options['channel_rpc_timeout']) ?
89 | $options['channel_rpc_timeout'] : 0.0;
90 | return new static(
91 | $host,
92 | $port,
93 | $user,
94 | $password,
95 | $vhost,
96 | $insist,
97 | $login_method,
98 | $login_response,
99 | $locale,
100 | $read_timeout,
101 | $keepalive,
102 | $write_timeout,
103 | $heartbeat,
104 | $channel_rpc_timeout
105 | );
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/AMQPStreamConnection.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Connection;
4 |
5 | use PhpAmqpLib\Wire\IO\StreamIO;
6 |
7 | class AMQPStreamConnection extends AbstractConnection
8 | {
9 | /**
10 | * @param string $host
11 | * @param int $port
12 | * @param string $user
13 | * @param string $password
14 | * @param string $vhost
15 | * @param bool $insist
16 | * @param string $login_method
17 | * @param null $login_response @deprecated
18 | * @param string $locale
19 | * @param float $connection_timeout
20 | * @param float $read_write_timeout
21 | * @param resource|array|null $context
22 | * @param bool $keepalive
23 | * @param int $heartbeat
24 | * @param float $channel_rpc_timeout
25 | * @param string|AMQPConnectionConfig|null $ssl_protocol @deprecated
26 | * @param AMQPConnectionConfig|null $config
27 | * @throws \Exception
28 | */
29 | public function __construct(
30 | $host,
31 | $port,
32 | $user,
33 | $password,
34 | $vhost = '/',
35 | $insist = false,
36 | $login_method = 'AMQPLAIN',
37 | $login_response = null,
38 | $locale = 'en_US',
39 | $connection_timeout = 3.0,
40 | $read_write_timeout = 3.0,
41 | $context = null,
42 | $keepalive = false,
43 | $heartbeat = 0,
44 | $channel_rpc_timeout = 0.0,
45 | $ssl_protocol = null,
46 | ?AMQPConnectionConfig $config = null
47 | ) {
48 | if ($ssl_protocol !== null && $ssl_protocol instanceof AMQPConnectionConfig === false) {
49 | trigger_error(
50 | '$ssl_protocol parameter is deprecated, use stream_context_set_option($context, \'ssl\', \'crypto_method\', $ssl_protocol) instead (see https://www.php.net/manual/en/function.stream-socket-enable-crypto.php for possible values)',
51 | E_USER_DEPRECATED
52 | );
53 | } elseif ($ssl_protocol instanceof AMQPConnectionConfig) {
54 | $config = $ssl_protocol;
55 | }
56 |
57 | if ($channel_rpc_timeout > $read_write_timeout) {
58 | throw new \InvalidArgumentException('channel RPC timeout must not be greater than I/O read-write timeout');
59 | }
60 |
61 | $io = new StreamIO(
62 | $host,
63 | $port,
64 | $connection_timeout,
65 | $read_write_timeout,
66 | $context,
67 | $keepalive,
68 | $heartbeat
69 | );
70 |
71 | parent::__construct(
72 | $user,
73 | $password,
74 | $vhost,
75 | $insist,
76 | $login_method,
77 | $login_response,
78 | $locale,
79 | $io,
80 | $heartbeat,
81 | $connection_timeout,
82 | $channel_rpc_timeout,
83 | $config
84 | );
85 |
86 | // save the params for the use of __clone, this will overwrite the parent
87 | $this->construct_params = func_get_args();
88 | }
89 |
90 | /**
91 | * @deprecated Use AmqpConnectionFactory
92 | * @throws \Exception
93 | */
94 | protected static function try_create_connection($host, $port, $user, $password, $vhost, $options)
95 | {
96 | $insist = isset($options['insist']) ?
97 | $options['insist'] : false;
98 | $login_method = isset($options['login_method']) ?
99 | $options['login_method'] : 'AMQPLAIN';
100 | $login_response = isset($options['login_response']) ?
101 | $options['login_response'] : null;
102 | $locale = isset($options['locale']) ?
103 | $options['locale'] : 'en_US';
104 | $connection_timeout = isset($options['connection_timeout']) ?
105 | $options['connection_timeout'] : 3.0;
106 | $read_write_timeout = isset($options['read_write_timeout']) ?
107 | $options['read_write_timeout'] : 3.0;
108 | $context = isset($options['context']) ?
109 | $options['context'] : null;
110 | $keepalive = isset($options['keepalive']) ?
111 | $options['keepalive'] : false;
112 | $heartbeat = isset($options['heartbeat']) ?
113 | $options['heartbeat'] : 60;
114 | $channel_rpc_timeout = isset($options['channel_rpc_timeout']) ?
115 | $options['channel_rpc_timeout'] : 0.0;
116 | return new static(
117 | $host,
118 | $port,
119 | $user,
120 | $password,
121 | $vhost,
122 | $insist,
123 | $login_method,
124 | $login_response,
125 | $locale,
126 | $connection_timeout,
127 | $read_write_timeout,
128 | $context,
129 | $keepalive,
130 | $heartbeat,
131 | $channel_rpc_timeout
132 | );
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/Heartbeat/AbstractSignalHeartbeatSender.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Connection\Heartbeat;
4 |
5 | use PhpAmqpLib\Connection\AbstractConnection;
6 | use PhpAmqpLib\Exception\AMQPRuntimeException;
7 |
8 | /**
9 | * Manages pcntl-based heartbeat sending for a {@link AbstractConnection}.
10 | */
11 | abstract class AbstractSignalHeartbeatSender
12 | {
13 | /**
14 | * @var AbstractConnection|null
15 | */
16 | protected $connection;
17 |
18 | /**
19 | * @var bool
20 | */
21 | protected $wasActive = false;
22 |
23 | /**
24 | * @param AbstractConnection $connection
25 | * @throws AMQPRuntimeException
26 | */
27 | public function __construct(AbstractConnection $connection)
28 | {
29 | if (!$this->isSupported()) {
30 | throw new AMQPRuntimeException('Signal-based heartbeat sender is unsupported');
31 | }
32 |
33 | $this->connection = $connection;
34 | }
35 |
36 | public function __destruct()
37 | {
38 | $this->unregister();
39 | }
40 |
41 | /**
42 | * @return bool
43 | */
44 | protected function isSupported(): bool
45 | {
46 | return extension_loaded('pcntl')
47 | && function_exists('pcntl_async_signals')
48 | && (defined('AMQP_WITHOUT_SIGNALS') ? !AMQP_WITHOUT_SIGNALS : true);
49 | }
50 |
51 | /**
52 | * Starts the heartbeats
53 | */
54 | abstract public function register(): void;
55 |
56 | /**
57 | * Stops the heartbeats.
58 | */
59 | abstract public function unregister(): void;
60 |
61 | /**
62 | * Handles the heartbeat when a signal interrupt is received
63 | *
64 | * @param int $interval
65 | */
66 | protected function handleSignal(int $interval): void
67 | {
68 | if (!$this->connection) {
69 | return;
70 | }
71 |
72 | // Support for lazy connections
73 | if (!$this->wasActive && $this->connection->isConnected()) {
74 | $this->wasActive = true;
75 | }
76 |
77 | if (!$this->wasActive) {
78 | return;
79 | }
80 |
81 | if (!$this->connection->isConnected()) {
82 | $this->unregister();
83 | return;
84 | }
85 |
86 | if ($this->connection->isWriting()) {
87 | return;
88 | }
89 |
90 | if (time() > ($this->connection->getLastActivity() + $interval)) {
91 | $this->connection->checkHeartBeat();
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/Heartbeat/PCNTLHeartbeatSender.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Connection\Heartbeat;
4 |
5 | use PhpAmqpLib\Exception\AMQPRuntimeException;
6 |
7 | /**
8 | * @see AbstractSignalHeartbeatSender
9 | *
10 | * This version of a signal based heartbeat sendler relies on using SIGALRM and uses the OS to trigger an alarm
11 | * after a given time.
12 | */
13 | final class PCNTLHeartbeatSender extends AbstractSignalHeartbeatSender
14 | {
15 | public function register(): void
16 | {
17 | if (!$this->connection) {
18 | throw new AMQPRuntimeException('Unable to re-register heartbeat sender');
19 | }
20 |
21 | $timeout = $this->connection->getHeartbeat();
22 |
23 | if ($timeout > 0) {
24 | $interval = (int)ceil($timeout / 2);
25 | pcntl_async_signals(true);
26 | $this->registerListener($interval);
27 | pcntl_alarm($interval);
28 | }
29 | }
30 |
31 | public function unregister(): void
32 | {
33 | $this->connection = null;
34 | // restore default signal handler
35 | pcntl_signal(SIGALRM, SIG_IGN);
36 | }
37 |
38 | private function registerListener(int $interval): void
39 | {
40 | pcntl_signal(SIGALRM, function () use ($interval) {
41 | $this->handleSignal($interval);
42 | if ($this->connection) {
43 | pcntl_alarm($interval);
44 | }
45 | }, true);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Connection/Heartbeat/SIGHeartbeatSender.php:
--------------------------------------------------------------------------------
1 | <?php
2 | namespace PhpAmqpLib\Connection\Heartbeat;
3 |
4 | use PhpAmqpLib\Connection\AbstractConnection;
5 | use PhpAmqpLib\Exception\AMQPRuntimeException;
6 |
7 | /**
8 | * @see AbstractSignalHeartbeatSender
9 | * @since 3.2.0
10 | *
11 | * This version of a signal based heartbeat sender allows using any signal number. It forks the current process
12 | * to create a child process that periodically sends a signal to the parent process.
13 | * The default signal used is SIGUSR1
14 | */
15 | final class SIGHeartbeatSender extends AbstractSignalHeartbeatSender
16 | {
17 | /**
18 | * @var int the UNIX signal to be used for managing heartbeats
19 | */
20 | private $signal;
21 |
22 | /**
23 | * @var int the PID (process ID) of the child process sending regular signals to manage heartbeats
24 | */
25 | private $childPid;
26 |
27 | /**
28 | * @param AbstractConnection $connection
29 | * @param int $signal
30 | * @throws AMQPRuntimeException
31 | */
32 | public function __construct(AbstractConnection $connection, int $signal = SIGUSR1)
33 | {
34 | parent::__construct($connection);
35 | $this->signal = $signal;
36 | }
37 |
38 | public function register(): void
39 | {
40 | if (!$this->connection) {
41 | throw new AMQPRuntimeException('Unable to re-register heartbeat sender');
42 | }
43 |
44 | $timeout = $this->connection->getHeartbeat();
45 |
46 | if ($timeout > 0) {
47 | $interval = (int)ceil($timeout / 2);
48 | $this->registerListener($interval);
49 | }
50 | }
51 |
52 | public function unregister(): void
53 | {
54 | $this->connection = null;
55 | // restore default signal handler
56 | pcntl_signal($this->signal, SIG_IGN);
57 | if ($this->childPid > 0) {
58 | posix_kill($this->childPid, SIGKILL);
59 | pcntl_waitpid($this->childPid, $status);
60 | }
61 | $this->childPid = 0;
62 | }
63 |
64 | private function registerListener(int $interval): void
65 | {
66 | pcntl_async_signals(true);
67 | $this->periodicAlarm($interval);
68 | pcntl_signal($this->signal, function () use ($interval) {
69 | $this->handleSignal($interval);
70 | });
71 | }
72 |
73 | /**
74 | * Forks the current process to create a child process that will send periodic signals to the parent
75 | *
76 | * @param int $interval
77 | */
78 | private function periodicAlarm(int $interval): void
79 | {
80 | $parent = getmypid();
81 | $pid = pcntl_fork();
82 | if(!$pid) {
83 | while (true){
84 | $slept = sleep($interval);
85 | if ($slept !== 0) {
86 | // interupted by signal from parent, exit immediately
87 | die;
88 | }
89 | posix_kill($parent, $this->signal);
90 | }
91 | } else {
92 | $this->childPid = $pid;
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPBasicCancelException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPBasicCancelException extends \Exception implements AMQPExceptionInterface
6 | {
7 | /**
8 | * @var string
9 | * @internal Use getter getConsumerTag()
10 | */
11 | public $consumerTag;
12 |
13 | /**
14 | * @param string $consumerTag
15 | */
16 | public function __construct($consumerTag)
17 | {
18 | parent::__construct('Channel was canceled');
19 | $this->consumerTag = $consumerTag;
20 | }
21 |
22 | /**
23 | * @return string
24 | */
25 | public function getConsumerTag()
26 | {
27 | return $this->consumerTag;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPChannelClosedException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPChannelClosedException extends AMQPRuntimeException
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPConnectionBlockedException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPConnectionBlockedException extends AMQPRuntimeException
6 | {
7 | public function __construct($message = '', $code = 0, $previous = null)
8 | {
9 | if (empty($message)) {
10 | $message = 'Connection is blocked due to low resources';
11 | }
12 | parent::__construct($message, $code, $previous);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPConnectionClosedException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | /**
6 | * When connection was closed by server, proxy or some tunnel due to timeout or network issue.
7 | */
8 | class AMQPConnectionClosedException extends AMQPRuntimeException
9 | {
10 | }
11 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPDataReadException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPDataReadException extends AMQPRuntimeException
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPEmptyDeliveryTagException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPEmptyDeliveryTagException extends AMQPRuntimeException
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPExceptionInterface.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | use Throwable;
6 |
7 | interface AMQPExceptionInterface extends Throwable
8 | {
9 | }
10 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPHeartbeatMissedException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPHeartbeatMissedException extends AMQPConnectionClosedException
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPIOException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPIOException extends \Exception implements AMQPExceptionInterface
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPIOWaitException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPIOWaitException extends AMQPRuntimeException
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPInvalidArgumentException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPInvalidArgumentException extends \RuntimeException implements AMQPExceptionInterface
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPInvalidFrameException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPInvalidFrameException extends AMQPRuntimeException
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPLogicException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPLogicException extends \LogicException implements AMQPExceptionInterface
6 | {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPNoDataException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | /**
6 | * Used mostly in non-blocking methods when no data is ready for processing.
7 | */
8 | class AMQPNoDataException extends AMQPRuntimeException
9 | {
10 | }
11 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPNotImplementedException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPNotImplementedException extends AMQPRuntimeException
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPOutOfBoundsException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPOutOfBoundsException extends \OutOfBoundsException implements AMQPExceptionInterface
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPOutOfRangeException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPOutOfRangeException extends \OutOfRangeException implements AMQPExceptionInterface
6 | {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPProtocolChannelException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPProtocolChannelException extends AMQPProtocolException
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPProtocolException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPProtocolException extends \Exception implements AMQPExceptionInterface
6 | {
7 | /** @var int */
8 | public $amqp_reply_code;
9 |
10 | /** @var string */
11 | public $amqp_reply_text;
12 |
13 | /** @var int[] */
14 | public $amqp_method_sig;
15 |
16 | /** @var array */
17 | public $args;
18 |
19 | /**
20 | * @param int $reply_code
21 | * @param string $reply_text
22 | * @param int[] $method_sig
23 | */
24 | public function __construct($reply_code, $reply_text, $method_sig)
25 | {
26 | parent::__construct($reply_text, $reply_code);
27 |
28 | $this->amqp_reply_code = $reply_code; // redundant, but kept for BC
29 | $this->amqp_reply_text = $reply_text; // redundant, but kept for BC
30 | $this->amqp_method_sig = $method_sig;
31 |
32 | $this->args = array($reply_code, $reply_text, $method_sig);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPRuntimeException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPRuntimeException extends \RuntimeException implements AMQPExceptionInterface
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPSocketException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPSocketException extends AMQPRuntimeException
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exception/AMQPTimeoutException.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exception;
4 |
5 | class AMQPTimeoutException extends \RuntimeException implements AMQPExceptionInterface
6 | {
7 | /**
8 | * @var int|float|null
9 | */
10 | private $timeout;
11 |
12 | public function __construct($message = '', $timeout = 0, $code = 0, ?\Exception $previous = null)
13 | {
14 | parent::__construct($message, $code, $previous);
15 | $this->timeout = $timeout;
16 | }
17 |
18 | /**
19 | * @param int|float|null $timeout
20 | * @param int $code
21 | * @return self
22 | */
23 | public static function writeTimeout($timeout, $code = 0)
24 | {
25 | return new self('Error sending data. Connection timed out.', $timeout, $code);
26 | }
27 |
28 | /**
29 | * @return int|float|null
30 | */
31 | public function getTimeout()
32 | {
33 | return $this->timeout;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Exchange/AMQPExchangeType.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Exchange;
4 |
5 | final class AMQPExchangeType
6 | {
7 | const DIRECT = 'direct';
8 | const FANOUT = 'fanout';
9 | const TOPIC = 'topic';
10 | const HEADERS = 'headers';
11 | }
12 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Helper/Assert.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Helper;
4 |
5 | use InvalidArgumentException;
6 |
7 | class Assert
8 | {
9 | /**
10 | * @param mixed $argument
11 | * @throws \InvalidArgumentException
12 | */
13 | public static function isCallable($argument)
14 | {
15 | if (!is_callable($argument)) {
16 | throw new InvalidArgumentException(sprintf(
17 | 'Given argument "%s" should be callable. %s type was given.',
18 | $argument,
19 | gettype($argument)
20 | ));
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Helper/BigInteger.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Helper;
4 |
5 | if (class_exists('phpseclib\Math\BigInteger')) {
6 | class BigInteger extends \phpseclib\Math\BigInteger
7 | {
8 | }
9 | } elseif (class_exists('phpseclib3\Math\BigInteger')) {
10 | class BigInteger extends \phpseclib3\Math\BigInteger
11 | {
12 | }
13 | } else {
14 | throw new \RuntimeException('Cannot find supported phpseclib/phpseclib library');
15 | }
16 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Helper/DebugHelper.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Helper;
4 |
5 | use PhpAmqpLib\Wire\Constants;
6 |
7 | class DebugHelper
8 | {
9 | /**
10 | * @var bool
11 | */
12 | protected $debug;
13 |
14 | /**
15 | * @var resource
16 | */
17 | protected $debug_output;
18 |
19 | /**
20 | * @var Constants
21 | */
22 | protected $constants;
23 |
24 | /**
25 | * @param Constants $constants
26 | */
27 | public function __construct(Constants $constants)
28 | {
29 | $this->debug = defined('AMQP_DEBUG') ? AMQP_DEBUG : false;
30 | if (defined('AMQP_DEBUG_OUTPUT')) {
31 | $this->debug_output = AMQP_DEBUG_OUTPUT;
32 | } else {
33 | $this->debug_output = fopen('php://output', 'wb');
34 | }
35 | $this->constants = $constants;
36 | }
37 |
38 | /**
39 | * @param string $msg
40 | */
41 | public function debug_msg($msg)
42 | {
43 | if ($this->debug) {
44 | $this->print_msg($msg);
45 | }
46 | }
47 |
48 | /**
49 | * @param array|null $allowed_methods
50 | */
51 | public function debug_allowed_methods($allowed_methods)
52 | {
53 | if ($this->debug) {
54 | if ($allowed_methods) {
55 | $msg = 'waiting for ' . implode(', ', $allowed_methods);
56 | } else {
57 | $msg = 'waiting for any method';
58 | }
59 | $this->debug_msg($msg);
60 | }
61 | }
62 |
63 | /**
64 | * @param string|array $method_sig
65 | */
66 | public function debug_method_signature1($method_sig)
67 | {
68 | $this->debug_method_signature('< %s:', $method_sig);
69 | }
70 |
71 | /**
72 | * @param string $msg
73 | * @param string|array $method_sig
74 | */
75 | public function debug_method_signature($msg, $method_sig)
76 | {
77 | if ($this->debug) {
78 | $constants = $this->constants;
79 | $methods = $constants::$GLOBAL_METHOD_NAMES;
80 | $key = MiscHelper::methodSig($method_sig);
81 | $this->debug_msg(sprintf($msg . ': %s', $key, $methods[$key]));
82 | }
83 | }
84 |
85 | /**
86 | * @param string $data
87 | */
88 | public function debug_hexdump($data)
89 | {
90 | if ($this->debug) {
91 | $this->debug_msg(
92 | sprintf(
93 | '< [hex]: %s%s',
94 | PHP_EOL,
95 | MiscHelper::hexdump($data, $htmloutput = false, $uppercase = true, $return = true)
96 | )
97 | );
98 | }
99 | }
100 |
101 | /**
102 | * @param int $version_major
103 | * @param int $version_minor
104 | * @param array $server_properties
105 | * @param array $mechanisms
106 | * @param array $locales
107 | */
108 | public function debug_connection_start($version_major, $version_minor, $server_properties, $mechanisms, $locales)
109 | {
110 | if ($this->debug) {
111 | $this->debug_msg(
112 | sprintf(
113 | 'Start from server, version: %d.%d, properties: %s, mechanisms: %s, locales: %s',
114 | $version_major,
115 | $version_minor,
116 | MiscHelper::dump_table($server_properties),
117 | implode(', ', $mechanisms),
118 | implode(', ', $locales)
119 | )
120 | );
121 | }
122 | }
123 |
124 | /**
125 | * @param string $s
126 | */
127 | protected function print_msg($s)
128 | {
129 | fwrite($this->debug_output, $s . PHP_EOL);
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Helper/MiscHelper.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Helper;
4 |
5 | class MiscHelper
6 | {
7 | /**
8 | * @param string|array $a
9 | * @return string
10 | */
11 | public static function methodSig($a)
12 | {
13 | if (is_string($a)) {
14 | return $a;
15 | }
16 |
17 | return sprintf('%d,%d', $a[0], $a[1]);
18 | }
19 |
20 | /**
21 | * Gets a number (either int or float) and returns an array containing its integer part as first element and its
22 | * decimal part multiplied by 10^6. Useful for some PHP stream functions that need seconds and microseconds as
23 | * different arguments
24 | *
25 | * @param int|float $number
26 | * @return int[]
27 | */
28 | public static function splitSecondsMicroseconds($number)
29 | {
30 | return array((int)floor($number), (int)(fmod($number, 1) * 1000000));
31 | }
32 |
33 | /**
34 | * View any string as a hexdump.
35 | *
36 | * This is most commonly used to view binary data from streams
37 | * or sockets while debugging, but can be used to view any string
38 | * with non-viewable characters.
39 | *
40 | * @version 1.3.2
41 | * @author Aidan Lister <aidan@php.net>
42 | * @author Peter Waller <iridum@php.net>
43 | * @link http://aidanlister.com/repos/v/function.hexdump.php
44 | *
45 | * @param string $data The string to be dumped
46 | * @param bool $htmloutput Set to false for non-HTML output
47 | * @param bool $uppercase Set to true for uppercase hex
48 | * @param bool $return Set to true to return the dump
49 | * @return string|null
50 | */
51 | public static function hexdump($data, $htmloutput = true, $uppercase = false, $return = false)
52 | {
53 | // Init
54 | $hexi = '';
55 | $ascii = '';
56 | $dump = $htmloutput ? '<pre>' : '';
57 | $offset = 0;
58 | $len = mb_strlen($data, 'ASCII');
59 |
60 | // Upper or lower case hexidecimal
61 | $hexFormat = $uppercase ? 'X' : 'x';
62 |
63 | // Iterate string
64 | for ($i = $j = 0; $i < $len; $i++) {
65 | // Convert to hexidecimal
66 | // We must use concatenation here because the $hexFormat value
67 | // is needed for sprintf() to parse the format
68 | $hexi .= sprintf('%02' . $hexFormat . ' ', ord($data[$i]));
69 |
70 | // Replace non-viewable bytes with '.'
71 | if (ord($data[$i]) >= 32) {
72 | $ascii .= $htmloutput ? htmlentities($data[$i]) : $data[$i];
73 | } else {
74 | $ascii .= '.';
75 | }
76 |
77 | // Add extra column spacing
78 | if ($j === 7) {
79 | $hexi .= ' ';
80 | $ascii .= ' ';
81 | }
82 |
83 | // Add row
84 | if (++$j === 16 || $i === $len - 1) {
85 | // Join the hexi / ascii output
86 | // We must use concatenation here because the $hexFormat value
87 | // is needed for sprintf() to parse the format
88 | $dump .= sprintf('%04' . $hexFormat . ' %-49s %s', $offset, $hexi, $ascii);
89 |
90 | // Reset vars
91 | $hexi = $ascii = '';
92 | $offset += 16;
93 | $j = 0;
94 |
95 | // Add newline
96 | if ($i !== $len - 1) {
97 | $dump .= PHP_EOL;
98 | }
99 | }
100 | }
101 |
102 | // Finish dump
103 | $dump .= $htmloutput ? '</pre>' : '';
104 | $dump .= PHP_EOL;
105 |
106 | if ($return) {
107 | return $dump;
108 | }
109 |
110 | echo $dump;
111 |
112 | return null;
113 | }
114 |
115 | /**
116 | * @param array $table
117 | * @return string
118 | */
119 | public static function dump_table($table)
120 | {
121 | $tokens = array();
122 | foreach ($table as $name => $value) {
123 | switch ($value[0]) {
124 | case 'D':
125 | $val = $value[1]->n . 'E' . $value[1]->e;
126 | break;
127 | case 'F':
128 | $val = '(' . self::dump_table($value[1]) . ')';
129 | break;
130 | case 'T':
131 | $val = date('Y-m-d H:i:s', $value[1]);
132 | break;
133 | default:
134 | $val = $value[1];
135 | }
136 | $tokens[] = $name . '=' . $val;
137 | }
138 |
139 | return implode(', ', $tokens);
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Helper/Protocol/MethodMap080.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | /* This file was autogenerated by spec/parser.php - Do not modify */
4 |
5 | namespace PhpAmqpLib\Helper\Protocol;
6 |
7 | class MethodMap080
8 | {
9 | /**
10 | * @var array
11 | */
12 | protected $method_map = array(
13 | '10,10' => 'connection_start',
14 | '10,11' => 'connection_start_ok',
15 | '10,20' => 'connection_secure',
16 | '10,21' => 'connection_secure_ok',
17 | '10,30' => 'connection_tune',
18 | '10,31' => 'connection_tune_ok',
19 | '10,40' => 'connection_open',
20 | '10,41' => 'connection_open_ok',
21 | '10,50' => 'connection_redirect',
22 | '10,60' => 'connection_close',
23 | '10,61' => 'connection_close_ok',
24 | '20,10' => 'channel_open',
25 | '20,11' => 'channel_open_ok',
26 | '20,20' => 'channel_flow',
27 | '20,21' => 'channel_flow_ok',
28 | '20,30' => 'channel_alert',
29 | '20,40' => 'channel_close',
30 | '20,41' => 'channel_close_ok',
31 | '30,10' => 'access_request',
32 | '30,11' => 'access_request_ok',
33 | '40,10' => 'exchange_declare',
34 | '40,11' => 'exchange_declare_ok',
35 | '40,20' => 'exchange_delete',
36 | '40,21' => 'exchange_delete_ok',
37 | '50,10' => 'queue_declare',
38 | '50,11' => 'queue_declare_ok',
39 | '50,20' => 'queue_bind',
40 | '50,21' => 'queue_bind_ok',
41 | '50,30' => 'queue_purge',
42 | '50,31' => 'queue_purge_ok',
43 | '50,40' => 'queue_delete',
44 | '50,41' => 'queue_delete_ok',
45 | '50,50' => 'queue_unbind',
46 | '50,51' => 'queue_unbind_ok',
47 | '60,10' => 'basic_qos',
48 | '60,11' => 'basic_qos_ok',
49 | '60,20' => 'basic_consume',
50 | '60,21' => 'basic_consume_ok',
51 | '60,30' => 'basic_cancel',
52 | '60,31' => 'basic_cancel_ok',
53 | '60,40' => 'basic_publish',
54 | '60,50' => 'basic_return',
55 | '60,60' => 'basic_deliver',
56 | '60,70' => 'basic_get',
57 | '60,71' => 'basic_get_ok',
58 | '60,72' => 'basic_get_empty',
59 | '60,80' => 'basic_ack',
60 | '60,90' => 'basic_reject',
61 | '60,100' => 'basic_recover_async',
62 | '60,110' => 'basic_recover',
63 | '60,111' => 'basic_recover_ok',
64 | '70,10' => 'file_qos',
65 | '70,11' => 'file_qos_ok',
66 | '70,20' => 'file_consume',
67 | '70,21' => 'file_consume_ok',
68 | '70,30' => 'file_cancel',
69 | '70,31' => 'file_cancel_ok',
70 | '70,40' => 'file_open',
71 | '70,41' => 'file_open_ok',
72 | '70,50' => 'file_stage',
73 | '70,60' => 'file_publish',
74 | '70,70' => 'file_return',
75 | '70,80' => 'file_deliver',
76 | '70,90' => 'file_ack',
77 | '70,100' => 'file_reject',
78 | '80,10' => 'stream_qos',
79 | '80,11' => 'stream_qos_ok',
80 | '80,20' => 'stream_consume',
81 | '80,21' => 'stream_consume_ok',
82 | '80,30' => 'stream_cancel',
83 | '80,31' => 'stream_cancel_ok',
84 | '80,40' => 'stream_publish',
85 | '80,50' => 'stream_return',
86 | '80,60' => 'stream_deliver',
87 | '90,10' => 'tx_select',
88 | '90,11' => 'tx_select_ok',
89 | '90,20' => 'tx_commit',
90 | '90,21' => 'tx_commit_ok',
91 | '90,30' => 'tx_rollback',
92 | '90,31' => 'tx_rollback_ok',
93 | '100,10' => 'dtx_select',
94 | '100,11' => 'dtx_select_ok',
95 | '100,20' => 'dtx_start',
96 | '100,21' => 'dtx_start_ok',
97 | '110,10' => 'tunnel_request',
98 | '120,10' => 'test_integer',
99 | '120,11' => 'test_integer_ok',
100 | '120,20' => 'test_string',
101 | '120,21' => 'test_string_ok',
102 | '120,30' => 'test_table',
103 | '120,31' => 'test_table_ok',
104 | '120,40' => 'test_content',
105 | '120,41' => 'test_content_ok',
106 | );
107 |
108 | /**
109 | * @var string $method_sig
110 | * @return string
111 | */
112 | public function get_method($method_sig)
113 | {
114 | return $this->method_map[$method_sig];
115 | }
116 |
117 | /**
118 | * @var string $method_sig
119 | * @return bool
120 | */
121 | public function valid_method($method_sig)
122 | {
123 | return array_key_exists($method_sig, $this->method_map);
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Helper/Protocol/MethodMap091.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | /* This file was autogenerated by spec/parser.php - Do not modify */
4 |
5 | namespace PhpAmqpLib\Helper\Protocol;
6 |
7 | class MethodMap091
8 | {
9 | /**
10 | * @var array
11 | */
12 | protected $method_map = array(
13 | '10,10' => 'connection_start',
14 | '10,11' => 'connection_start_ok',
15 | '10,20' => 'connection_secure',
16 | '10,21' => 'connection_secure_ok',
17 | '10,30' => 'connection_tune',
18 | '10,31' => 'connection_tune_ok',
19 | '10,40' => 'connection_open',
20 | '10,41' => 'connection_open_ok',
21 | '10,50' => 'connection_close',
22 | '10,51' => 'connection_close_ok',
23 | '10,60' => 'connection_blocked',
24 | '10,61' => 'connection_unblocked',
25 | '20,10' => 'channel_open',
26 | '20,11' => 'channel_open_ok',
27 | '20,20' => 'channel_flow',
28 | '20,21' => 'channel_flow_ok',
29 | '20,40' => 'channel_close',
30 | '20,41' => 'channel_close_ok',
31 | '30,10' => 'access_request',
32 | '30,11' => 'access_request_ok',
33 | '40,10' => 'exchange_declare',
34 | '40,11' => 'exchange_declare_ok',
35 | '40,20' => 'exchange_delete',
36 | '40,21' => 'exchange_delete_ok',
37 | '40,30' => 'exchange_bind',
38 | '40,31' => 'exchange_bind_ok',
39 | '40,40' => 'exchange_unbind',
40 | '40,51' => 'exchange_unbind_ok',
41 | '50,10' => 'queue_declare',
42 | '50,11' => 'queue_declare_ok',
43 | '50,20' => 'queue_bind',
44 | '50,21' => 'queue_bind_ok',
45 | '50,30' => 'queue_purge',
46 | '50,31' => 'queue_purge_ok',
47 | '50,40' => 'queue_delete',
48 | '50,41' => 'queue_delete_ok',
49 | '50,50' => 'queue_unbind',
50 | '50,51' => 'queue_unbind_ok',
51 | '60,10' => 'basic_qos',
52 | '60,11' => 'basic_qos_ok',
53 | '60,20' => 'basic_consume',
54 | '60,21' => 'basic_consume_ok',
55 | '60,30' => 'basic_cancel_from_server',
56 | '60,31' => 'basic_cancel_ok',
57 | '60,40' => 'basic_publish',
58 | '60,50' => 'basic_return',
59 | '60,60' => 'basic_deliver',
60 | '60,70' => 'basic_get',
61 | '60,71' => 'basic_get_ok',
62 | '60,72' => 'basic_get_empty',
63 | '60,80' => 'basic_ack_from_server',
64 | '60,90' => 'basic_reject',
65 | '60,100' => 'basic_recover_async',
66 | '60,110' => 'basic_recover',
67 | '60,111' => 'basic_recover_ok',
68 | '60,120' => 'basic_nack_from_server',
69 | '90,10' => 'tx_select',
70 | '90,11' => 'tx_select_ok',
71 | '90,20' => 'tx_commit',
72 | '90,21' => 'tx_commit_ok',
73 | '90,30' => 'tx_rollback',
74 | '90,31' => 'tx_rollback_ok',
75 | '85,10' => 'confirm_select',
76 | '85,11' => 'confirm_select_ok',
77 | );
78 |
79 | /**
80 | * @var string $method_sig
81 | * @return string
82 | */
83 | public function get_method($method_sig)
84 | {
85 | return $this->method_map[$method_sig];
86 | }
87 |
88 | /**
89 | * @var string $method_sig
90 | * @return bool
91 | */
92 | public function valid_method($method_sig)
93 | {
94 | return array_key_exists($method_sig, $this->method_map);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Helper/Protocol/Wait080.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | /* This file was autogenerated by spec/parser.php - Do not modify */
4 |
5 | namespace PhpAmqpLib\Helper\Protocol;
6 |
7 | class Wait080
8 | {
9 | /**
10 | * @var array
11 | */
12 | protected $wait = array(
13 | 'connection.start' => '10,10',
14 | 'connection.start_ok' => '10,11',
15 | 'connection.secure' => '10,20',
16 | 'connection.secure_ok' => '10,21',
17 | 'connection.tune' => '10,30',
18 | 'connection.tune_ok' => '10,31',
19 | 'connection.open' => '10,40',
20 | 'connection.open_ok' => '10,41',
21 | 'connection.redirect' => '10,50',
22 | 'connection.close' => '10,60',
23 | 'connection.close_ok' => '10,61',
24 | 'channel.open' => '20,10',
25 | 'channel.open_ok' => '20,11',
26 | 'channel.flow' => '20,20',
27 | 'channel.flow_ok' => '20,21',
28 | 'channel.alert' => '20,30',
29 | 'channel.close' => '20,40',
30 | 'channel.close_ok' => '20,41',
31 | 'access.request' => '30,10',
32 | 'access.request_ok' => '30,11',
33 | 'exchange.declare' => '40,10',
34 | 'exchange.declare_ok' => '40,11',
35 | 'exchange.delete' => '40,20',
36 | 'exchange.delete_ok' => '40,21',
37 | 'queue.declare' => '50,10',
38 | 'queue.declare_ok' => '50,11',
39 | 'queue.bind' => '50,20',
40 | 'queue.bind_ok' => '50,21',
41 | 'queue.purge' => '50,30',
42 | 'queue.purge_ok' => '50,31',
43 | 'queue.delete' => '50,40',
44 | 'queue.delete_ok' => '50,41',
45 | 'queue.unbind' => '50,50',
46 | 'queue.unbind_ok' => '50,51',
47 | 'basic.qos' => '60,10',
48 | 'basic.qos_ok' => '60,11',
49 | 'basic.consume' => '60,20',
50 | 'basic.consume_ok' => '60,21',
51 | 'basic.cancel' => '60,30',
52 | 'basic.cancel_ok' => '60,31',
53 | 'basic.publish' => '60,40',
54 | 'basic.return' => '60,50',
55 | 'basic.deliver' => '60,60',
56 | 'basic.get' => '60,70',
57 | 'basic.get_ok' => '60,71',
58 | 'basic.get_empty' => '60,72',
59 | 'basic.ack' => '60,80',
60 | 'basic.reject' => '60,90',
61 | 'basic.recover_async' => '60,100',
62 | 'basic.recover' => '60,110',
63 | 'basic.recover_ok' => '60,111',
64 | 'file.qos' => '70,10',
65 | 'file.qos_ok' => '70,11',
66 | 'file.consume' => '70,20',
67 | 'file.consume_ok' => '70,21',
68 | 'file.cancel' => '70,30',
69 | 'file.cancel_ok' => '70,31',
70 | 'file.open' => '70,40',
71 | 'file.open_ok' => '70,41',
72 | 'file.stage' => '70,50',
73 | 'file.publish' => '70,60',
74 | 'file.return' => '70,70',
75 | 'file.deliver' => '70,80',
76 | 'file.ack' => '70,90',
77 | 'file.reject' => '70,100',
78 | 'stream.qos' => '80,10',
79 | 'stream.qos_ok' => '80,11',
80 | 'stream.consume' => '80,20',
81 | 'stream.consume_ok' => '80,21',
82 | 'stream.cancel' => '80,30',
83 | 'stream.cancel_ok' => '80,31',
84 | 'stream.publish' => '80,40',
85 | 'stream.return' => '80,50',
86 | 'stream.deliver' => '80,60',
87 | 'tx.select' => '90,10',
88 | 'tx.select_ok' => '90,11',
89 | 'tx.commit' => '90,20',
90 | 'tx.commit_ok' => '90,21',
91 | 'tx.rollback' => '90,30',
92 | 'tx.rollback_ok' => '90,31',
93 | 'dtx.select' => '100,10',
94 | 'dtx.select_ok' => '100,11',
95 | 'dtx.start' => '100,20',
96 | 'dtx.start_ok' => '100,21',
97 | 'tunnel.request' => '110,10',
98 | 'test.integer' => '120,10',
99 | 'test.integer_ok' => '120,11',
100 | 'test.string' => '120,20',
101 | 'test.string_ok' => '120,21',
102 | 'test.table' => '120,30',
103 | 'test.table_ok' => '120,31',
104 | 'test.content' => '120,40',
105 | 'test.content_ok' => '120,41',
106 | );
107 |
108 | /**
109 | * @var string $method
110 | * @return string
111 | */
112 | public function get_wait($method)
113 | {
114 | return $this->wait[$method];
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Helper/Protocol/Wait091.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | /* This file was autogenerated by spec/parser.php - Do not modify */
4 |
5 | namespace PhpAmqpLib\Helper\Protocol;
6 |
7 | class Wait091
8 | {
9 | /**
10 | * @var array
11 | */
12 | protected $wait = array(
13 | 'connection.start' => '10,10',
14 | 'connection.start_ok' => '10,11',
15 | 'connection.secure' => '10,20',
16 | 'connection.secure_ok' => '10,21',
17 | 'connection.tune' => '10,30',
18 | 'connection.tune_ok' => '10,31',
19 | 'connection.open' => '10,40',
20 | 'connection.open_ok' => '10,41',
21 | 'connection.close' => '10,50',
22 | 'connection.close_ok' => '10,51',
23 | 'connection.blocked' => '10,60',
24 | 'connection.unblocked' => '10,61',
25 | 'channel.open' => '20,10',
26 | 'channel.open_ok' => '20,11',
27 | 'channel.flow' => '20,20',
28 | 'channel.flow_ok' => '20,21',
29 | 'channel.close' => '20,40',
30 | 'channel.close_ok' => '20,41',
31 | 'access.request' => '30,10',
32 | 'access.request_ok' => '30,11',
33 | 'exchange.declare' => '40,10',
34 | 'exchange.declare_ok' => '40,11',
35 | 'exchange.delete' => '40,20',
36 | 'exchange.delete_ok' => '40,21',
37 | 'exchange.bind' => '40,30',
38 | 'exchange.bind_ok' => '40,31',
39 | 'exchange.unbind' => '40,40',
40 | 'exchange.unbind_ok' => '40,51',
41 | 'queue.declare' => '50,10',
42 | 'queue.declare_ok' => '50,11',
43 | 'queue.bind' => '50,20',
44 | 'queue.bind_ok' => '50,21',
45 | 'queue.purge' => '50,30',
46 | 'queue.purge_ok' => '50,31',
47 | 'queue.delete' => '50,40',
48 | 'queue.delete_ok' => '50,41',
49 | 'queue.unbind' => '50,50',
50 | 'queue.unbind_ok' => '50,51',
51 | 'basic.qos' => '60,10',
52 | 'basic.qos_ok' => '60,11',
53 | 'basic.consume' => '60,20',
54 | 'basic.consume_ok' => '60,21',
55 | 'basic.cancel' => '60,30',
56 | 'basic.cancel_ok' => '60,31',
57 | 'basic.publish' => '60,40',
58 | 'basic.return' => '60,50',
59 | 'basic.deliver' => '60,60',
60 | 'basic.get' => '60,70',
61 | 'basic.get_ok' => '60,71',
62 | 'basic.get_empty' => '60,72',
63 | 'basic.ack' => '60,80',
64 | 'basic.reject' => '60,90',
65 | 'basic.recover_async' => '60,100',
66 | 'basic.recover' => '60,110',
67 | 'basic.recover_ok' => '60,111',
68 | 'basic.nack' => '60,120',
69 | 'tx.select' => '90,10',
70 | 'tx.select_ok' => '90,11',
71 | 'tx.commit' => '90,20',
72 | 'tx.commit_ok' => '90,21',
73 | 'tx.rollback' => '90,30',
74 | 'tx.rollback_ok' => '90,31',
75 | 'confirm.select' => '85,10',
76 | 'confirm.select_ok' => '85,11',
77 | );
78 |
79 | /**
80 | * @var string $method
81 | * @return string
82 | */
83 | public function get_wait($method)
84 | {
85 | return $this->wait[$method];
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Helper/SocketConstants.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Helper;
4 |
5 | /**
6 | * @property-read int $SOCKET_EPIPE
7 | * @property-read int $SOCKET_ENETDOWN
8 | * @property-read int $SOCKET_ENETUNREACH
9 | * @property-read int $SOCKET_ENETRESET
10 | * @property-read int $SOCKET_ECONNABORTED
11 | * @property-read int $SOCKET_ECONNRESET
12 | * @property-read int $SOCKET_ECONNREFUSED
13 | * @property-read int $SOCKET_ETIMEDOUT
14 | * @property-read int $SOCKET_EWOULDBLOCK
15 | * @property-read int $SOCKET_EINTR
16 | * @property-read int $SOCKET_EAGAIN
17 | */
18 | final class SocketConstants
19 | {
20 | /**
21 | * @var int[]
22 | */
23 | private $constants;
24 |
25 | /** @var self */
26 | private static $instance;
27 |
28 | public function __construct()
29 | {
30 | $constants = get_defined_constants(true);
31 | if (isset($constants['sockets'])) {
32 | $this->constants = $constants['sockets'];
33 | } else {
34 | trigger_error('Sockets extension is not enabled', E_USER_WARNING);
35 | $this->constants = array();
36 | }
37 | }
38 |
39 | /**
40 | * @param string $name
41 | * @return int
42 | */
43 | public function __get($name)
44 | {
45 | return isset($this->constants[$name]) ? $this->constants[$name] : 0;
46 | }
47 |
48 | /**
49 | * @param string $name
50 | * @param int $value
51 | * @internal
52 | */
53 | public function __set($name, $value)
54 | {
55 | }
56 |
57 | /**
58 | * @param string $name
59 | * @return bool
60 | */
61 | public function __isset($name)
62 | {
63 | return isset($this->constants[$name]);
64 | }
65 |
66 | /**
67 | * @return self
68 | */
69 | public static function getInstance()
70 | {
71 | if (!self::$instance) {
72 | self::$instance = new self();
73 | }
74 |
75 | return self::$instance;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Message/AMQPMessage.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Message;
4 |
5 | use PhpAmqpLib\Channel\AMQPChannel;
6 | use PhpAmqpLib\Exception\AMQPEmptyDeliveryTagException;
7 | use PhpAmqpLib\Wire\AMQPReader;
8 | use PhpAmqpLib\Wire\AMQPWriter;
9 |
10 | /**
11 | * A Message for use with the Channnel.basic_* methods.
12 | */
13 | class AMQPMessage
14 | {
15 | const DELIVERY_MODE_NON_PERSISTENT = 1;
16 | const DELIVERY_MODE_PERSISTENT = 2;
17 |
18 | /**
19 | * @var string
20 | * @deprecated Will be removed in version 4.0, use getBody() instead.
21 | */
22 | public $body;
23 |
24 | /**
25 | * @var int
26 | * @deprecated Will be removed in version 4.0, use getBodySize() instead.
27 | */
28 | public $body_size;
29 |
30 | /**
31 | * @var bool
32 | * @deprecated Will be removed in version 4.0, use isTruncated() instead.
33 | */
34 | public $is_truncated = false;
35 |
36 | /**
37 | * @var string
38 | * @deprecated Will be removed in version 4.0, use getContentEncoding() instead.
39 | */
40 | public $content_encoding;
41 |
42 | /** @var int */
43 | private $deliveryTag;
44 |
45 | /** @var string|null */
46 | private $consumerTag;
47 |
48 | /** @var bool|null */
49 | private $redelivered;
50 |
51 | /** @var string|null */
52 | private $exchange;
53 |
54 | /** @var string|null */
55 | private $routingKey;
56 |
57 | /** @var int|null */
58 | private $messageCount;
59 |
60 | /** @var AMQPChannel|null */
61 | private $channel;
62 |
63 | /** @var bool */
64 | private $responded = false;
65 |
66 | /**
67 | * @var array
68 | * @internal
69 | * @deprecated Will be removed in version 4.0, use one of getters to get delivery info.
70 | */
71 | public $delivery_info = array();
72 |
73 | /** @var array Properties content */
74 | protected $properties = array();
75 |
76 | /** @var null|string Compiled properties */
77 | protected $serialized_properties;
78 |
79 | /** @var array */
80 | protected static $propertyDefinitions = array(
81 | 'content_type' => 'shortstr',
82 | 'content_encoding' => 'shortstr',
83 | 'application_headers' => 'table_object',
84 | 'delivery_mode' => 'octet',
85 | 'priority' => 'octet',
86 | 'correlation_id' => 'shortstr',
87 | 'reply_to' => 'shortstr',
88 | 'expiration' => 'shortstr',
89 | 'message_id' => 'shortstr',
90 | 'timestamp' => 'timestamp',
91 | 'type' => 'shortstr',
92 | 'user_id' => 'shortstr',
93 | 'app_id' => 'shortstr',
94 | 'cluster_id' => 'shortstr',
95 | );
96 |
97 | /**
98 | * @param string $body
99 | * @param array $properties
100 | */
101 | public function __construct($body = '', $properties = array())
102 | {
103 | $this->setBody($body);
104 |
105 | if (!empty($properties) && is_array($properties)) {
106 | $this->properties = array_intersect_key($properties, self::$propertyDefinitions);
107 | }
108 | }
109 |
110 | /**
111 | * Acknowledge one or more messages.
112 | *
113 | * @param bool $multiple If true, the delivery tag is treated as "up to and including",
114 | * so that multiple messages can be acknowledged with a single method.
115 | * @since 2.12.0
116 | * @link https://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.ack
117 | */
118 | public function ack($multiple = false)
119 | {
120 | $this->assertUnacked();
121 | $this->channel->basic_ack($this->deliveryTag, $multiple);
122 | $this->onResponse();
123 | }
124 |
125 | /**
126 | * Reject one or more incoming messages.
127 | *
128 | * @param bool $requeue If true, the server will attempt to requeue the message. If requeue is false or the requeue
129 | * attempt fails the messages are discarded or dead-lettered.
130 | * @param bool $multiple If true, the delivery tag is treated as "up to and including",
131 | * so that multiple messages can be rejected with a single method.
132 | * @since 2.12.0
133 | * @link https://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.nack
134 | */
135 | public function nack($requeue = false, $multiple = false)
136 | {
137 | $this->assertUnacked();
138 | $this->channel->basic_nack($this->deliveryTag, $multiple, $requeue);
139 | $this->onResponse();
140 | }
141 |
142 | /**
143 | * Reject an incoming message.
144 | *
145 | * @param bool $requeue If requeue is true, the server will attempt to requeue the message.
146 | * If requeue is false or the requeue attempt fails the messages are discarded or dead-lettered.
147 | * @since 2.12.0
148 | * @link https://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.reject
149 | */
150 | public function reject($requeue = true)
151 | {
152 | $this->assertUnacked();
153 | $this->channel->basic_reject($this->deliveryTag, $requeue);
154 | $this->onResponse();
155 | }
156 |
157 | /**
158 | * @throws \LogicException When response to broker was already sent.
159 | */
160 | protected function assertUnacked()
161 | {
162 | if (!$this->channel || $this->responded) {
163 | throw new \LogicException('Message is not published or response was already sent');
164 | }
165 | }
166 |
167 | protected function onResponse()
168 | {
169 | $this->responded = true;
170 | }
171 |
172 | /**
173 | * @return AMQPChannel|null
174 | * @since 2.12.0
175 | */
176 | public function getChannel()
177 | {
178 | return $this->channel;
179 | }
180 |
181 | /**
182 | * @param AMQPChannel $channel
183 | * @return $this
184 | * @throws \RuntimeException
185 | * @since 2.12.0
186 | */
187 | public function setChannel($channel)
188 | {
189 | if ($this->channel) {
190 | throw new \RuntimeException('A message is already assigned to channel');
191 | }
192 | $this->channel = $channel;
193 | $this->delivery_info['channel'] = $channel;
194 |
195 | return $this;
196 | }
197 |
198 | /**
199 | * @param int $deliveryTag
200 | * @param bool $redelivered
201 | * @param string $exchange
202 | * @param string $routingKey
203 | * @return $this
204 | * @since 2.12.0
205 | */
206 | public function setDeliveryInfo($deliveryTag, $redelivered, $exchange, $routingKey)
207 | {
208 | $this->deliveryTag = $this->delivery_info['delivery_tag'] = $deliveryTag;
209 | $this->redelivered = $this->delivery_info['redelivered'] = $redelivered;
210 | $this->exchange = $this->delivery_info['exchange'] = $exchange;
211 | $this->routingKey = $this->delivery_info['routing_key'] = $routingKey;
212 |
213 | return $this;
214 | }
215 |
216 | /**
217 | * @return bool|null
218 | * @since 2.12.0
219 | */
220 | public function isRedelivered()
221 | {
222 | return $this->redelivered;
223 | }
224 |
225 | /**
226 | * @return string|null
227 | * @since 2.12.0
228 | */
229 | public function getExchange()
230 | {
231 | return $this->exchange;
232 | }
233 |
234 | /**
235 | * @return string|null
236 | * @since 2.12.0
237 | */
238 | public function getRoutingKey()
239 | {
240 | return $this->routingKey;
241 | }
242 |
243 | /**
244 | * @return string|null
245 | * @since 2.12.0
246 | */
247 | public function getConsumerTag()
248 | {
249 | return $this->consumerTag;
250 | }
251 |
252 | /**
253 | * @param string $consumerTag
254 | * @return $this
255 | * @since 2.12.0
256 | */
257 | public function setConsumerTag($consumerTag)
258 | {
259 | $this->consumerTag = $consumerTag;
260 | $this->delivery_info['consumer_tag'] = $consumerTag;
261 |
262 | return $this;
263 | }
264 |
265 | /**
266 | * @return int|null
267 | * @since 2.12.0
268 | */
269 | public function getMessageCount()
270 | {
271 | return $this->messageCount;
272 | }
273 |
274 | /**
275 | * @param int $messageCount
276 | * @return $this
277 | * @since 2.12.0
278 | */
279 | public function setMessageCount($messageCount)
280 | {
281 | $this->messageCount = (int)$messageCount;
282 | $this->delivery_info['message_count'] = $this->messageCount;
283 |
284 | return $this;
285 | }
286 |
287 | /**
288 | * @return string
289 | */
290 | public function getBody()
291 | {
292 | return $this->body;
293 | }
294 |
295 | /**
296 | * Sets the message payload
297 | *
298 | * @param string $body
299 | * @return $this
300 | */
301 | public function setBody($body)
302 | {
303 | $this->body = $body;
304 |
305 | return $this;
306 | }
307 |
308 | /**
309 | * @return string
310 | */
311 | public function getContentEncoding()
312 | {
313 | return $this->content_encoding;
314 | }
315 |
316 | /**
317 | * @return int
318 | */
319 | public function getBodySize()
320 | {
321 | return $this->body_size;
322 | }
323 |
324 | /**
325 | * @param int $body_size Message body size in byte(s)
326 | * @return AMQPMessage
327 | */
328 | public function setBodySize($body_size)
329 | {
330 | $this->body_size = (int)$body_size;
331 |
332 | return $this;
333 | }
334 |
335 | /**
336 | * @return boolean
337 | */
338 | public function isTruncated()
339 | {
340 | return $this->is_truncated;
341 | }
342 |
343 | /**
344 | * @param bool $is_truncated
345 | * @return AMQPMessage
346 | */
347 | public function setIsTruncated($is_truncated)
348 | {
349 | $this->is_truncated = (bool)$is_truncated;
350 |
351 | return $this;
352 | }
353 |
354 | /**
355 | * @param int|string $deliveryTag
356 | * @return $this
357 | * @since 2.12.0
358 | */
359 | public function setDeliveryTag($deliveryTag)
360 | {
361 | if (!empty($this->deliveryTag)) {
362 | throw new \LogicException('Delivery tag cannot be changed');
363 | }
364 | $this->deliveryTag = $deliveryTag;
365 | $this->delivery_info['delivery_tag'] = $deliveryTag;
366 |
367 | return $this;
368 | }
369 |
370 | /**
371 | * @return int
372 | *
373 | * @throws AMQPEmptyDeliveryTagException
374 | */
375 | public function getDeliveryTag()
376 | {
377 | if (empty($this->deliveryTag)) {
378 | throw new AMQPEmptyDeliveryTagException('This message was not delivered yet');
379 | }
380 |
381 | return $this->deliveryTag;
382 | }
383 |
384 | /**
385 | * Check whether a property exists in the 'properties' dictionary
386 | * or if present - in the 'delivery_info' dictionary.
387 | *
388 | * @param string $name
389 | * @return bool
390 | */
391 | public function has($name)
392 | {
393 | return isset($this->properties[$name]) || isset($this->delivery_info[$name]);
394 | }
395 |
396 | /**
397 | * Look for additional properties in the 'properties' dictionary,
398 | * and if present - the 'delivery_info' dictionary.
399 | *
400 | * @param string $name
401 | * @return mixed|AMQPChannel
402 | * @throws \OutOfBoundsException
403 | */
404 | public function get($name)
405 | {
406 | if (isset($this->properties[$name])) {
407 | return $this->properties[$name];
408 | }
409 |
410 | if (isset($this->delivery_info[$name])) {
411 | return $this->delivery_info[$name];
412 | }
413 |
414 | throw new \OutOfBoundsException(sprintf(
415 | 'No "%s" property',
416 | $name
417 | ));
418 | }
419 |
420 | /**
421 | * Returns the properties content
422 | *
423 | * @return array
424 | */
425 | public function get_properties()
426 | {
427 | return $this->properties;
428 | }
429 |
430 | /**
431 | * Sets a property value
432 | *
433 | * @param string $name The property name (one of the property definition)
434 | * @param mixed $value The property value
435 | * @throws \OutOfBoundsException
436 | */
437 | public function set($name, $value)
438 | {
439 | if (!array_key_exists($name, self::$propertyDefinitions)) {
440 | throw new \OutOfBoundsException(sprintf(
441 | 'No "%s" property',
442 | $name
443 | ));
444 | }
445 |
446 | if (isset($this->properties[$name]) && $this->properties[$name] === $value) {
447 | // same value, nothing to do
448 | return;
449 | }
450 |
451 | $this->properties[$name] = $value;
452 | $this->serialized_properties = null;
453 | }
454 |
455 | /**
456 | * Given the raw bytes containing the property-flags and
457 | * property-list from a content-frame-header, parse and insert
458 | * into a dictionary stored in this object as an attribute named
459 | * 'properties'.
460 | *
461 | * @param AMQPReader $reader
462 | * NOTE: do not mutate $reader
463 | * @return $this
464 | */
465 | public function load_properties(AMQPReader $reader)
466 | {
467 | // Read 16-bit shorts until we get one with a low bit set to zero
468 | $flags = array();
469 |
470 | while (true) {
471 | $flag_bits = $reader->read_short();
472 | $flags[] = $flag_bits;
473 |
474 | if (($flag_bits & 1) === 0) {
475 | break;
476 | }
477 | }
478 |
479 | $shift = 0;
480 | $data = array();
481 |
482 | foreach (self::$propertyDefinitions as $key => $proptype) {
483 | if ($shift === 0) {
484 | if (!$flags) {
485 | break;
486 | }
487 | $flag_bits = array_shift($flags);
488 | $shift = 15;
489 | }
490 |
491 | if ($flag_bits & (1 << $shift)) {
492 | $data[$key] = $reader->{'read_' . $proptype}();
493 | }
494 |
495 | $shift -= 1;
496 | }
497 |
498 | $this->properties = $data;
499 |
500 | return $this;
501 | }
502 |
503 |
504 | /**
505 | * Serializes the 'properties' attribute (a dictionary) into the
506 | * raw bytes making up a set of property flags and a property
507 | * list, suitable for putting into a content frame header.
508 | *
509 | * @return string
510 | * @todo Inject the AMQPWriter to make the method easier to test
511 | */
512 | public function serialize_properties()
513 | {
514 | if (!empty($this->serialized_properties)) {
515 | return $this->serialized_properties;
516 | }
517 |
518 | $shift = 15;
519 | $flag_bits = 0;
520 | $flags = array();
521 | $raw_bytes = new AMQPWriter();
522 |
523 | foreach (self::$propertyDefinitions as $key => $prototype) {
524 | $val = isset($this->properties[$key]) ? $this->properties[$key] : null;
525 |
526 | // Very important: PHP type eval is weak, use the === to test the
527 | // value content. Zero or false value should not be removed
528 | if ($val === null) {
529 | $shift -= 1;
530 | continue;
531 | }
532 |
533 | if ($shift === 0) {
534 | $flags[] = $flag_bits;
535 | $flag_bits = 0;
536 | $shift = 15;
537 | }
538 |
539 | $flag_bits |= (1 << $shift);
540 | if ($prototype !== 'bit') {
541 | $raw_bytes->{'write_' . $prototype}($val);
542 | }
543 |
544 | $shift -= 1;
545 | }
546 |
547 | $flags[] = $flag_bits;
548 | $result = new AMQPWriter();
549 | foreach ($flags as $flag_bits) {
550 | $result->write_short($flag_bits);
551 | }
552 |
553 | $result->write($raw_bytes->getvalue());
554 |
555 | $this->serialized_properties = $result->getvalue();
556 |
557 | return $this->serialized_properties;
558 | }
559 | }
560 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Package.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib;
4 |
5 | final class Package
6 | {
7 | public const NAME = 'AMQPLib';
8 | public const VERSION = '3.7.3';
9 | }
10 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/AMQPAbstractCollection.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire;
4 |
5 | use PhpAmqpLib\Channel\AbstractChannel;
6 | use PhpAmqpLib\Exception;
7 | use PhpAmqpLib\Wire;
8 |
9 | /**
10 | * Iterator implemented for transparent integration with AMQPWriter::write_[array|table]()
11 | */
12 | abstract class AMQPAbstractCollection implements \Iterator, \ArrayAccess
13 | {
14 | //protocol defines available field types and their corresponding symbols
15 | const PROTOCOL_RBT = 'rabbit'; //pseudo proto
16 |
17 | //Abstract data types
18 | const T_INT_SHORTSHORT = 1;
19 | const T_INT_SHORTSHORT_U = 2;
20 | const T_INT_SHORT = 3;
21 | const T_INT_SHORT_U = 4;
22 | const T_INT_LONG = 5;
23 | const T_INT_LONG_U = 6;
24 | const T_INT_LONGLONG = 7;
25 | const T_INT_LONGLONG_U = 8;
26 |
27 | const T_DECIMAL = 9;
28 | const T_TIMESTAMP = 10;
29 | const T_VOID = 11;
30 |
31 | const T_BOOL = 12;
32 |
33 | const T_STRING_SHORT = 13;
34 | const T_STRING_LONG = 14;
35 |
36 | const T_ARRAY = 15;
37 | const T_TABLE = 16;
38 |
39 | const T_BYTES = 17;
40 |
41 | const T_FLOAT = 18;
42 | const T_DOUBLE = 19;
43 |
44 | /**
45 | * @var string
46 | */
47 | private static $protocol;
48 |
49 | /*
50 | * Field types messy mess http://www.rabbitmq.com/amqp-0-9-1-errata.html#section_3
51 | * Default behaviour is to use rabbitMQ compatible field-set
52 | * Define AMQP_STRICT_FLD_TYPES=true to use strict AMQP instead
53 | * @var array<int, string>
54 | * @deprecated
55 | */
56 | private static $types_080 = array(
57 | self::T_INT_LONG => 'I',
58 | self::T_DECIMAL => 'D',
59 | self::T_TIMESTAMP => 'T',
60 | self::T_STRING_LONG => 'S',
61 | self::T_TABLE => 'F'
62 | );
63 |
64 | /**
65 | * @var array<int, string>
66 | */
67 | private static $types_091 = array(
68 | self::T_INT_SHORTSHORT => 'b',
69 | self::T_INT_SHORTSHORT_U => 'B',
70 | self::T_INT_SHORT => 'U',
71 | self::T_INT_SHORT_U => 'u',
72 | self::T_INT_LONG => 'I',
73 | self::T_INT_LONG_U => 'i',
74 | self::T_INT_LONGLONG => 'L',
75 | self::T_INT_LONGLONG_U => 'l',
76 | self::T_FLOAT => 'f',
77 | self::T_DOUBLE => 'd',
78 | self::T_DECIMAL => 'D',
79 | self::T_TIMESTAMP => 'T',
80 | self::T_VOID => 'V',
81 | self::T_BOOL => 't',
82 | self::T_STRING_SHORT => 's',
83 | self::T_STRING_LONG => 'S',
84 | self::T_ARRAY => 'A',
85 | self::T_TABLE => 'F',
86 | self::T_BYTES => 'x',
87 | );
88 |
89 | /**
90 | * @var array<int, string>
91 | */
92 | private static $types_rabbit = array(
93 | self::T_INT_SHORTSHORT => 'b',
94 | self::T_INT_SHORTSHORT_U => 'B',
95 | self::T_INT_SHORT => 's',
96 | self::T_INT_SHORT_U => 'u',
97 | self::T_INT_LONG => 'I',
98 | self::T_INT_LONG_U => 'i',
99 | self::T_INT_LONGLONG => 'l',
100 | self::T_FLOAT => 'f',
101 | self::T_DOUBLE => 'd',
102 | self::T_DECIMAL => 'D',
103 | self::T_TIMESTAMP => 'T',
104 | self::T_VOID => 'V',
105 | self::T_BOOL => 't',
106 | self::T_STRING_LONG => 'S',
107 | self::T_ARRAY => 'A',
108 | self::T_TABLE => 'F',
109 | self::T_BYTES => 'x',
110 | );
111 |
112 | /**
113 | * @var array
114 | */
115 | protected $data = array();
116 |
117 | public function __construct(?array $data = null)
118 | {
119 | if (!empty($data)) {
120 | $this->data = $this->encodeCollection($data);
121 | }
122 | }
123 |
124 | /**
125 | * @return int
126 | */
127 | abstract public function getType();
128 |
129 | /**
130 | * @param mixed $val
131 | * @param int|null $type
132 | * @param string $key
133 | */
134 | final protected function setValue($val, $type = null, $key = null)
135 | {
136 | if ($val instanceof self) {
137 | if ($type && ($type !== $val->getType())) {
138 | throw new Exception\AMQPInvalidArgumentException(
139 | sprintf(
140 | 'Attempted to add instance of %s representing type [%s] as mismatching type [%s]',
141 | get_class($val),
142 | $val->getType(),
143 | $type
144 | )
145 | );
146 | }
147 | $type = $val->getType();
148 | } elseif ($type) { //ensuring data integrity and that all members are properly validated
149 | switch ($type) {
150 | case self::T_ARRAY:
151 | throw new Exception\AMQPInvalidArgumentException('Arrays must be passed as AMQPArray instance');
152 | case self::T_TABLE:
153 | throw new Exception\AMQPInvalidArgumentException('Tables must be passed as AMQPTable instance');
154 | case self::T_DECIMAL:
155 | if (!($val instanceof AMQPDecimal)) {
156 | throw new Exception\AMQPInvalidArgumentException(
157 | 'Decimal values must be instance of AMQPDecimal'
158 | );
159 | }
160 | break;
161 | }
162 | }
163 |
164 | if ($type) {
165 | self::checkDataTypeIsSupported($type, false);
166 | $val = array($type, $val);
167 | } else {
168 | $val = $this->encodeValue($val);
169 | }
170 |
171 | if ($key === null) {
172 | $this->data[] = $val;
173 | } else {
174 | $this->data[$key] = $val;
175 | }
176 | }
177 |
178 | /**
179 | * @return array
180 | */
181 | final public function getNativeData()
182 | {
183 | return $this->decodeCollection($this->data);
184 | }
185 |
186 | /**
187 | * @param array $val
188 | * @return array
189 | */
190 | final protected function encodeCollection(array $val)
191 | {
192 | foreach ($val as $k => $v) {
193 | $val[$k] = $this->encodeValue($v);
194 | }
195 |
196 | return $val;
197 | }
198 |
199 | /**
200 | * @param array $val
201 | * @return array
202 | */
203 | final protected function decodeCollection(array $val)
204 | {
205 | foreach ($val as $k => $v) {
206 | $val[$k] = $this->decodeValue($v[1], $v[0]);
207 | }
208 |
209 | return $val;
210 | }
211 |
212 | public function offsetExists($offset): bool
213 | {
214 | return isset($this->data[$offset]);
215 | }
216 |
217 | /**
218 | * @param mixed $offset
219 | * @return mixed
220 | */
221 | #[\ReturnTypeWillChange]
222 | public function offsetGet($offset)
223 | {
224 | $value = isset($this->data[$offset]) ? $this->data[$offset] : null;
225 |
226 | return is_array($value) ? $value[1] : $value;
227 | }
228 |
229 | public function offsetSet($offset, $value): void
230 | {
231 | $this->setValue($value, null, $offset);
232 | }
233 |
234 | public function offsetUnset($offset): void
235 | {
236 | unset($this->data[$offset]);
237 | }
238 |
239 | /**
240 | * @param mixed $val
241 | * @return mixed
242 | * @throws Exception\AMQPOutOfBoundsException
243 | */
244 | protected function encodeValue($val)
245 | {
246 | if (is_string($val)) {
247 | $val = $this->encodeString($val);
248 | } elseif (is_float($val)) {
249 | $val = $this->encodeFloat($val);
250 | } elseif (is_int($val)) {
251 | $val = $this->encodeInt($val);
252 | } elseif (is_bool($val)) {
253 | $val = $this->encodeBool($val);
254 | } elseif (is_null($val)) {
255 | $val = $this->encodeVoid();
256 | } elseif ($val instanceof \DateTimeInterface) {
257 | $val = array(self::T_TIMESTAMP, $val->getTimestamp());
258 | } elseif ($val instanceof AMQPDecimal) {
259 | $val = array(self::T_DECIMAL, $val);
260 | } elseif ($val instanceof self) {
261 | //avoid silent type correction of strictly typed values
262 | self::checkDataTypeIsSupported($val->getType(), false);
263 | $val = array($val->getType(), $val);
264 | } elseif (is_array($val)) {
265 | //AMQP specs says "Field names MUST start with a letter, '#39; or '#'"
266 | //so beware, some servers may raise an exception with 503 code in cases when indexed
267 | // array is encoded as table
268 | if (self::isProtocol(Wire\Constants080::VERSION)) {
269 | //080 doesn't support arrays, forcing table
270 | $val = array(self::T_TABLE, new AMQPTable($val));
271 | } elseif (empty($val) || (array_keys($val) === range(0, count($val) - 1))) {
272 | $val = array(self::T_ARRAY, new AMQPArray($val));
273 | } else {
274 | $val = array(self::T_TABLE, new AMQPTable($val));
275 | }
276 | } else {
277 | throw new Exception\AMQPOutOfBoundsException(
278 | sprintf('Encountered value of unsupported type: %s', gettype($val))
279 | );
280 | }
281 |
282 | return $val;
283 | }
284 |
285 | /**
286 | * @param mixed $val
287 | * @param int $type
288 | * @return array|bool|\DateTime|null
289 | */
290 | protected function decodeValue($val, $type)
291 | {
292 | if ($val instanceof self) {
293 | //covering arrays and tables
294 | $val = $val->getNativeData();
295 | } else {
296 | switch ($type) {
297 | case self::T_BOOL:
298 | $val = (bool) $val;
299 | break;
300 | case self::T_TIMESTAMP:
301 | $val = \DateTime::createFromFormat('U', $val);
302 | break;
303 | case self::T_VOID:
304 | $val = null;
305 | break;
306 | case self::T_ARRAY:
307 | case self::T_TABLE:
308 | throw new Exception\AMQPLogicException(
309 | sprintf(
310 | '%s %s',
311 | 'Encountered an array/table struct which is not an instance of AMQPCollection.',
312 | 'This is considered a bug and should be fixed, please report'
313 | )
314 | );
315 | }
316 | }
317 |
318 | return $val;
319 | }
320 |
321 | /**
322 | * @param string $val
323 | * @return array
324 | */
325 | protected function encodeString($val)
326 | {
327 | return array(self::T_STRING_LONG, $val);
328 | }
329 |
330 | /**
331 | * @param int $val
332 | * @return array
333 | */
334 | protected function encodeInt($val)
335 | {
336 | if (($val >= -2147483648) && ($val <= 2147483647)) {
337 | $ev = array(self::T_INT_LONG, $val);
338 | } elseif (self::isProtocol(Wire\Constants080::VERSION)) {
339 | //080 doesn't support longlong
340 | $ev = $this->encodeString((string) $val);
341 | } else {
342 | $ev = array(self::T_INT_LONGLONG, $val);
343 | }
344 |
345 | return $ev;
346 | }
347 |
348 | /**
349 | * @param float $val
350 | * @return array
351 | */
352 | protected function encodeFloat($val)
353 | {
354 | return $this->encodeString((string) $val);
355 | }
356 |
357 | /**
358 | * @param bool $val
359 | * @return array
360 | */
361 | protected function encodeBool($val)
362 | {
363 | $val = (bool) $val;
364 |
365 | return self::isProtocol(Wire\Constants080::VERSION)
366 | ? array(self::T_INT_LONG, (int) $val)
367 | : array(self::T_BOOL, $val);
368 | }
369 |
370 | /**
371 | * @return array
372 | */
373 | protected function encodeVoid()
374 | {
375 | return self::isProtocol(Wire\Constants080::VERSION) ? $this->encodeString('') : array(self::T_VOID, null);
376 | }
377 |
378 | /**
379 | * @return string
380 | * @deprecated
381 | */
382 | final public static function getProtocol()
383 | {
384 | if (self::$protocol === null) {
385 | self::$protocol = defined('AMQP_STRICT_FLD_TYPES') && AMQP_STRICT_FLD_TYPES ?
386 | AbstractChannel::getProtocolVersion() :
387 | self::PROTOCOL_RBT;
388 | }
389 |
390 | return self::$protocol;
391 | }
392 |
393 | /**
394 | * @param string $proto
395 | * @return bool
396 | */
397 | final public static function isProtocol($proto)
398 | {
399 | return self::getProtocol() === $proto;
400 | }
401 |
402 | /**
403 | * @return array [dataTypeConstant => dataTypeSymbol]
404 | */
405 | final public static function getSupportedDataTypes()
406 | {
407 | switch ($proto = self::getProtocol()) {
408 | case Wire\Constants080::VERSION:
409 | $types = self::$types_080;
410 | break;
411 | case Wire\Constants091::VERSION:
412 | $types = self::$types_091;
413 | break;
414 | case self::PROTOCOL_RBT:
415 | $types = self::$types_rabbit;
416 | break;
417 | default:
418 | throw new Exception\AMQPOutOfRangeException(sprintf('Unknown protocol: %s', $proto));
419 | }
420 |
421 | return $types;
422 | }
423 |
424 | /**
425 | * @param string $type
426 | * @param bool $return Whether to return or raise AMQPOutOfRangeException
427 | * @return boolean
428 | */
429 | final public static function checkDataTypeIsSupported($type, $return = true)
430 | {
431 | try {
432 | $supported = self::getSupportedDataTypes();
433 | if (!isset($supported[$type])) {
434 | throw new Exception\AMQPOutOfRangeException(sprintf(
435 | 'AMQP-%s doesn\'t support data of type [%s]',
436 | self::getProtocol(),
437 | $type
438 | ));
439 | }
440 | return true;
441 | } catch (Exception\AMQPOutOfRangeException $ex) {
442 | if (!$return) {
443 | throw $ex;
444 | }
445 |
446 | return false;
447 | }
448 | }
449 |
450 | /**
451 | * @param int $type
452 | * @return string
453 | */
454 | final public static function getSymbolForDataType($type)
455 | {
456 | $types = self::getSupportedDataTypes();
457 | if (!isset($types[$type])) {
458 | throw new Exception\AMQPOutOfRangeException(sprintf(
459 | 'AMQP-%s doesn\'t support data of type [%s]',
460 | self::getProtocol(),
461 | $type
462 | ));
463 | }
464 |
465 | return $types[$type];
466 | }
467 |
468 | /**
469 | * @param string $symbol
470 | * @return integer
471 | */
472 | final public static function getDataTypeForSymbol($symbol)
473 | {
474 | $symbols = array_flip(self::getSupportedDataTypes());
475 | if (!isset($symbols[$symbol])) {
476 | throw new Exception\AMQPOutOfRangeException(sprintf(
477 | 'AMQP-%s doesn\'t define data of type [%s]',
478 | self::getProtocol(),
479 | $symbol
480 | ));
481 | }
482 |
483 | return $symbols[$symbol];
484 | }
485 |
486 | /**
487 | * @return mixed
488 | */
489 | #[\ReturnTypeWillChange]
490 | public function current()
491 | {
492 | return current($this->data);
493 | }
494 |
495 | /**
496 | * @return mixed
497 | */
498 | #[\ReturnTypeWillChange]
499 | public function key()
500 | {
501 | return key($this->data);
502 | }
503 |
504 | public function next(): void
505 | {
506 | next($this->data);
507 | }
508 |
509 | public function rewind(): void
510 | {
511 | reset($this->data);
512 | }
513 |
514 | public function valid(): bool
515 | {
516 | return key($this->data) !== null;
517 | }
518 | }
519 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/AMQPArray.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire;
4 |
5 | class AMQPArray extends AMQPAbstractCollection
6 | {
7 |
8 | /**
9 | * @param array|null $data
10 | */
11 | public function __construct(?array $data = null)
12 | {
13 | parent::__construct(empty($data) ? null : array_values($data));
14 | }
15 |
16 | /**
17 | * @return int
18 | */
19 | final public function getType()
20 | {
21 | return self::T_ARRAY;
22 | }
23 |
24 | /**
25 | * @param mixed $val
26 | * @param int|null $type
27 | * @return $this
28 | */
29 | public function push($val, $type = null)
30 | {
31 | $this->setValue($val, $type);
32 |
33 | return $this;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/AMQPBufferReader.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire;
4 |
5 | use PhpAmqpLib\Exception\AMQPDataReadException;
6 |
7 | class AMQPBufferReader extends AMQPReader
8 | {
9 | /**
10 | * @var string
11 | */
12 | private $buffer;
13 |
14 | /**
15 | * @var int
16 | */
17 | private $length;
18 |
19 | public function __construct(string $buffer)
20 | {
21 | $this->buffer = $buffer;
22 | $this->length = mb_strlen($buffer, 'ASCII');
23 | }
24 |
25 | public function close(): void
26 | {
27 | }
28 |
29 | /**
30 | * Resets the object from the injected param
31 | *
32 | * Used to not need to create a new AMQPBufferReader instance every time.
33 | * when we can just pass a string and reset the object state.
34 | * NOTE: since we are working with strings we don't need to pass an AbstractIO
35 | * or a timeout.
36 | *
37 | * @param string $str
38 | */
39 | public function reset(string $str): void
40 | {
41 | $this->buffer = $str;
42 | $this->length = mb_strlen($this->buffer, 'ASCII');
43 | $this->offset = 0;
44 | $this->resetCounters();
45 | }
46 |
47 | protected function rawread(int $n): string
48 | {
49 | if ($this->length < $n) {
50 | throw new AMQPDataReadException(sprintf(
51 | 'Error reading data. Requested %s bytes while string buffer has only %s',
52 | $n,
53 | $this->length
54 | ));
55 | }
56 |
57 | $res = mb_substr($this->buffer, 0, $n, 'ASCII');
58 | $this->buffer = mb_substr($this->buffer, $n, null, 'ASCII');
59 | $this->length -= $n;
60 | $this->offset += $n;
61 |
62 | return $res;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/AMQPByteStream.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire;
4 |
5 | use PhpAmqpLib\Helper\BigInteger;
6 |
7 | abstract class AMQPByteStream
8 | {
9 | public const BIT = 1;
10 | public const OCTET = 1;
11 | public const SHORTSTR = 1;
12 | public const SHORT = 2;
13 | public const LONG = 4;
14 | public const SIGNED_LONG = 4;
15 | public const READ_PHP_INT = 4; // use READ_ to avoid possible clashes with PHP
16 | public const LONGLONG = 8;
17 | public const TIMESTAMP = 8;
18 |
19 | /** @var bool */
20 | protected const PLATFORM_64BIT = PHP_INT_SIZE === 8;
21 |
22 | /** @var BigInteger[][] */
23 | protected static $bigIntegers = array();
24 |
25 | /**
26 | * @var bool
27 | */
28 | protected static $isLittleEndian;
29 |
30 | /**
31 | * Converts byte-string between native and network byte order, in both directions
32 | *
33 | * @param string $bytes
34 | * @return string
35 | */
36 | protected function correctEndianness($bytes)
37 | {
38 | return self::isLittleEndian() ? $this->convertByteOrder($bytes) : $bytes;
39 | }
40 |
41 | /**
42 | * @param string $bytes
43 | * @return string
44 | */
45 | protected function convertByteOrder($bytes)
46 | {
47 | return strrev($bytes);
48 | }
49 |
50 | /**
51 | * @param int $longInt
52 | * @return bool
53 | */
54 | protected function getLongMSB($longInt)
55 | {
56 | return (bool) ($longInt & 0x80000000);
57 | }
58 |
59 | /**
60 | * @param string $bytes
61 | * @return bool
62 | */
63 | protected function getMSB($bytes)
64 | {
65 | return ord($bytes[0]) > 127;
66 | }
67 |
68 | /**
69 | * @return bool
70 | */
71 | protected static function isLittleEndian()
72 | {
73 | if (self::$isLittleEndian === null) {
74 | $tmp = unpack('S', "\x01\x00"); // to maintain 5.3 compatibility
75 | self::$isLittleEndian = $tmp[1] === 1;
76 | }
77 |
78 | return self::$isLittleEndian;
79 | }
80 |
81 | /**
82 | * @param string $value
83 | * @param int $base
84 | * @return BigInteger
85 | */
86 | protected static function getBigInteger($value, $base = 10)
87 | {
88 | if (!isset(self::$bigIntegers[$base])) {
89 | self::$bigIntegers[$base] = array();
90 | }
91 | if (isset(self::$bigIntegers[$base][$value])) {
92 | return self::$bigIntegers[$base][$value];
93 | }
94 |
95 | $integer = new BigInteger($value, $base);
96 | self::$bigIntegers[$base][$value] = $integer;
97 |
98 | return $integer;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/AMQPDecimal.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire;
4 |
5 | use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
6 | use PhpAmqpLib\Helper\BigInteger;
7 |
8 | /**
9 | * AMQP protocol decimal value.
10 | *
11 | * Values are represented as (n,e) pairs. The actual value
12 | * is n * 10^(-e).
13 | *
14 | * From 0.8 spec: Decimal values are
15 | * not intended to support floating point values, but rather
16 | * business values such as currency rates and amounts. The
17 | * 'decimals' octet is not signed.
18 | */
19 | class AMQPDecimal
20 | {
21 | /** @var int */
22 | protected $n;
23 |
24 | /** @var int */
25 | protected $e;
26 |
27 | /**
28 | * @param int $n
29 | * @param int $e
30 | * @throws \PhpAmqpLib\Exception\AMQPOutOfBoundsException
31 | */
32 | public function __construct($n, $e)
33 | {
34 | if ($e < 0) {
35 | throw new AMQPOutOfBoundsException('Decimal exponent value must be unsigned!');
36 | }
37 |
38 | $this->n = $n;
39 | $this->e = $e;
40 | }
41 |
42 | /**
43 | * @return string
44 | */
45 | public function asBCvalue()
46 | {
47 | $n = new BigInteger($this->n);
48 | $e = new BigInteger('1' . str_repeat('0', $this->e));
49 | list($q) = $n->divide($e);
50 | return $q->toString();
51 | }
52 |
53 | /**
54 | * @return int
55 | */
56 | public function getE()
57 | {
58 | return $this->e;
59 | }
60 |
61 | /**
62 | * @return int
63 | */
64 | public function getN()
65 | {
66 | return $this->n;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/AMQPIOReader.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire;
4 |
5 | use PhpAmqpLib\Exception\AMQPDataReadException;
6 | use PhpAmqpLib\Exception\AMQPIOException;
7 | use PhpAmqpLib\Exception\AMQPNoDataException;
8 | use PhpAmqpLib\Exception\AMQPTimeoutException;
9 | use PhpAmqpLib\Helper\MiscHelper;
10 | use PhpAmqpLib\Wire\IO\AbstractIO;
11 | use RuntimeException;
12 |
13 | class AMQPIOReader extends AMQPReader
14 | {
15 | /** @var AbstractIO */
16 | private $io;
17 |
18 | /** @var int|float|null */
19 | protected $timeout;
20 |
21 | public function __construct(AbstractIO $io, $timeout = 0)
22 | {
23 | $this->io = $io;
24 | $this->timeout = $timeout;
25 | }
26 |
27 | public function close(): void
28 | {
29 | $this->io->close();
30 | }
31 |
32 | /**
33 | * @return float|int|mixed|null
34 | */
35 | public function getTimeout()
36 | {
37 | return $this->timeout;
38 | }
39 |
40 | /**
41 | * Sets the timeout (second)
42 | *
43 | * @param int|float|null $timeout
44 | */
45 | public function setTimeout($timeout)
46 | {
47 | $this->timeout = $timeout;
48 | }
49 |
50 | /**
51 | * @param int $n
52 | * @return string
53 | * @throws RuntimeException
54 | * @throws AMQPDataReadException|AMQPNoDataException|AMQPIOException
55 | */
56 | protected function rawread(int $n): string
57 | {
58 | $res = '';
59 | while (true) {
60 | $this->wait();
61 | try {
62 | $res = $this->io->read($n);
63 | break;
64 | } catch (AMQPTimeoutException $e) {
65 | if ($this->getTimeout() > 0) {
66 | throw $e;
67 | }
68 | }
69 | }
70 | $this->offset += $n;
71 |
72 | return $res;
73 | }
74 |
75 | /**
76 | * Waits until some data is retrieved from the socket.
77 | *
78 | * AMQPTimeoutException can be raised if the timeout is set
79 | *
80 | * @throws AMQPTimeoutException when timeout is set and no data received
81 | * @throws AMQPNoDataException when no data is ready to read from IO
82 | */
83 | protected function wait(): void
84 | {
85 | $timeout = $this->timeout;
86 | if (null === $timeout) {
87 | // timeout=null just poll state and return instantly
88 | $result = $this->io->select(0);
89 | if ($result === 0) {
90 | throw new AMQPNoDataException('No data is ready to read');
91 | }
92 | return;
93 | }
94 |
95 | if (!($timeout > 0)) {
96 | // wait indefinitely for data if timeout=0
97 | $result = $this->io->select(null);
98 | if ($result === 0) {
99 | throw new AMQPNoDataException('No data is ready to read');
100 | }
101 | return;
102 | }
103 |
104 | $leftTime = $timeout;
105 | $started = microtime(true);
106 | do {
107 | [$sec, $usec] = MiscHelper::splitSecondsMicroseconds($leftTime);
108 | $result = $this->io->select($sec, $usec);
109 | if ($result > 0) {
110 | return;
111 | }
112 | // select might be interrupted by signal, calculate left time and repeat
113 | $leftTime = $timeout - (microtime(true) - $started);
114 | } while ($leftTime > 0);
115 |
116 | throw new AMQPTimeoutException(sprintf(
117 | 'The connection timed out after %s sec while awaiting incoming data',
118 | $timeout
119 | ));
120 |
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/AMQPReader.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire;
4 |
5 | use PhpAmqpLib\Channel\Frame;
6 | use PhpAmqpLib\Exception\AMQPInvalidArgumentException;
7 | use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
8 | use PhpAmqpLib\Helper\BigInteger;
9 |
10 | abstract class AMQPReader extends AMQPByteStream
11 | {
12 | /** @var int */
13 | protected $offset = 0;
14 |
15 | /** @var int */
16 | protected $bitcount = 0;
17 |
18 | /** @var int */
19 | protected $bits = 0;
20 |
21 | /**
22 | * Close the byte stream.
23 | */
24 | abstract public function close(): void;
25 |
26 | abstract protected function rawread(int $n): string;
27 |
28 | /**
29 | * @param int $n
30 | * @return string
31 | */
32 | public function read($n)
33 | {
34 | $this->resetCounters();
35 |
36 | return $this->rawread($n);
37 | }
38 |
39 | public function read_bit(): bool
40 | {
41 | if (empty($this->bitcount)) {
42 | $this->bits = ord($this->rawread(1));
43 | $this->bitcount = 8;
44 | }
45 |
46 | $result = ($this->bits & 1) === 1;
47 | $this->bits >>= 1;
48 | $this->bitcount--;
49 |
50 | return $result;
51 | }
52 |
53 | /**
54 | * @return int
55 | */
56 | public function read_octet()
57 | {
58 | $this->resetCounters();
59 | list(, $res) = unpack('C', $this->rawread(1));
60 |
61 | return $res;
62 | }
63 |
64 | /**
65 | * @return int
66 | */
67 | public function read_signed_octet()
68 | {
69 | $this->resetCounters();
70 | list(, $res) = unpack('c', $this->rawread(1));
71 |
72 | return $res;
73 | }
74 |
75 | /**
76 | * @return int
77 | */
78 | public function read_short()
79 | {
80 | $this->resetCounters();
81 | list(, $res) = unpack('n', $this->rawread(2));
82 |
83 | return $res;
84 | }
85 |
86 | /**
87 | * @return int
88 | */
89 | public function read_signed_short()
90 | {
91 | $this->resetCounters();
92 | list(, $res) = unpack('s', $this->correctEndianness($this->rawread(2)));
93 |
94 | return $res;
95 | }
96 |
97 | /**
98 | * Reads 32 bit integer in big-endian byte order.
99 | *
100 | * On 64 bit systems it will return always unsigned int
101 | * value in 0..2^32 range.
102 | *
103 | * On 32 bit systems it will return signed int value in
104 | * -2^31...+2^31 range.
105 | *
106 | * Use with caution!
107 | * @return int|string
108 | */
109 | public function read_php_int()
110 | {
111 | list(, $res) = unpack('N', $this->rawread(4));
112 |
113 | if (self::PLATFORM_64BIT) {
114 | return (int) sprintf('%u', $res);
115 | }
116 |
117 | return $res;
118 | }
119 |
120 | /**
121 | * PHP does not have unsigned 32 bit int,
122 | * so we return it as a string
123 | *
124 | * @return int|string
125 | */
126 | public function read_long()
127 | {
128 | $this->resetCounters();
129 | list(, $res) = unpack('N', $this->rawread(4));
130 | if (!self::PLATFORM_64BIT && $this->getLongMSB($res)) {
131 | return sprintf('%u', $res);
132 | }
133 |
134 | return $res;
135 | }
136 |
137 | /**
138 | * @return int
139 | */
140 | private function readSignedLong()
141 | {
142 | $this->resetCounters();
143 | list(, $res) = unpack('l', $this->correctEndianness($this->rawread(4)));
144 |
145 | return $res;
146 | }
147 |
148 | /**
149 | * Even on 64 bit systems PHP integers are signed.
150 | * Since we need an unsigned value here we return it as a string.
151 | *
152 | * @return int|string
153 | */
154 | public function read_longlong()
155 | {
156 | $this->resetCounters();
157 | $bytes = $this->rawread(8);
158 |
159 | if (self::PLATFORM_64BIT) {
160 | // we can "unpack" if MSB bit is 0 (at most 63 bit integer), fallback to BigInteger otherwise
161 | if (!$this->getMSB($bytes)) {
162 | $res = unpack('J', $bytes);
163 | return $res[1];
164 | }
165 | } else {
166 | // on 32-bit systems we can "unpack" up to 31 bits integer
167 | list(, $hi, $lo) = unpack('N2', $bytes);
168 | if ($hi === 0 && $lo > 0) {
169 | return $lo;
170 | }
171 | }
172 |
173 | $var = new BigInteger($bytes, 256);
174 |
175 | return $var->toString();
176 | }
177 |
178 | /**
179 | * @return int|string
180 | */
181 | public function read_signed_longlong()
182 | {
183 | $this->resetCounters();
184 | $bytes = $this->rawread(8);
185 |
186 | if (self::PLATFORM_64BIT) {
187 | $res = unpack('q', $this->correctEndianness($bytes));
188 | return $res[1];
189 | } else {
190 | // on 32-bit systems we can "unpack" up to 31 bits integer
191 | list(, $hi, $lo) = unpack('N2', $bytes);
192 | if ($hi === 0 && $lo > 0) {
193 | // positive and less than 2^31-1
194 | return $lo;
195 | }
196 | // negative and more than -2^31
197 | if ($hi === -1 && $this->getLongMSB($lo)) {
198 | return $lo;
199 | }
200 | }
201 |
202 | $var = new BigInteger($bytes, -256);
203 |
204 | return $var->toString();
205 | }
206 |
207 | /**
208 | * @return float
209 | */
210 | public function read_float()
211 | {
212 | $this->resetCounters();
213 | list(, $res) = unpack('G', $this->rawread(4));
214 |
215 | return (float)$res;
216 | }
217 |
218 | /**
219 | * @return float
220 | */
221 | public function read_double()
222 | {
223 | $this->resetCounters();
224 | list(, $res) = unpack('E', $this->rawread(8));
225 |
226 | return (float)$res;
227 | }
228 |
229 | /**
230 | * Read a utf-8 encoded string that's stored in up to
231 | * 255 bytes. Return it decoded as a PHP unicode object.
232 | * @return string
233 | */
234 | public function read_shortstr()
235 | {
236 | $this->resetCounters();
237 | list(, $slen) = unpack('C', $this->rawread(1));
238 |
239 | return $this->rawread($slen);
240 | }
241 |
242 | /**
243 | * Read a string that's up to 2**32 bytes, the encoding
244 | * isn't specified in the AMQP spec, so just return it as
245 | * a plain PHP string.
246 | * @return string
247 | */
248 | public function read_longstr()
249 | {
250 | $this->resetCounters();
251 | $slen = $this->read_php_int();
252 |
253 | if ($slen < 0) {
254 | throw new AMQPOutOfBoundsException('Strings longer than supported on this platform');
255 | }
256 |
257 | return $this->rawread($slen);
258 | }
259 |
260 | /**
261 | * Read and AMQP timestamp, which is a 64-bit integer representing
262 | * seconds since the Unix epoch in 1-second resolution.
263 | * @return int|string
264 | */
265 | public function read_timestamp()
266 | {
267 | return $this->read_longlong();
268 | }
269 |
270 | /**
271 | * Read an AMQP table, and return as a PHP array. keys are strings,
272 | * values are (type,value) tuples.
273 | *
274 | * @param bool $returnObject Whether to return AMQPArray instance instead of plain array
275 | * @return array|AMQPTable
276 | */
277 | public function read_table(bool $returnObject = false)
278 | {
279 | $this->resetCounters();
280 | $tlen = $this->read_php_int();
281 |
282 | if ($tlen < 0) {
283 | throw new AMQPOutOfBoundsException('Table is longer than supported');
284 | }
285 |
286 | $table_data = new AMQPBufferReader($this->rawread($tlen));
287 | $result = $returnObject ? new AMQPTable() : array();
288 |
289 | while ($table_data->tell() < $tlen) {
290 | $name = $table_data->read_shortstr();
291 | $ftype = AMQPAbstractCollection::getDataTypeForSymbol($ftypeSym = $table_data->rawread(1));
292 | $val = $table_data->read_value($ftype, $returnObject);
293 | $returnObject ? $result->set($name, $val, $ftype) : $result[$name] = array($ftypeSym, $val);
294 | }
295 |
296 | return $result;
297 | }
298 |
299 | /**
300 | * @return array|AMQPTable
301 | */
302 | public function read_table_object()
303 | {
304 | return $this->read_table(true);
305 | }
306 |
307 | /**
308 | * Reads the array in the next value.
309 | *
310 | * @param bool $returnObject Whether to return AMQPArray instance instead of plain array
311 | * @return array|AMQPArray
312 | */
313 | public function read_array($returnObject = false)
314 | {
315 | $this->resetCounters();
316 |
317 | // Determine array length and its end position
318 | $arrayLength = $this->read_php_int();
319 | $endOffset = $this->offset + $arrayLength;
320 |
321 | $result = $returnObject ? new AMQPArray() : array();
322 |
323 | // Read values until we reach the end of the array
324 | while ($this->offset < $endOffset) {
325 | $fieldType = AMQPAbstractCollection::getDataTypeForSymbol($this->rawread(1));
326 | $fieldValue = $this->read_value($fieldType, $returnObject);
327 | $returnObject ? $result->push($fieldValue, $fieldType) : $result[] = $fieldValue;
328 | }
329 |
330 | return $result;
331 | }
332 |
333 | /**
334 | * @return array|AMQPArray
335 | */
336 | public function read_array_object()
337 | {
338 | return $this->read_array(true);
339 | }
340 |
341 | /**
342 | * @return array{type:int, channel:int, size:int}
343 | */
344 | public function readFrameHeader(): array
345 | {
346 | return unpack('Ctype/nchannel/Nsize', $this->rawread(Frame::FRAME_HEADER_SIZE));
347 | }
348 |
349 | /**
350 | * Reads the next value as the provided field type.
351 | *
352 | * @param int $fieldType One of AMQPAbstractCollection::T_* constants
353 | * @param bool $collectionsAsObjects Description
354 | * @return mixed
355 | * @throws \PhpAmqpLib\Exception\AMQPDataReadException
356 | */
357 | public function read_value(int $fieldType, bool $collectionsAsObjects = false)
358 | {
359 | $this->resetCounters();
360 |
361 | switch ($fieldType) {
362 | case AMQPAbstractCollection::T_INT_SHORTSHORT:
363 | //according to AMQP091 spec, 'b' is not bit, it is short-short-int, also valid for rabbit/qpid
364 | //$val=$this->read_bit();
365 | $val = $this->read_signed_octet();
366 | break;
367 | case AMQPAbstractCollection::T_INT_SHORTSHORT_U:
368 | case AMQPAbstractCollection::T_BOOL:
369 | $val = $this->read_octet();
370 | break;
371 | case AMQPAbstractCollection::T_INT_SHORT:
372 | $val = $this->read_signed_short();
373 | break;
374 | case AMQPAbstractCollection::T_INT_SHORT_U:
375 | $val = $this->read_short();
376 | break;
377 | case AMQPAbstractCollection::T_INT_LONG:
378 | $val = $this->readSignedLong();
379 | break;
380 | case AMQPAbstractCollection::T_INT_LONG_U:
381 | $val = $this->read_long();
382 | break;
383 | case AMQPAbstractCollection::T_INT_LONGLONG:
384 | $val = $this->read_signed_longlong();
385 | break;
386 | case AMQPAbstractCollection::T_INT_LONGLONG_U:
387 | $val = $this->read_longlong();
388 | break;
389 | case AMQPAbstractCollection::T_DECIMAL:
390 | $e = $this->read_octet();
391 | $n = $this->readSignedLong();
392 | $val = new AMQPDecimal($n, $e);
393 | break;
394 | case AMQPAbstractCollection::T_TIMESTAMP:
395 | $val = $this->read_timestamp();
396 | break;
397 | case AMQPAbstractCollection::T_STRING_SHORT:
398 | $val = $this->read_shortstr();
399 | break;
400 | case AMQPAbstractCollection::T_STRING_LONG:
401 | case AMQPAbstractCollection::T_BYTES:
402 | $val = $this->read_longstr();
403 | break;
404 | case AMQPAbstractCollection::T_ARRAY:
405 | $val = $this->read_array($collectionsAsObjects);
406 | break;
407 | case AMQPAbstractCollection::T_TABLE:
408 | $val = $this->read_table($collectionsAsObjects);
409 | break;
410 | case AMQPAbstractCollection::T_VOID:
411 | $val = null;
412 | break;
413 | case AMQPAbstractCollection::T_FLOAT:
414 | $val = $this->read_float();
415 | break;
416 | case AMQPAbstractCollection::T_DOUBLE:
417 | $val = $this->read_double();
418 | break;
419 | default:
420 | throw new AMQPInvalidArgumentException(sprintf(
421 | 'Unsupported type "%s"',
422 | $fieldType
423 | ));
424 | }
425 |
426 | return $val;
427 | }
428 |
429 | protected function tell(): int
430 | {
431 | return $this->offset;
432 | }
433 |
434 | protected function resetCounters(): void
435 | {
436 | $this->bitcount = $this->bits = 0;
437 | }
438 | }
439 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/AMQPTable.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire;
4 |
5 | use PhpAmqpLib\Exception;
6 |
7 | class AMQPTable extends AMQPAbstractCollection
8 | {
9 |
10 | /**
11 | * @return int
12 | */
13 | final public function getType()
14 | {
15 | return self::T_TABLE;
16 | }
17 |
18 | /**
19 | * @param string $key
20 | * @param mixed $val
21 | * @param int|null $type
22 | */
23 | public function set($key, $val, $type = null)
24 | {
25 | //https://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf, https://www.rabbitmq.com/resources/specs/amqp0-8.pdf
26 | //Field names MUST start with a letter, '#39; or '#' and may continue with letters, '#39; or '#', digits,
27 | // or underlines, to a maximum length of 128 characters
28 | //The server SHOULD validate field names and upon receiving an invalid field name, it SHOULD signal a connection
29 | // exception with reply code 503 (syntax error)
30 |
31 | //validating length only and delegating other stuff to server, as rabbit seems to currently support numeric keys
32 | if (!($len = strlen($key)) || ($len > 128)) {
33 | throw new Exception\AMQPInvalidArgumentException(
34 | 'Table key must be non-empty string up to 128 chars in length'
35 | );
36 | }
37 | $this->setValue($val, $type, $key);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/AMQPWriter.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire;
4 |
5 | use PhpAmqpLib\Exception\AMQPInvalidArgumentException;
6 | use PhpAmqpLib\Exception\AMQPOutOfRangeException;
7 | use PhpAmqpLib\Helper\BigInteger;
8 |
9 | class AMQPWriter extends AMQPByteStream
10 | {
11 | /** @var string */
12 | protected $out = '';
13 |
14 | /** @var array */
15 | protected $bits = array();
16 |
17 | /** @var int */
18 | protected $bitcount = 0;
19 |
20 | private function flushbits()
21 | {
22 | if (!empty($this->bits)) {
23 | $this->out .= implode('', array_map('chr', $this->bits));
24 | $this->bits = array();
25 | $this->bitcount = 0;
26 | }
27 | }
28 |
29 | /**
30 | * Get what's been encoded so far.
31 | *
32 | * @return string
33 | */
34 | public function getvalue()
35 | {
36 | /* temporarily needed for compatibility with write_bit unit tests */
37 | if ($this->bitcount) {
38 | $this->flushbits();
39 | }
40 |
41 | return $this->out;
42 | }
43 |
44 | /**
45 | * Write a plain PHP string, with no special encoding.
46 | *
47 | * @param string $s
48 | *
49 | * @return $this
50 | */
51 | public function write($s)
52 | {
53 | $this->out .= $s;
54 |
55 | return $this;
56 | }
57 |
58 | /**
59 | * Write a boolean value.
60 | * (deprecated, use write_bits instead)
61 | *
62 | * @deprecated
63 | * @param bool $b
64 | * @return $this
65 | */
66 | public function write_bit($b)
67 | {
68 | $b = $b ? 1 : 0;
69 | $shift = $this->bitcount % 8;
70 | $last = $shift === 0 ? 0 : array_pop($this->bits);
71 | $last |= ($b << $shift);
72 | $this->bits[] = $last;
73 | $this->bitcount++;
74 |
75 | return $this;
76 | }
77 |
78 | /**
79 | * Write multiple bits as an octet
80 | *
81 | * @param bool[] $bits
82 | * @return $this
83 | */
84 | public function write_bits($bits)
85 | {
86 | $value = 0;
87 |
88 | foreach ($bits as $n => $bit) {
89 | $bit = $bit ? 1 : 0;
90 | $value |= ($bit << $n);
91 | }
92 |
93 | $this->out .= chr($value);
94 |
95 | return $this;
96 | }
97 |
98 | /**
99 | * Write an integer as an unsigned 8-bit value
100 | *
101 | * @param int $n
102 | * @return $this
103 | * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
104 | */
105 | public function write_octet($n)
106 | {
107 | if ($n < 0 || $n > 255) {
108 | throw new AMQPInvalidArgumentException('Octet out of range: ' . $n);
109 | }
110 |
111 | $this->out .= chr($n);
112 |
113 | return $this;
114 | }
115 |
116 | /**
117 | * @param int $n
118 | * @return $this
119 | */
120 | public function write_signed_octet($n)
121 | {
122 | if (($n < -128) || ($n > 127)) {
123 | throw new AMQPInvalidArgumentException('Signed octet out of range: ' . $n);
124 | }
125 |
126 | $this->out .= pack('c', $n);
127 |
128 | return $this;
129 | }
130 |
131 | /**
132 | * Write an integer as an unsigned 16-bit value
133 | *
134 | * @param int $n
135 | * @return $this
136 | * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
137 | */
138 | public function write_short($n)
139 | {
140 | if ($n < 0 || $n > 65535) {
141 | throw new AMQPInvalidArgumentException('Short out of range: ' . $n);
142 | }
143 |
144 | $this->out .= pack('n', $n);
145 |
146 | return $this;
147 | }
148 |
149 | /**
150 | * @param int $n
151 | * @return $this
152 | */
153 | public function write_signed_short($n)
154 | {
155 | if (($n < -32768) || ($n > 32767)) {
156 | throw new AMQPInvalidArgumentException('Signed short out of range: ' . $n);
157 | }
158 |
159 | $this->out .= $this->correctEndianness(pack('s', $n));
160 |
161 | return $this;
162 | }
163 |
164 | /**
165 | * Write an integer as an unsigned 32-bit value
166 | *
167 | * @param int|string $n
168 | * @return $this
169 | */
170 | public function write_long($n)
171 | {
172 | if (($n < 0) || ($n > 4294967295)) {
173 | throw new AMQPInvalidArgumentException('Long out of range: ' . $n);
174 | }
175 |
176 | //Numeric strings >PHP_INT_MAX on 32bit are casted to PHP_INT_MAX, damn PHP
177 | if (!self::PLATFORM_64BIT && is_string($n)) {
178 | $n = (float) $n;
179 | }
180 | $this->out .= pack('N', $n);
181 |
182 | return $this;
183 | }
184 |
185 | /**
186 | * @param int $n
187 | * @return $this
188 | */
189 | private function writeSignedLong($n)
190 | {
191 | if (($n < -2147483648) || ($n > 2147483647)) {
192 | throw new AMQPInvalidArgumentException('Signed long out of range: ' . $n);
193 | }
194 |
195 | //on my 64bit debian this approach is slightly faster than splitIntoQuads()
196 | $this->out .= $this->correctEndianness(pack('l', $n));
197 |
198 | return $this;
199 | }
200 |
201 | /**
202 | * Write a numeric value as an unsigned 64-bit value
203 | *
204 | * @param int|string $n
205 | * @return $this
206 | * @throws AMQPOutOfRangeException
207 | */
208 | public function write_longlong($n)
209 | {
210 | if (is_int($n)) {
211 | if ($n < 0) {
212 | throw new AMQPOutOfRangeException('Longlong out of range: ' . $n);
213 | }
214 |
215 | if (self::PLATFORM_64BIT) {
216 | $res = pack('J', $n);
217 | $this->out .= $res;
218 | } else {
219 | $this->out .= pack('NN', 0, $n);
220 | }
221 |
222 | return $this;
223 | }
224 |
225 | $value = new BigInteger($n);
226 | if (
227 | $value->compare(self::getBigInteger('0')) < 0
228 | || $value->compare(self::getBigInteger('FFFFFFFFFFFFFFFF', 16)) > 0
229 | ) {
230 | throw new AMQPInvalidArgumentException('Longlong out of range: ' . $n);
231 | }
232 |
233 | $value->setPrecision(64);
234 | $this->out .= $value->toBytes();
235 |
236 | return $this;
237 | }
238 |
239 | /**
240 | * @param int|string $n
241 | * @return $this
242 | */
243 | public function write_signed_longlong($n)
244 | {
245 | if (is_int($n)) {
246 | if (self::PLATFORM_64BIT) {
247 | // q is for 64-bit signed machine byte order
248 | $packed = pack('q', $n);
249 | if (self::isLittleEndian()) {
250 | $packed = $this->convertByteOrder($packed);
251 | }
252 | $this->out .= $packed;
253 | } else {
254 | $hi = $n < 0 ? -1 : 0;
255 | $lo = $n;
256 | $this->out .= pack('NN', $hi, $lo);
257 | }
258 |
259 | return $this;
260 | }
261 |
262 | $value = new BigInteger($n);
263 | if (
264 | $value->compare(self::getBigInteger('-8000000000000000', 16)) < 0
265 | || $value->compare(self::getBigInteger('7FFFFFFFFFFFFFFF', 16)) > 0
266 | ) {
267 | throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n);
268 | }
269 |
270 | $value->setPrecision(64);
271 | $this->out .= substr($value->toBytes(true), -8);
272 |
273 | return $this;
274 | }
275 |
276 | /**
277 | * Write a string up to 255 bytes long after encoding.
278 | * Assume UTF-8 encoding
279 | *
280 | * @param string $s
281 | * @return $this
282 | * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
283 | */
284 | public function write_shortstr($s)
285 | {
286 | if ($s === null) {
287 | $this->write_octet(0);
288 |
289 | return $this;
290 | }
291 |
292 | $len = mb_strlen($s, 'ASCII');
293 | if ($len > 255) {
294 | throw new AMQPInvalidArgumentException('String too long');
295 | }
296 |
297 | $this->write_octet($len);
298 | $this->out .= $s;
299 |
300 | return $this;
301 | }
302 |
303 | /**
304 | * Write a string up to 2**32 bytes long. Assume UTF-8 encoding
305 | *
306 | * @param string $s
307 | * @return $this
308 | */
309 | public function write_longstr($s)
310 | {
311 | if ($s === null) {
312 | $this->write_long(0);
313 |
314 | return $this;
315 | }
316 |
317 | $this->write_long(mb_strlen($s, 'ASCII'));
318 | $this->out .= $s;
319 |
320 | return $this;
321 | }
322 |
323 | /**
324 | * Supports the writing of Array types, so that you can implement
325 | * array methods, like Rabbitmq's HA parameters
326 | *
327 | * @param AMQPArray|array $a Instance of AMQPArray or PHP array WITHOUT format hints (unlike write_table())
328 | * @return self
329 | */
330 | public function write_array($a)
331 | {
332 | if (!($a instanceof AMQPArray)) {
333 | $a = new AMQPArray($a);
334 | }
335 | $data = new self();
336 |
337 | foreach ($a as $v) {
338 | $data->writeValue($v[0], $v[1]);
339 | }
340 |
341 | $data = $data->getvalue();
342 | $this->write_long(mb_strlen($data, 'ASCII'));
343 | $this->write($data);
344 |
345 | return $this;
346 | }
347 |
348 | /**
349 | * Write unix time_t value as 64 bit timestamp
350 | *
351 | * @param int $v
352 | * @return $this
353 | */
354 | public function write_timestamp($v)
355 | {
356 | $this->write_longlong($v);
357 |
358 | return $this;
359 | }
360 |
361 | /**
362 | * Write PHP array, as table. Input array format: keys are strings,
363 | * values are (type,value) tuples.
364 | *
365 | * @param AMQPTable|array $d Instance of AMQPTable or PHP array WITH format hints (unlike write_array())
366 | * @return $this
367 | * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
368 | */
369 | public function write_table($d)
370 | {
371 | $typeIsSym = !($d instanceof AMQPTable); //purely for back-compat purposes
372 |
373 | $table_data = new self();
374 | foreach ($d as $k => $va) {
375 | list($ftype, $v) = $va;
376 | $table_data->write_shortstr($k);
377 | $table_data->writeValue($typeIsSym ? AMQPAbstractCollection::getDataTypeForSymbol($ftype) : $ftype, $v);
378 | }
379 |
380 | $table_data = $table_data->getvalue();
381 | $this->write_long(mb_strlen($table_data, 'ASCII'));
382 | $this->write($table_data);
383 |
384 | return $this;
385 | }
386 |
387 | /**
388 | * for compat with method mapping used by AMQPMessage
389 | *
390 | * @param AMQPTable|array $d
391 | * @return $this
392 | */
393 | public function write_table_object($d)
394 | {
395 | return $this->write_table($d);
396 | }
397 |
398 | /**
399 | * @param int $type One of AMQPAbstractCollection::T_* constants
400 | * @param mixed $val
401 | */
402 | private function writeValue($type, $val)
403 | {
404 | //This will find appropriate symbol for given data type for currently selected protocol
405 | //Also will raise an exception on unknown type
406 | $this->write(AMQPAbstractCollection::getSymbolForDataType($type));
407 |
408 | switch ($type) {
409 | case AMQPAbstractCollection::T_INT_SHORTSHORT:
410 | $this->write_signed_octet($val);
411 | break;
412 | case AMQPAbstractCollection::T_INT_SHORTSHORT_U:
413 | $this->write_octet($val);
414 | break;
415 | case AMQPAbstractCollection::T_INT_SHORT:
416 | $this->write_signed_short($val);
417 | break;
418 | case AMQPAbstractCollection::T_INT_SHORT_U:
419 | $this->write_short($val);
420 | break;
421 | case AMQPAbstractCollection::T_INT_LONG:
422 | $this->writeSignedLong($val);
423 | break;
424 | case AMQPAbstractCollection::T_INT_LONG_U:
425 | $this->write_long($val);
426 | break;
427 | case AMQPAbstractCollection::T_INT_LONGLONG:
428 | $this->write_signed_longlong($val);
429 | break;
430 | case AMQPAbstractCollection::T_INT_LONGLONG_U:
431 | $this->write_longlong($val);
432 | break;
433 | case AMQPAbstractCollection::T_DECIMAL:
434 | $this->write_octet($val->getE());
435 | $this->writeSignedLong($val->getN());
436 | break;
437 | case AMQPAbstractCollection::T_TIMESTAMP:
438 | $this->write_timestamp($val);
439 | break;
440 | case AMQPAbstractCollection::T_BOOL:
441 | $this->write_octet($val ? 1 : 0);
442 | break;
443 | case AMQPAbstractCollection::T_STRING_SHORT:
444 | $this->write_shortstr($val);
445 | break;
446 | case AMQPAbstractCollection::T_STRING_LONG:
447 | $this->write_longstr($val);
448 | break;
449 | case AMQPAbstractCollection::T_ARRAY:
450 | $this->write_array($val);
451 | break;
452 | case AMQPAbstractCollection::T_TABLE:
453 | $this->write_table($val);
454 | break;
455 | case AMQPAbstractCollection::T_VOID:
456 | break;
457 | case AMQPAbstractCollection::T_BYTES:
458 | $this->write_longstr($val);
459 | break;
460 | default:
461 | throw new AMQPInvalidArgumentException(sprintf(
462 | 'Unsupported type "%s"',
463 | $type
464 | ));
465 | }
466 | }
467 | }
468 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/Constants.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire;
4 |
5 | abstract class Constants
6 | {
7 | const VERSION = '';
8 | const AMQP_HEADER = '';
9 |
10 | /**
11 | * @var array<int, string>
12 | */
13 | protected static $FRAME_TYPES = array();
14 |
15 | /**
16 | * @var array<int, string>
17 | */
18 | protected static $CONTENT_METHODS = array();
19 |
20 | /**
21 | * @var array<int, string>
22 | */
23 | protected static $CLOSE_METHODS = array();
24 |
25 | /**
26 | * @var array<string, string>
27 | */
28 | public static $GLOBAL_METHOD_NAMES = array();
29 |
30 | /**
31 | * @return string
32 | */
33 | public function getHeader()
34 | {
35 | return static::AMQP_HEADER;
36 | }
37 |
38 | /**
39 | * @param int $type
40 | * @return bool
41 | */
42 | public function isFrameType($type)
43 | {
44 | return array_key_exists($type, static::$FRAME_TYPES);
45 | }
46 |
47 | /**
48 | * @param int $type
49 | * @return string
50 | */
51 | public function getFrameType($type)
52 | {
53 | return static::$FRAME_TYPES[$type];
54 | }
55 |
56 | /**
57 | * @param string $method
58 | * @return bool
59 | */
60 | public function isContentMethod($method)
61 | {
62 | return in_array($method, static::$CONTENT_METHODS, false);
63 | }
64 |
65 | /**
66 | * @param string $method
67 | * @return bool
68 | */
69 | public function isCloseMethod($method)
70 | {
71 | return in_array($method, static::$CLOSE_METHODS, false);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/Constants080.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | /* This file was autogenerated by spec/parser.php - Do not modify */
4 |
5 | namespace PhpAmqpLib\Wire;
6 |
7 | final class Constants080 extends Constants
8 | {
9 | const VERSION = '8.0';
10 | const AMQP_HEADER = "AMQP\x01\x01\x08\x00";
11 |
12 | /**
13 | * @var array
14 | */
15 | public static $FRAME_TYPES = array(
16 | 1 => 'FRAME-METHOD',
17 | 2 => 'FRAME-HEADER',
18 | 3 => 'FRAME-BODY',
19 | 4 => 'FRAME-OOB-METHOD',
20 | 5 => 'FRAME-OOB-HEADER',
21 | 6 => 'FRAME-OOB-BODY',
22 | 7 => 'FRAME-TRACE',
23 | 8 => 'FRAME-HEARTBEAT',
24 | 4096 => 'FRAME-MIN-SIZE',
25 | 206 => 'FRAME-END',
26 | 501 => 'FRAME-ERROR',
27 | );
28 |
29 | /**
30 | * @var array
31 | */
32 | public static $CONTENT_METHODS = array(
33 | 0 => '60,40',
34 | 1 => '60,50',
35 | 2 => '60,60',
36 | 3 => '60,71',
37 | 4 => '70,50',
38 | 5 => '70,70',
39 | 6 => '80,40',
40 | 7 => '80,50',
41 | 8 => '80,60',
42 | 9 => '110,10',
43 | 10 => '120,40',
44 | 11 => '120,41',
45 | );
46 |
47 | /**
48 | * @var array
49 | */
50 | public static $CLOSE_METHODS = array(
51 | 0 => '10,60',
52 | 1 => '20,40',
53 | );
54 |
55 | /**
56 | * @var array
57 | */
58 | public static $GLOBAL_METHOD_NAMES = array(
59 | '10,10' => 'Connection.start',
60 | '10,11' => 'Connection.start_ok',
61 | '10,20' => 'Connection.secure',
62 | '10,21' => 'Connection.secure_ok',
63 | '10,30' => 'Connection.tune',
64 | '10,31' => 'Connection.tune_ok',
65 | '10,40' => 'Connection.open',
66 | '10,41' => 'Connection.open_ok',
67 | '10,50' => 'Connection.redirect',
68 | '10,60' => 'Connection.close',
69 | '10,61' => 'Connection.close_ok',
70 | '20,10' => 'Channel.open',
71 | '20,11' => 'Channel.open_ok',
72 | '20,20' => 'Channel.flow',
73 | '20,21' => 'Channel.flow_ok',
74 | '20,30' => 'Channel.alert',
75 | '20,40' => 'Channel.close',
76 | '20,41' => 'Channel.close_ok',
77 | '30,10' => 'Access.request',
78 | '30,11' => 'Access.request_ok',
79 | '40,10' => 'Exchange.declare',
80 | '40,11' => 'Exchange.declare_ok',
81 | '40,20' => 'Exchange.delete',
82 | '40,21' => 'Exchange.delete_ok',
83 | '50,10' => 'Queue.declare',
84 | '50,11' => 'Queue.declare_ok',
85 | '50,20' => 'Queue.bind',
86 | '50,21' => 'Queue.bind_ok',
87 | '50,30' => 'Queue.purge',
88 | '50,31' => 'Queue.purge_ok',
89 | '50,40' => 'Queue.delete',
90 | '50,41' => 'Queue.delete_ok',
91 | '50,50' => 'Queue.unbind',
92 | '50,51' => 'Queue.unbind_ok',
93 | '60,10' => 'Basic.qos',
94 | '60,11' => 'Basic.qos_ok',
95 | '60,20' => 'Basic.consume',
96 | '60,21' => 'Basic.consume_ok',
97 | '60,30' => 'Basic.cancel',
98 | '60,31' => 'Basic.cancel_ok',
99 | '60,40' => 'Basic.publish',
100 | '60,50' => 'Basic.return',
101 | '60,60' => 'Basic.deliver',
102 | '60,70' => 'Basic.get',
103 | '60,71' => 'Basic.get_ok',
104 | '60,72' => 'Basic.get_empty',
105 | '60,80' => 'Basic.ack',
106 | '60,90' => 'Basic.reject',
107 | '60,100' => 'Basic.recover_async',
108 | '60,110' => 'Basic.recover',
109 | '60,111' => 'Basic.recover_ok',
110 | '70,10' => 'File.qos',
111 | '70,11' => 'File.qos_ok',
112 | '70,20' => 'File.consume',
113 | '70,21' => 'File.consume_ok',
114 | '70,30' => 'File.cancel',
115 | '70,31' => 'File.cancel_ok',
116 | '70,40' => 'File.open',
117 | '70,41' => 'File.open_ok',
118 | '70,50' => 'File.stage',
119 | '70,60' => 'File.publish',
120 | '70,70' => 'File.return',
121 | '70,80' => 'File.deliver',
122 | '70,90' => 'File.ack',
123 | '70,100' => 'File.reject',
124 | '80,10' => 'Stream.qos',
125 | '80,11' => 'Stream.qos_ok',
126 | '80,20' => 'Stream.consume',
127 | '80,21' => 'Stream.consume_ok',
128 | '80,30' => 'Stream.cancel',
129 | '80,31' => 'Stream.cancel_ok',
130 | '80,40' => 'Stream.publish',
131 | '80,50' => 'Stream.return',
132 | '80,60' => 'Stream.deliver',
133 | '90,10' => 'Tx.select',
134 | '90,11' => 'Tx.select_ok',
135 | '90,20' => 'Tx.commit',
136 | '90,21' => 'Tx.commit_ok',
137 | '90,30' => 'Tx.rollback',
138 | '90,31' => 'Tx.rollback_ok',
139 | '100,10' => 'Dtx.select',
140 | '100,11' => 'Dtx.select_ok',
141 | '100,20' => 'Dtx.start',
142 | '100,21' => 'Dtx.start_ok',
143 | '110,10' => 'Tunnel.request',
144 | '120,10' => 'Test.integer',
145 | '120,11' => 'Test.integer_ok',
146 | '120,20' => 'Test.string',
147 | '120,21' => 'Test.string_ok',
148 | '120,30' => 'Test.table',
149 | '120,31' => 'Test.table_ok',
150 | '120,40' => 'Test.content',
151 | '120,41' => 'Test.content_ok',
152 | );
153 | }
154 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/Constants091.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | /* This file was autogenerated by spec/parser.php - Do not modify */
4 |
5 | namespace PhpAmqpLib\Wire;
6 |
7 | final class Constants091 extends Constants
8 | {
9 | const VERSION = '0.9.1';
10 | const AMQP_HEADER = "AMQP\x00\x00\x09\x01";
11 |
12 | /**
13 | * @var array
14 | */
15 | public static $FRAME_TYPES = array(
16 | 1 => 'FRAME-METHOD',
17 | 2 => 'FRAME-HEADER',
18 | 3 => 'FRAME-BODY',
19 | 8 => 'FRAME-HEARTBEAT',
20 | 4096 => 'FRAME-MIN-SIZE',
21 | 206 => 'FRAME-END',
22 | 501 => 'FRAME-ERROR',
23 | );
24 |
25 | /**
26 | * @var array
27 | */
28 | public static $CONTENT_METHODS = array(
29 | 0 => '60,40',
30 | 1 => '60,50',
31 | 2 => '60,60',
32 | 3 => '60,71',
33 | );
34 |
35 | /**
36 | * @var array
37 | */
38 | public static $CLOSE_METHODS = array(
39 | 0 => '10,50',
40 | 1 => '20,40',
41 | );
42 |
43 | /**
44 | * @var array
45 | */
46 | public static $GLOBAL_METHOD_NAMES = array(
47 | '10,10' => 'Connection.start',
48 | '10,11' => 'Connection.start_ok',
49 | '10,20' => 'Connection.secure',
50 | '10,21' => 'Connection.secure_ok',
51 | '10,30' => 'Connection.tune',
52 | '10,31' => 'Connection.tune_ok',
53 | '10,40' => 'Connection.open',
54 | '10,41' => 'Connection.open_ok',
55 | '10,50' => 'Connection.close',
56 | '10,51' => 'Connection.close_ok',
57 | '10,60' => 'Connection.blocked',
58 | '10,61' => 'Connection.unblocked',
59 | '20,10' => 'Channel.open',
60 | '20,11' => 'Channel.open_ok',
61 | '20,20' => 'Channel.flow',
62 | '20,21' => 'Channel.flow_ok',
63 | '20,40' => 'Channel.close',
64 | '20,41' => 'Channel.close_ok',
65 | '30,10' => 'Access.request',
66 | '30,11' => 'Access.request_ok',
67 | '40,10' => 'Exchange.declare',
68 | '40,11' => 'Exchange.declare_ok',
69 | '40,20' => 'Exchange.delete',
70 | '40,21' => 'Exchange.delete_ok',
71 | '40,30' => 'Exchange.bind',
72 | '40,31' => 'Exchange.bind_ok',
73 | '40,40' => 'Exchange.unbind',
74 | '40,51' => 'Exchange.unbind_ok',
75 | '50,10' => 'Queue.declare',
76 | '50,11' => 'Queue.declare_ok',
77 | '50,20' => 'Queue.bind',
78 | '50,21' => 'Queue.bind_ok',
79 | '50,30' => 'Queue.purge',
80 | '50,31' => 'Queue.purge_ok',
81 | '50,40' => 'Queue.delete',
82 | '50,41' => 'Queue.delete_ok',
83 | '50,50' => 'Queue.unbind',
84 | '50,51' => 'Queue.unbind_ok',
85 | '60,10' => 'Basic.qos',
86 | '60,11' => 'Basic.qos_ok',
87 | '60,20' => 'Basic.consume',
88 | '60,21' => 'Basic.consume_ok',
89 | '60,30' => 'Basic.cancel',
90 | '60,31' => 'Basic.cancel_ok',
91 | '60,40' => 'Basic.publish',
92 | '60,50' => 'Basic.return',
93 | '60,60' => 'Basic.deliver',
94 | '60,70' => 'Basic.get',
95 | '60,71' => 'Basic.get_ok',
96 | '60,72' => 'Basic.get_empty',
97 | '60,80' => 'Basic.ack',
98 | '60,90' => 'Basic.reject',
99 | '60,100' => 'Basic.recover_async',
100 | '60,110' => 'Basic.recover',
101 | '60,111' => 'Basic.recover_ok',
102 | '60,120' => 'Basic.nack',
103 | '90,10' => 'Tx.select',
104 | '90,11' => 'Tx.select_ok',
105 | '90,20' => 'Tx.commit',
106 | '90,21' => 'Tx.commit_ok',
107 | '90,30' => 'Tx.rollback',
108 | '90,31' => 'Tx.rollback_ok',
109 | '85,10' => 'Confirm.select',
110 | '85,11' => 'Confirm.select_ok',
111 | );
112 | }
113 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/IO/AbstractIO.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire\IO;
4 |
5 | use PhpAmqpLib\Connection\AMQPConnectionConfig;
6 | use PhpAmqpLib\Exception\AMQPConnectionClosedException;
7 | use PhpAmqpLib\Exception\AMQPHeartbeatMissedException;
8 | use PhpAmqpLib\Exception\AMQPIOWaitException;
9 | use PhpAmqpLib\Exception\AMQPRuntimeException;
10 | use PhpAmqpLib\Wire\AMQPWriter;
11 |
12 | abstract class AbstractIO
13 | {
14 | const BUFFER_SIZE = 8192;
15 |
16 | /** @var null|AMQPConnectionConfig */
17 | protected $config;
18 |
19 | /** @var string */
20 | protected $host;
21 |
22 | /** @var int */
23 | protected $port;
24 |
25 | /** @var int|float */
26 | protected $connection_timeout;
27 |
28 | /** @var float */
29 | protected $read_timeout;
30 |
31 | /** @var float */
32 | protected $write_timeout;
33 |
34 | /** @var int */
35 | protected $heartbeat;
36 |
37 | /** @var int */
38 | protected $initial_heartbeat;
39 |
40 | /** @var bool */
41 | protected $keepalive;
42 |
43 | /** @var int|float */
44 | protected $last_read;
45 |
46 | /** @var int|float */
47 | protected $last_write;
48 |
49 | /** @var \ErrorException|null */
50 | protected $last_error;
51 |
52 | /** @var bool */
53 | protected $canDispatchPcntlSignal = false;
54 |
55 | /**
56 | * @param int $len
57 | * @return string
58 | * @throws \PhpAmqpLib\Exception\AMQPIOException
59 | * @throws AMQPRuntimeException
60 | * @throws \PhpAmqpLib\Exception\AMQPSocketException
61 | * @throws \PhpAmqpLib\Exception\AMQPTimeoutException
62 | * @throws \PhpAmqpLib\Exception\AMQPConnectionClosedException
63 | */
64 | abstract public function read($len);
65 |
66 | /**
67 | * @param string $data
68 | * @throws \PhpAmqpLib\Exception\AMQPIOException
69 | * @throws \PhpAmqpLib\Exception\AMQPSocketException
70 | * @throws \PhpAmqpLib\Exception\AMQPConnectionClosedException
71 | * @throws \PhpAmqpLib\Exception\AMQPTimeoutException
72 | */
73 | abstract public function write($data);
74 |
75 | /**
76 | * @return void
77 | */
78 | abstract public function close();
79 |
80 | /**
81 | * @param int|null $sec
82 | * @param int $usec
83 | * @return int
84 | * @throws AMQPIOWaitException
85 | * @throws AMQPRuntimeException
86 | * @throws AMQPConnectionClosedException
87 | */
88 | public function select(?int $sec, int $usec = 0)
89 | {
90 | $this->check_heartbeat();
91 | $this->setErrorHandler();
92 | try {
93 | $result = $this->do_select($sec, $usec);
94 | $this->throwOnError();
95 | } catch (\ErrorException $e) {
96 | throw new AMQPIOWaitException($e->getMessage(), $e->getCode(), $e->getPrevious());
97 | } finally {
98 | $this->restoreErrorHandler();
99 | }
100 |
101 | if ($this->canDispatchPcntlSignal) {
102 | pcntl_signal_dispatch();
103 | }
104 |
105 | // no exception and false result - either timeout or signal was sent
106 | if ($result === false) {
107 | $result = 0;
108 | }
109 |
110 | return $result;
111 | }
112 |
113 | /**
114 | * @param int|null $sec
115 | * @param int $usec
116 | * @return int|bool
117 | * @throws AMQPConnectionClosedException
118 | */
119 | abstract protected function do_select(?int $sec, int $usec);
120 |
121 | /**
122 | * Set ups the connection.
123 | * @return void
124 | * @throws \PhpAmqpLib\Exception\AMQPIOException
125 | * @throws AMQPRuntimeException
126 | */
127 | abstract public function connect();
128 |
129 | /**
130 | * Set connection params connection tune(negotiation).
131 | * @param int $heartbeat
132 | */
133 | public function afterTune(int $heartbeat): void
134 | {
135 | $this->heartbeat = $heartbeat;
136 | $this->initial_heartbeat = $heartbeat;
137 | }
138 |
139 | /**
140 | * Heartbeat logic: check connection health here
141 | * @return void
142 | * @throws AMQPRuntimeException
143 | */
144 | public function check_heartbeat()
145 | {
146 | // ignore unless heartbeat interval is set
147 | if ($this->heartbeat !== 0 && $this->last_read > 0 && $this->last_write > 0) {
148 | // server has gone away
149 | $this->checkBrokerHeartbeat();
150 |
151 | // time for client to send a heartbeat
152 | $now = microtime(true);
153 | if (($this->heartbeat / 2) < $now - $this->last_write) {
154 | $this->write_heartbeat();
155 | }
156 | }
157 | }
158 |
159 | /**
160 | * @throws \PhpAmqpLib\Exception\AMQPHeartbeatMissedException
161 | */
162 | protected function checkBrokerHeartbeat(): void
163 | {
164 | if ($this->heartbeat > 0 && ($this->last_read > 0 || $this->last_write > 0)) {
165 | $lastActivity = $this->getLastActivity();
166 | $now = microtime(true);
167 | if (($now - $lastActivity) > $this->heartbeat * 2 + 1) {
168 | $this->close();
169 | throw new AMQPHeartbeatMissedException('Missed server heartbeat');
170 | }
171 | }
172 | }
173 |
174 | /**
175 | * @return float|int
176 | */
177 | public function getLastActivity()
178 | {
179 | return max($this->last_read, $this->last_write);
180 | }
181 |
182 | public function getReadTimeout(): float
183 | {
184 | return $this->read_timeout;
185 | }
186 |
187 | /**
188 | * @return $this
189 | */
190 | public function disableHeartbeat()
191 | {
192 | $this->initial_heartbeat = $this->heartbeat;
193 | $this->heartbeat = 0;
194 |
195 | return $this;
196 | }
197 |
198 | /**
199 | * @return $this
200 | */
201 | public function reenableHeartbeat()
202 | {
203 | $this->heartbeat = $this->initial_heartbeat;
204 |
205 | return $this;
206 | }
207 |
208 | /**
209 | * Sends a heartbeat message
210 | */
211 | protected function write_heartbeat()
212 | {
213 | $pkt = new AMQPWriter();
214 | $pkt->write_octet(8);
215 | $pkt->write_short(0);
216 | $pkt->write_long(0);
217 | $pkt->write_octet(0xCE);
218 | $this->write($pkt->getvalue());
219 | }
220 |
221 | /**
222 | * Begin tracking errors and set the error handler
223 | */
224 | protected function setErrorHandler(): void
225 | {
226 | $this->last_error = null;
227 | set_error_handler(array($this, 'error_handler'));
228 | }
229 |
230 | protected function throwOnError(): void
231 | {
232 | if ($this->last_error instanceof \ErrorException) {
233 | $error = $this->last_error;
234 | $this->last_error = null;
235 | throw $error;
236 | }
237 | }
238 |
239 | protected function restoreErrorHandler(): void
240 | {
241 | restore_error_handler();
242 | }
243 |
244 | /**
245 | * Internal error handler to deal with stream and socket errors.
246 | *
247 | * @param int $errno
248 | * @param string $errstr
249 | * @param string $errfile
250 | * @param int $errline
251 | * @return void
252 | */
253 | public function error_handler($errno, $errstr, $errfile, $errline): void
254 | {
255 | // throwing an exception in an error handler will halt execution
256 | // collect error continue
257 | $this->last_error = new \ErrorException($errstr, $errno, 1, $errfile, $errline, $this->last_error);
258 | }
259 |
260 | protected function isPcntlSignalEnabled(): bool
261 | {
262 | return extension_loaded('pcntl')
263 | && function_exists('pcntl_signal_dispatch')
264 | && (defined('AMQP_WITHOUT_SIGNALS') ? !AMQP_WITHOUT_SIGNALS : true);
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/IO/SocketIO.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire\IO;
4 |
5 | use PhpAmqpLib\Connection\AMQPConnectionConfig;
6 | use PhpAmqpLib\Exception\AMQPConnectionClosedException;
7 | use PhpAmqpLib\Exception\AMQPIOException;
8 | use PhpAmqpLib\Exception\AMQPSocketException;
9 | use PhpAmqpLib\Exception\AMQPTimeoutException;
10 | use PhpAmqpLib\Helper\MiscHelper;
11 | use PhpAmqpLib\Helper\SocketConstants;
12 |
13 | class SocketIO extends AbstractIO
14 | {
15 | /** @var null|resource|\Socket */
16 | private $sock;
17 |
18 | /**
19 | * @param string $host
20 | * @param int $port
21 | * @param int|float $read_timeout
22 | * @param bool $keepalive
23 | * @param int|float|null $write_timeout if null defaults to read timeout
24 | * @param int $heartbeat how often to send heartbeat. 0 means off
25 | * @param null|AMQPConnectionConfig $config
26 | */
27 | public function __construct(
28 | $host,
29 | $port,
30 | $read_timeout = 3,
31 | $keepalive = false,
32 | $write_timeout = null,
33 | $heartbeat = 0,
34 | ?AMQPConnectionConfig $config = null
35 | ) {
36 | $this->config = $config;
37 | $this->host = str_replace(['[', ']'], '', $host);
38 | $this->port = $port;
39 | $this->read_timeout = (float)$read_timeout;
40 | $this->write_timeout = (float)($write_timeout ?: $read_timeout);
41 | $this->heartbeat = $heartbeat;
42 | $this->initial_heartbeat = $heartbeat;
43 | $this->keepalive = $keepalive;
44 | $this->canDispatchPcntlSignal = $this->isPcntlSignalEnabled();
45 |
46 | /*
47 | TODO FUTURE enable this check
48 | php-amqplib/php-amqplib#648, php-amqplib/php-amqplib#666
49 | if ($this->heartbeat !== 0 && ($this->read_timeout <= ($this->heartbeat * 2))) {
50 | throw new \InvalidArgumentException('read_timeout must be greater than 2x the heartbeat');
51 | }
52 | if ($this->heartbeat !== 0 && ($this->write_timeout <= ($this->heartbeat * 2))) {
53 | throw new \InvalidArgumentException('send_timeout must be greater than 2x the heartbeat');
54 | }
55 | */
56 | }
57 |
58 | /**
59 | * @inheritdoc
60 | */
61 | public function connect()
62 | {
63 | $this->sock = socket_create(!$this->isIpv6() ? AF_INET : AF_INET6, SOCK_STREAM, SOL_TCP);
64 |
65 | list($sec, $uSec) = MiscHelper::splitSecondsMicroseconds($this->write_timeout);
66 | socket_set_option($this->sock, SOL_SOCKET, SO_SNDTIMEO, array('sec' => $sec, 'usec' => $uSec));
67 | list($sec, $uSec) = MiscHelper::splitSecondsMicroseconds($this->read_timeout);
68 | socket_set_option($this->sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $sec, 'usec' => $uSec));
69 |
70 | $this->setErrorHandler();
71 | try {
72 | $connected = socket_connect($this->sock, $this->host, $this->port);
73 | $this->throwOnError();
74 | } catch (\ErrorException $e) {
75 | $connected = false;
76 | } finally {
77 | $this->restoreErrorHandler();
78 | }
79 | if (!$connected) {
80 | $errno = socket_last_error($this->sock);
81 | $errstr = socket_strerror($errno);
82 | throw new AMQPIOException(sprintf(
83 | 'Error Connecting to server (%s): %s',
84 | $errno,
85 | $errstr
86 | ), $errno);
87 | }
88 |
89 | socket_set_block($this->sock);
90 | socket_set_option($this->sock, SOL_TCP, TCP_NODELAY, 1);
91 | if ($this->config && $this->config->getSendBufferSize() > 0) {
92 | socket_set_option($this->sock, SOL_SOCKET, SO_SNDBUF, $this->config->getSendBufferSize());
93 | }
94 |
95 | if ($this->keepalive) {
96 | $this->enable_keepalive();
97 | }
98 |
99 | $this->heartbeat = $this->initial_heartbeat;
100 | }
101 |
102 | /**
103 | * @deprecated
104 | * @return null|resource|\Socket
105 | */
106 | public function getSocket()
107 | {
108 | return $this->sock;
109 | }
110 |
111 | /**
112 | * @inheritdoc
113 | */
114 | public function read($len)
115 | {
116 | if (is_null($this->sock)) {
117 | throw new AMQPSocketException(sprintf(
118 | 'Socket was null! Last SocketError was: %s',
119 | socket_strerror(socket_last_error())
120 | ));
121 | }
122 |
123 | $this->check_heartbeat();
124 |
125 | list($timeout_sec, $timeout_uSec) = MiscHelper::splitSecondsMicroseconds($this->read_timeout);
126 | $read_start = microtime(true);
127 | $read = 0;
128 | $data = '';
129 | while ($read < $len) {
130 | $buffer = null;
131 | $result = socket_recv($this->sock, $buffer, $len - $read, 0);
132 | if ($result === 0) {
133 | // From linux recv() manual:
134 | // When a stream socket peer has performed an orderly shutdown,
135 | // the return value will be 0 (the traditional "end-of-file" return).
136 | // http://php.net/manual/en/function.socket-recv.php#47182
137 | $this->close();
138 | throw new AMQPConnectionClosedException('Broken pipe or closed connection');
139 | }
140 |
141 | if (empty($buffer)) {
142 | $read_now = microtime(true);
143 | $t_read = $read_now - $read_start;
144 | if ($t_read > $this->read_timeout) {
145 | throw new AMQPTimeoutException('Too many read attempts detected in SocketIO');
146 | }
147 | $this->select($timeout_sec, $timeout_uSec);
148 | continue;
149 | }
150 |
151 | $read += mb_strlen($buffer, 'ASCII');
152 | $data .= $buffer;
153 | }
154 |
155 | if (mb_strlen($data, 'ASCII') !== $len) {
156 | throw new AMQPIOException(sprintf(
157 | 'Error reading data. Received %s instead of expected %s bytes',
158 | mb_strlen($data, 'ASCII'),
159 | $len
160 | ));
161 | }
162 |
163 | $this->last_read = microtime(true);
164 |
165 | return $data;
166 | }
167 |
168 | /**
169 | * @inheritdoc
170 | */
171 | public function write($data)
172 | {
173 | // Null sockets are invalid, throw exception
174 | if (is_null($this->sock)) {
175 | throw new AMQPSocketException(sprintf(
176 | 'Socket was null! Last SocketError was: %s',
177 | socket_strerror(socket_last_error())
178 | ));
179 | }
180 |
181 | $this->checkBrokerHeartbeat();
182 |
183 | $written = 0;
184 | $len = mb_strlen($data, 'ASCII');
185 | $write_start = microtime(true);
186 |
187 | while ($written < $len) {
188 | $this->setErrorHandler();
189 | try {
190 | $result = 0;
191 | if ($this->select_write()) {
192 | // if data is smaller than buffer - no need to cut part of it
193 | if ($len <= self::BUFFER_SIZE) {
194 | $buffer = $data;
195 | } else {
196 | $buffer = mb_substr($data, $written, self::BUFFER_SIZE, 'ASCII');
197 | }
198 | $result = socket_write($this->sock, $buffer);
199 | }
200 | $this->throwOnError();
201 | } catch (\ErrorException $e) {
202 | $code = socket_last_error($this->sock);
203 | $constants = SocketConstants::getInstance();
204 | switch ($code) {
205 | case $constants->SOCKET_EPIPE:
206 | case $constants->SOCKET_ENETDOWN:
207 | case $constants->SOCKET_ENETUNREACH:
208 | case $constants->SOCKET_ENETRESET:
209 | case $constants->SOCKET_ECONNABORTED:
210 | case $constants->SOCKET_ECONNRESET:
211 | case $constants->SOCKET_ECONNREFUSED:
212 | case $constants->SOCKET_ETIMEDOUT:
213 | $this->close();
214 | throw new AMQPConnectionClosedException(socket_strerror($code), $code, $e);
215 | default:
216 | throw new AMQPIOException(sprintf(
217 | 'Error sending data. Last SocketError: %s',
218 | socket_strerror($code)
219 | ), $code, $e);
220 | }
221 | } finally {
222 | $this->restoreErrorHandler();
223 | }
224 |
225 | if ($result === false) {
226 | throw new AMQPIOException(sprintf(
227 | 'Error sending data. Last SocketError: %s',
228 | socket_strerror(socket_last_error($this->sock))
229 | ));
230 | }
231 |
232 | $now = microtime(true);
233 | if ($result > 0) {
234 | $this->last_write = $write_start = $now;
235 | $written += $result;
236 | } else {
237 | if (($now - $write_start) > $this->write_timeout) {
238 | throw AMQPTimeoutException::writeTimeout($this->write_timeout);
239 | }
240 | }
241 | }
242 | }
243 |
244 | /**
245 | * @inheritdoc
246 | */
247 | public function close()
248 | {
249 | $this->disableHeartbeat();
250 | if (is_resource($this->sock) || is_a($this->sock, \Socket::class)) {
251 | socket_close($this->sock);
252 | }
253 | $this->sock = null;
254 | $this->last_read = 0;
255 | $this->last_write = 0;
256 | }
257 |
258 | /**
259 | * @inheritdoc
260 | */
261 | protected function do_select(?int $sec, int $usec)
262 | {
263 | if (!is_resource($this->sock) && !is_a($this->sock, \Socket::class)) {
264 | $this->sock = null;
265 | throw new AMQPConnectionClosedException('Broken pipe or closed connection', 0);
266 | }
267 |
268 | $read = array($this->sock);
269 | $write = null;
270 | $except = null;
271 |
272 | return socket_select($read, $write, $except, $sec, $usec);
273 | }
274 |
275 | /**
276 | * @return int|bool
277 | */
278 | protected function select_write()
279 | {
280 | $read = $except = null;
281 | $write = array($this->sock);
282 |
283 | return socket_select($read, $write, $except, 0, 100000);
284 | }
285 |
286 | /**
287 | * @throws \PhpAmqpLib\Exception\AMQPIOException
288 | */
289 | protected function enable_keepalive(): void
290 | {
291 | if (!defined('SOL_SOCKET') || !defined('SO_KEEPALIVE')) {
292 | throw new AMQPIOException('Can not enable keepalive: SOL_SOCKET or SO_KEEPALIVE is not defined');
293 | }
294 |
295 | socket_set_option($this->sock, SOL_SOCKET, SO_KEEPALIVE, 1);
296 | }
297 |
298 | /**
299 | * @inheritdoc
300 | */
301 | public function error_handler($errno, $errstr, $errfile, $errline): void
302 | {
303 | $constants = SocketConstants::getInstance();
304 | // socket_select warning that it has been interrupted by a signal - EINTR
305 | if (isset($constants->SOCKET_EINTR) && false !== strrpos($errstr, socket_strerror($constants->SOCKET_EINTR))) {
306 | // it's allowed while processing signals
307 | return;
308 | }
309 |
310 | parent::error_handler($errno, $errstr, $errfile, $errline);
311 | }
312 |
313 | /**
314 | * @inheritdoc
315 | */
316 | protected function setErrorHandler(): void
317 | {
318 | parent::setErrorHandler();
319 | socket_clear_error($this->sock);
320 | }
321 |
322 | private function isIpv6(): bool
323 | {
324 | $ipv6 = filter_var($this->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
325 |
326 | if ($ipv6 !== false || checkdnsrr($this->host, 'AAAA')) {
327 | return true;
328 | }
329 |
330 | return false;
331 | }
332 | }
333 |
--------------------------------------------------------------------------------
/PhpAmqpLib/Wire/IO/StreamIO.php:
--------------------------------------------------------------------------------
1 | <?php
2 |
3 | namespace PhpAmqpLib\Wire\IO;
4 |
5 | use PhpAmqpLib\Exception\AMQPConnectionClosedException;
6 | use PhpAmqpLib\Exception\AMQPDataReadException;
7 | use PhpAmqpLib\Exception\AMQPIOException;
8 | use PhpAmqpLib\Exception\AMQPRuntimeException;
9 | use PhpAmqpLib\Exception\AMQPTimeoutException;
10 | use PhpAmqpLib\Helper\MiscHelper;
11 | use PhpAmqpLib\Helper\SocketConstants;
12 |
13 | class StreamIO extends AbstractIO
14 | {
15 | /** @var null|resource */
16 | protected $context;
17 |
18 | /** @var null|resource */
19 | private $sock;
20 |
21 | /**
22 | * @param string $host
23 | * @param int $port
24 | * @param float $connection_timeout
25 | * @param float $read_write_timeout
26 | * @param resource|null $context
27 | * @param bool $keepalive
28 | * @param int $heartbeat
29 | * @param string|null $ssl_protocol @deprecated
30 | */
31 | public function __construct(
32 | $host,
33 | $port,
34 | $connection_timeout,
35 | $read_write_timeout,
36 | $context = null,
37 | $keepalive = false,
38 | $heartbeat = 0,
39 | $ssl_protocol = null
40 | ) {
41 | if (func_num_args() === 8) {
42 | trigger_error(
43 | '$ssl_protocol parameter is deprecated, use stream_context_set_option($context, \'ssl\', \'crypto_method\', $ssl_protocol) instead (see https://www.php.net/manual/en/function.stream-socket-enable-crypto.php for possible values)',
44 | E_USER_DEPRECATED
45 | );
46 | }
47 | // TODO FUTURE change comparison to <=
48 | // php-amqplib/php-amqplib#648, php-amqplib/php-amqplib#666
49 | /*
50 | TODO FUTURE enable this check
51 | if ($heartbeat !== 0 && ($read_write_timeout < ($heartbeat * 2))) {
52 | throw new \InvalidArgumentException('read_write_timeout must be at least 2x the heartbeat');
53 | }
54 | */
55 |
56 | $this->host = $host;
57 | $this->port = $port;
58 | $this->connection_timeout = $connection_timeout;
59 | $this->read_timeout = (float)$read_write_timeout;
60 | $this->write_timeout = (float)$read_write_timeout;
61 | $this->context = $context;
62 | $this->keepalive = $keepalive;
63 | $this->heartbeat = $heartbeat;
64 | $this->initial_heartbeat = $heartbeat;
65 | $this->canDispatchPcntlSignal = $this->isPcntlSignalEnabled();
66 | }
67 |
68 | /**
69 | * @inheritdoc
70 | */
71 | public function connect()
72 | {
73 | $errstr = $errno = null;
74 |
75 | $remote = sprintf(
76 | 'tcp://%s:%s',
77 | $this->host,
78 | $this->port
79 | );
80 |
81 | $context = $this->setupContext();
82 | $this->setErrorHandler();
83 |
84 | try {
85 | $this->sock = stream_socket_client(
86 | $remote,
87 | $errno,
88 | $errstr,
89 | $this->connection_timeout,
90 | STREAM_CLIENT_CONNECT,
91 | $context
92 | );
93 | $this->throwOnError();
94 | } catch (\ErrorException $e) {
95 | throw new AMQPIOException($e->getMessage(), $e->getCode(), $e->getPrevious());
96 | } finally {
97 | $this->restoreErrorHandler();
98 | }
99 |
100 | if (false === $this->sock) {
101 | throw new AMQPIOException(
102 | sprintf(
103 | 'Error Connecting to server(%s): %s ',
104 | $errno,
105 | $errstr
106 | ),
107 | $errno
108 | );
109 | }
110 |
111 | if (!stream_socket_get_name($this->sock, true)) {
112 | throw new AMQPIOException(
113 | sprintf(
114 | 'Connection refused: %s ',
115 | $remote
116 | )
117 | );
118 | }
119 |
120 | list($sec, $uSec) = MiscHelper::splitSecondsMicroseconds(max($this->read_timeout, $this->write_timeout));
121 | if (!stream_set_timeout($this->sock, $sec, $uSec)) {
122 | throw new AMQPIOException('Timeout could not be set');
123 | }
124 |
125 | // php cannot capture signals while streams are blocking
126 | if ($this->canDispatchPcntlSignal) {
127 | stream_set_blocking($this->sock, false);
128 | stream_set_write_buffer($this->sock, 0);
129 | if (function_exists('stream_set_read_buffer')) {
130 | stream_set_read_buffer($this->sock, 0);
131 | }
132 | } else {
133 | stream_set_blocking($this->sock, true);
134 | }
135 |
136 | if ($this->keepalive) {
137 | $this->enable_keepalive();
138 | }
139 |
140 | $options = stream_context_get_options($context);
141 | if (isset($options['ssl']['crypto_method'])) {
142 | $this->enableCrypto();
143 | }
144 |
145 | $this->heartbeat = $this->initial_heartbeat;
146 | }
147 |
148 | /**
149 | * @return resource
150 | * @throws AMQPIOException
151 | */
152 | private function setupContext()
153 | {
154 | $context = $this->context;
155 | if (!is_resource($context) || get_resource_type($context) !== 'stream-context') {
156 | $context = stream_context_create();
157 | }
158 |
159 | stream_context_set_option($context, 'socket', 'tcp_nodelay', true);
160 |
161 | $options = stream_context_get_options($context);
162 | if (!empty($options['ssl']) && !isset($options['ssl']['crypto_method'])) {
163 | if (!stream_context_set_option($context, 'ssl', 'crypto_method', STREAM_CRYPTO_METHOD_ANY_CLIENT)) {
164 | throw new AMQPIOException("Can not set ssl.crypto_method stream context option");
165 | }
166 | }
167 |
168 | return $context;
169 | }
170 |
171 | /**
172 | * @inheritdoc
173 | */
174 | public function read($len)
175 | {
176 | $this->check_heartbeat();
177 |
178 | list($timeout_sec, $timeout_uSec) = MiscHelper::splitSecondsMicroseconds($this->read_timeout);
179 |
180 | $read_start = microtime(true);
181 | $read = 0;
182 | $data = '';
183 |
184 | while ($read < $len) {
185 | if (!is_resource($this->sock) || feof($this->sock)) {
186 | $this->close();
187 | throw new AMQPConnectionClosedException('Broken pipe or closed connection');
188 | }
189 |
190 | $this->setErrorHandler();
191 | try {
192 | $buffer = fread($this->sock, ($len - $read));
193 | $this->throwOnError();
194 | } catch (\ErrorException $e) {
195 | throw new AMQPDataReadException($e->getMessage(), $e->getCode(), $e->getPrevious());
196 | } finally {
197 | $this->restoreErrorHandler();
198 | }
199 |
200 | if ($buffer === false) {
201 | throw new AMQPDataReadException('Error receiving data');
202 | }
203 |
204 | if ($buffer === '') {
205 | $read_now = microtime(true);
206 | $t_read = $read_now - $read_start;
207 | if ($t_read > $this->read_timeout) {
208 | throw new AMQPTimeoutException('Too many read attempts detected in StreamIO');
209 | }
210 | $this->select($timeout_sec, $timeout_uSec);
211 |
212 | continue;
213 | }
214 |
215 | $this->last_read = microtime(true);
216 | $read_start = $this->last_read;
217 | $read += mb_strlen($buffer, 'ASCII');
218 | $data .= $buffer;
219 | }
220 |
221 | if (mb_strlen($data, 'ASCII') !== $len) {
222 | throw new AMQPDataReadException(
223 | sprintf(
224 | 'Error reading data. Received %s instead of expected %s bytes',
225 | mb_strlen($data, 'ASCII'),
226 | $len
227 | )
228 | );
229 | }
230 |
231 | $this->last_read = microtime(true);
232 |
233 | return $data;
234 | }
235 |
236 | /**
237 | * @inheritdoc
238 | */
239 | public function write($data)
240 | {
241 | $this->checkBrokerHeartbeat();
242 |
243 | $written = 0;
244 | $len = mb_strlen($data, 'ASCII');
245 | $write_start = microtime(true);
246 |
247 | while ($written < $len) {
248 | // on Windows, feof() fails when connecting to some (but not all) servers
249 | if (!is_resource($this->sock) || (PHP_OS_FAMILY != 'Windows' && feof($this->sock))) {
250 | $this->close();
251 | $constants = SocketConstants::getInstance();
252 | throw new AMQPConnectionClosedException('Broken pipe or closed connection', $constants->SOCKET_EPIPE);
253 | }
254 |
255 | $result = false;
256 | $this->setErrorHandler();
257 | // OpenSSL's C library function SSL_write() can balk on buffers > 8192
258 | // bytes in length, so we're limiting the write size here. On both TLS
259 | // and plaintext connections, the write loop will continue until the
260 | // buffer has been fully written.
261 | // This behavior has been observed in OpenSSL dating back to at least
262 | // September 2002:
263 | // http://comments.gmane.org/gmane.comp.encryption.openssl.user/4361
264 | try {
265 | // check stream and prevent from high CPU usage
266 | $result = 0;
267 | if ($this->select_write()) {
268 | // if data is smaller than buffer - no need to cut part of it
269 | if ($len <= self::BUFFER_SIZE) {
270 | $buffer = $data;
271 | } else {
272 | $buffer = mb_substr($data, $written, self::BUFFER_SIZE, 'ASCII');
273 | }
274 | $result = fwrite($this->sock, $buffer);
275 | }
276 | $this->throwOnError();
277 | } catch (\ErrorException $e) {
278 | $code = $e->getCode();
279 | $constants = SocketConstants::getInstance();
280 | switch ($code) {
281 | case $constants->SOCKET_EPIPE:
282 | case $constants->SOCKET_ENETDOWN:
283 | case $constants->SOCKET_ENETUNREACH:
284 | case $constants->SOCKET_ENETRESET:
285 | case $constants->SOCKET_ECONNABORTED:
286 | case $constants->SOCKET_ECONNRESET:
287 | case $constants->SOCKET_ECONNREFUSED:
288 | case $constants->SOCKET_ETIMEDOUT:
289 | $this->close();
290 | throw new AMQPConnectionClosedException(socket_strerror($code), $code, $e);
291 | default:
292 | throw new AMQPRuntimeException($e->getMessage(), $code, $e);
293 | }
294 | } finally {
295 | $this->restoreErrorHandler();
296 | }
297 |
298 | if ($result === false) {
299 | throw new AMQPRuntimeException('Error sending data');
300 | }
301 |
302 | if ($this->timed_out()) {
303 | throw AMQPTimeoutException::writeTimeout($this->write_timeout);
304 | }
305 |
306 | $now = microtime(true);
307 | if ($result > 0) {
308 | $this->last_write = $write_start = $now;
309 | $written += $result;
310 | } else {
311 | if (feof($this->sock)) {
312 | $this->close();
313 | throw new AMQPConnectionClosedException('Broken pipe or closed connection');
314 | }
315 | if (($now - $write_start) > $this->write_timeout) {
316 | throw AMQPTimeoutException::writeTimeout($this->write_timeout);
317 | }
318 | }
319 | }
320 | }
321 |
322 | /**
323 | * @inheritdoc
324 | */
325 | public function error_handler($errno, $errstr, $errfile, $errline): void
326 | {
327 | $code = $this->extract_error_code($errstr);
328 | $constants = SocketConstants::getInstance();
329 | switch ($code) {
330 | // fwrite notice that the stream isn't ready - EAGAIN or EWOULDBLOCK
331 | case $constants->SOCKET_EAGAIN:
332 | case $constants->SOCKET_EWOULDBLOCK:
333 | // stream_select warning that it has been interrupted by a signal - EINTR
334 | case $constants->SOCKET_EINTR:
335 | return;
336 | }
337 |
338 | parent::error_handler($code > 0 ? $code : $errno, $errstr, $errfile, $errline);
339 | }
340 |
341 | public function close()
342 | {
343 | $this->disableHeartbeat();
344 | if (is_resource($this->sock)) {
345 | fclose($this->sock);
346 | }
347 | $this->sock = null;
348 | $this->last_read = 0;
349 | $this->last_write = 0;
350 | }
351 |
352 | /**
353 | * @deprecated
354 | * @return null|resource|\Socket
355 | */
356 | public function getSocket()
357 | {
358 | return $this->sock;
359 | }
360 |
361 | /**
362 | * @inheritdoc
363 | */
364 | protected function do_select(?int $sec, int $usec)
365 | {
366 | if ($this->sock === null || !is_resource($this->sock)) {
367 | $this->sock = null;
368 | throw new AMQPConnectionClosedException('Broken pipe or closed connection', 0);
369 | }
370 |
371 | $read = array($this->sock);
372 | $write = null;
373 | $except = null;
374 |
375 | if ($sec === null && PHP_VERSION_ID >= 80100) {
376 | $usec = 0;
377 | }
378 |
379 | return stream_select($read, $write, $except, $sec, $usec);
380 | }
381 |
382 | /**
383 | * @return int|bool
384 | */
385 | protected function select_write()
386 | {
387 | $read = $except = null;
388 | $write = array($this->sock);
389 |
390 | return stream_select($read, $write, $except, 0, 100000);
391 | }
392 |
393 | /**
394 | * @return mixed
395 | */
396 | protected function timed_out()
397 | {
398 | // get status of socket to determine whether or not it has timed out
399 | $info = stream_get_meta_data($this->sock);
400 |
401 | return $info['timed_out'];
402 | }
403 |
404 | /**
405 | * @throws \PhpAmqpLib\Exception\AMQPIOException
406 | */
407 | protected function enable_keepalive(): void
408 | {
409 | if (!function_exists('socket_import_stream')) {
410 | throw new AMQPIOException('Can not enable keepalive: function socket_import_stream does not exist');
411 | }
412 |
413 | if (!defined('SOL_SOCKET') || !defined('SO_KEEPALIVE')) {
414 | throw new AMQPIOException('Can not enable keepalive: SOL_SOCKET or SO_KEEPALIVE is not defined');
415 | }
416 |
417 | $socket = socket_import_stream($this->sock);
418 | socket_set_option($socket, SOL_SOCKET, SO_KEEPALIVE, 1);
419 | }
420 |
421 | /**
422 | * @param string $message
423 | * @return int
424 | */
425 | protected function extract_error_code($message)
426 | {
427 | if (0 === strpos($message, 'stream_select():')) {
428 | $pattern = '/\s+\[(\d+)\]:\s+/';
429 | } else {
430 | $pattern = '/\s+errno=(\d+)\s+/';
431 | }
432 | $matches = array();
433 | $result = preg_match($pattern, $message, $matches);
434 | if ($result > 0) {
435 | return (int)$matches[1];
436 | }
437 |
438 | return 0;
439 | }
440 |
441 | /**
442 | * @throws AMQPIOException
443 | */
444 | private function enableCrypto(): void
445 | {
446 | $timeout_at = time() + ($this->read_timeout + $this->write_timeout) * 2; // 2 round-trips during handshake
447 |
448 | try {
449 | $this->setErrorHandler();
450 | do {
451 | $enabled = stream_socket_enable_crypto($this->sock, true);
452 | if ($enabled === true) {
453 | return;
454 | }
455 | $this->throwOnError();
456 | usleep(1e3);
457 | } while ($enabled === 0 && time() < $timeout_at);
458 | } catch (\ErrorException $exception) {
459 | throw new AMQPIOException($exception->getMessage(), $exception->getCode(), $exception->getPrevious());
460 | } finally {
461 | $this->restoreErrorHandler();
462 | }
463 |
464 | if ($enabled !== true) {
465 | throw new AMQPIOException('Could not enable socket crypto');
466 | }
467 | }
468 | }
469 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # php-amqplib #
2 |
3 | 
4 | [![Latest Version on Packagist][ico-version]][link-packagist]
5 | [![Total Downloads][ico-downloads]][link-downloads]
6 | [![Software License][ico-license]](LICENSE)
7 |
8 | [](https://codecov.io/gh/php-amqplib/php-amqplib)
9 | [![Coverage Status][ico-scrutinizer]][link-scrutinizer]
10 | [![Quality Score][ico-code-quality]][link-code-quality]
11 |
12 | This library is a _pure PHP_ implementation of the [AMQP 0-9-1 protocol](http://www.rabbitmq.com/tutorials/amqp-concepts.html).
13 | It's been tested against [RabbitMQ](http://www.rabbitmq.com/).
14 |
15 | The library was used for the PHP examples of [RabbitMQ in Action](http://manning.com/videla/) and the [official RabbitMQ tutorials](http://www.rabbitmq.com/tutorials/tutorial-one-php.html).
16 |
17 | Please note that this project is released with a [Contributor Code of Conduct](.github/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
18 |
19 | ## Project Maintainers
20 |
21 | Thanks to [videlalvaro](https://github.com/videlalvaro) and [postalservice14](https://github.com/postalservice14) for creating `php-amqplib`.
22 |
23 | The package is now maintained by [Ramūnas Dronga](https://github.com/ramunasd), [Luke Bakken](https://github.com/lukebakken) and several VMware engineers working on RabbitMQ.
24 |
25 | ## Supported RabbitMQ Versions ##
26 |
27 | Starting with version 2.0 this library uses `AMQP 0.9.1` by default and thus requires [RabbitMQ 2.0 or later version](http://www.rabbitmq.com/download.html).
28 | Usually server upgrades do not require any application code changes since
29 | the protocol changes very infrequently but please conduct your own testing before upgrading.
30 |
31 | ## Supported RabbitMQ Extensions ##
32 |
33 | Since the library uses `AMQP 0.9.1` we added support for the following RabbitMQ extensions:
34 |
35 | * Exchange to Exchange Bindings
36 | * Basic Nack
37 | * Publisher Confirms
38 | * Consumer Cancel Notify
39 |
40 | Extensions that modify existing methods like `alternate exchanges` are also supported.
41 |
42 | ### Related libraries
43 |
44 | * [enqueue/amqp-lib](https://github.com/php-enqueue/amqp-lib) is a [amqp interop](https://github.com/queue-interop/queue-interop#amqp-interop) compatible wrapper.
45 |
46 | * [AMQProxy](https://github.com/cloudamqp/amqproxy) is a proxy library with connection and channel pooling/reusing. This allows for lower connection and channel churn when using php-amqplib, leading to less CPU usage of RabbitMQ.
47 |
48 | ## Setup ##
49 |
50 | Ensure you have [composer](http://getcomposer.org) installed, then run the following command:
51 |
52 | ```bash
53 | composer require php-amqplib/php-amqplib
54 | ```
55 |
56 | That will fetch the library and its dependencies inside your vendor folder. Then you can add the following to your
57 | .php files in order to use the library
58 |
59 | ```php
60 | require_once __DIR__.'/vendor/autoload.php';
61 | ```
62 |
63 | Then you need to `use` the relevant classes, for example:
64 |
65 | ```php
66 | use PhpAmqpLib\Connection\AMQPStreamConnection;
67 | use PhpAmqpLib\Message\AMQPMessage;
68 | ```
69 |
70 | ## Usage ##
71 |
72 | With RabbitMQ running open two Terminals and on the first one execute the following commands to start the consumer:
73 |
74 | ```bash
75 | cd php-amqplib/demo
76 | php amqp_consumer.php
77 | ```
78 |
79 | Then on the other Terminal do:
80 |
81 | ```bash
82 | cd php-amqplib/demo
83 | php amqp_publisher.php some text to publish
84 | ```
85 |
86 | You should see the message arriving to the process on the other Terminal
87 |
88 | Then to stop the consumer, send to it the `quit` message:
89 |
90 | ```bash
91 | php amqp_publisher.php quit
92 | ```
93 |
94 | If you need to listen to the sockets used to connect to RabbitMQ then see the example in the non blocking consumer.
95 |
96 | ```bash
97 | php amqp_consumer_non_blocking.php
98 | ```
99 |
100 | ## Change log
101 |
102 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
103 |
104 | ## API Documentation ##
105 |
106 | http://php-amqplib.github.io/php-amqplib/
107 |
108 | ## Tutorials ##
109 |
110 | To not repeat ourselves, if you want to learn more about this library,
111 | please refer to the [official RabbitMQ tutorials](http://www.rabbitmq.com/tutorials/tutorial-one-php.html).
112 |
113 | ## More Examples ##
114 |
115 | - `amqp_ha_consumer.php`: demos the use of mirrored queues.
116 | - `amqp_consumer_exclusive.php` and `amqp_publisher_exclusive.php`: demos fanout exchanges using exclusive queues.
117 | - `amqp_consumer_fanout_{1,2}.php` and `amqp_publisher_fanout.php`: demos fanout exchanges with named queues.
118 | - `amqp_consumer_pcntl_heartbeat.php`: demos signal-based heartbeat sender usage.
119 | - `basic_get.php`: demos obtaining messages from the queues by using the _basic get_ AMQP call.
120 |
121 | ## Multiple hosts connections ##
122 |
123 | If you have a cluster of multiple nodes to which your application can connect,
124 | you can start a connection with an array of hosts. To do that you should use
125 | the `create_connection` static method.
126 |
127 | For example:
128 | ```php
129 | $connection = AMQPStreamConnection::create_connection([
130 | ['host' => HOST1, 'port' => PORT, 'user' => USER, 'password' => PASS, 'vhost' => VHOST],
131 | ['host' => HOST2, 'port' => PORT, 'user' => USER, 'password' => PASS, 'vhost' => VHOST]
132 | ],
133 | $options);
134 | ```
135 |
136 | This code will try to connect to `HOST1` first, and connect to `HOST2` if the
137 | first connection fails. The method returns a connection object for the first
138 | successful connection. Should all connections fail it will throw the exception
139 | from the last connection attempt.
140 |
141 | See `demo/amqp_connect_multiple_hosts.php` for more examples.
142 |
143 | ## Batch Publishing ##
144 |
145 | Let's say you have a process that generates a bunch of messages that are going to be published to the same `exchange` using the same `routing_key` and options like `mandatory`.
146 | Then you could make use of the `batch_basic_publish` library feature. You can batch messages like this:
147 |
148 | ```php
149 | $msg = new AMQPMessage($msg_body);
150 | $ch->batch_basic_publish($msg, $exchange);
151 |
152 | $msg2 = new AMQPMessage($msg_body);
153 | $ch->batch_basic_publish($msg2, $exchange);
154 | ```
155 |
156 | and then send the batch like this:
157 |
158 | ```php
159 | $ch->publish_batch();
160 | ```
161 |
162 | ### When do we publish the message batch? ###
163 |
164 | Let's say our program needs to read from a file and then publish one message per line. Depending on the message size, you will have to decide when it's better to send the batch.
165 | You could send it every 50 messages, or every hundred. That's up to you.
166 |
167 | ## Optimized Message Publishing ##
168 |
169 | Another way to speed up your message publishing is by reusing the `AMQPMessage` message instances. You can create your new message like this:
170 |
171 | ```php
172 | $properties = array('content_type' => 'text/plain', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT);
173 | $msg = new AMQPMessage($body, $properties);
174 | $ch->basic_publish($msg, $exchange);
175 | ```
176 |
177 | Now let's say that while you want to change the message body for future messages, you will keep the same properties, that is, your messages will still be `text/plain` and the `delivery_mode` will still be `AMQPMessage::DELIVERY_MODE_PERSISTENT`. If you create a new `AMQPMessage` instance for every published message, then those properties would have to be re-encoded in the AMQP binary format. You could avoid all that by just reusing the `AMQPMessage` and then resetting the message body like this:
178 |
179 | ```php
180 | $msg->setBody($body2);
181 | $ch->basic_publish($msg, $exchange);
182 | ```
183 |
184 | ## Truncating Large Messages ##
185 |
186 | AMQP imposes no limit on the size of messages; if a very large message is received by a consumer, PHP's memory limit may be reached
187 | within the library before the callback passed to `basic_consume` is called.
188 |
189 | To avoid this, you can call the method `AMQPChannel::setBodySizeLimit(int $bytes)` on your Channel instance. Body sizes exceeding this limit will be truncated,
190 | and delivered to your callback with a `AMQPMessage::$is_truncated` flag set to `true`. The property `AMQPMessage::$body_size` will reflect the true body size of
191 | a received message, which will be higher than `strlen(AMQPMessage::getBody())` if the message has been truncated.
192 |
193 | Note that all data above the limit is read from the AMQP Channel and immediately discarded, so there is no way to retrieve it within your
194 | callback. If you have another consumer which can handle messages with larger payloads, you can use `basic_reject` or `basic_nack` to tell
195 | the server (which still has a complete copy) to forward it to a Dead Letter Exchange.
196 |
197 | By default, no truncation will occur. To disable truncation on a Channel that has had it enabled, pass `0` (or `null`) to `AMQPChannel::setBodySizeLimit()`.
198 |
199 | ## Connection recovery ##
200 |
201 | Some RabbitMQ clients using automated connection recovery mechanisms to reconnect
202 | and recover channels and consumers in case of network errors.
203 |
204 | Since this client is using a single-thread, you can set up connection recovery
205 | using exception handling mechanism.
206 |
207 | Exceptions which might be thrown in case of connection errors:
208 |
209 | ```php
210 | PhpAmqpLib\Exception\AMQPConnectionClosedException
211 | PhpAmqpLib\Exception\AMQPIOException
212 | \RuntimeException
213 | \ErrorException
214 | ```
215 |
216 | Some other exceptions might be thrown, but connection can still be there. It's
217 | always a good idea to clean up an old connection when handling an exception
218 | before reconnecting.
219 |
220 | For example, if you want to set up a recovering connection:
221 |
222 | ```php
223 | $connection = null;
224 | $channel = null;
225 | while(true){
226 | try {
227 | $connection = new AMQPStreamConnection(HOST, PORT, USER, PASS, VHOST);
228 | // Your application code goes here.
229 | do_something_with_connection($connection);
230 | } catch(AMQPRuntimeException $e) {
231 | echo $e->getMessage();
232 | cleanup_connection($connection);
233 | usleep(WAIT_BEFORE_RECONNECT_uS);
234 | } catch(\RuntimeException $e) {
235 | cleanup_connection($connection);
236 | usleep(WAIT_BEFORE_RECONNECT_uS);
237 | } catch(\ErrorException $e) {
238 | cleanup_connection($connection);
239 | usleep(WAIT_BEFORE_RECONNECT_uS);
240 | }
241 | }
242 |
243 | ```
244 |
245 | A full example is in `demo/connection_recovery_consume.php`.
246 |
247 | This code will reconnect and retry the application code every time the
248 | exception occurs. Some exceptions can still be thrown and should not be handled
249 | as a part of reconnection process, because they might be application errors.
250 |
251 | This approach makes sense mostly for consumer applications, producers will
252 | require some additional application code to avoid publishing the same message
253 | multiple times.
254 |
255 | This was a simplest example, in a real-life application you might want to
256 | control retr count and maybe gracefully degrade wait time to reconnection.
257 |
258 | You can find a more excessive example in [#444](https://github.com/php-amqplib/php-amqplib/issues/444)
259 |
260 |
261 | ## UNIX Signals ##
262 |
263 | If you have installed [PCNTL extension](http://www.php.net/manual/en/book.pcntl.php) dispatching of signal will be handled when consumer is not processing message.
264 |
265 | ```php
266 | $pcntlHandler = function ($signal) {
267 | switch ($signal) {
268 | case \SIGTERM:
269 | case \SIGUSR1:
270 | case \SIGINT:
271 | // some stuff before stop consumer e.g. delete lock etc
272 | pcntl_signal($signal, SIG_DFL); // restore handler
273 | posix_kill(posix_getpid(), $signal); // kill self with signal, see https://www.cons.org/cracauer/sigint.html
274 | case \SIGHUP:
275 | // some stuff to restart consumer
276 | break;
277 | default:
278 | // do nothing
279 | }
280 | };
281 |
282 | pcntl_signal(\SIGTERM, $pcntlHandler);
283 | pcntl_signal(\SIGINT, $pcntlHandler);
284 | pcntl_signal(\SIGUSR1, $pcntlHandler);
285 | pcntl_signal(\SIGHUP, $pcntlHandler);
286 | ```
287 |
288 | To disable this feature just define constant `AMQP_WITHOUT_SIGNALS` as `true`
289 |
290 | ```php
291 | <?php
292 | define('AMQP_WITHOUT_SIGNALS', true);
293 |
294 | ... more code
295 |
296 | ```
297 |
298 |
299 | ## Signal-based Heartbeat ##
300 |
301 | If you have installed [PCNTL extension](http://www.php.net/manual/en/book.pcntl.php) and are using PHP 7.1 or greater,
302 | you can register a signal-based heartbeat sender.
303 |
304 | ```php
305 | <?php
306 |
307 | $sender = new PCNTLHeartbeatSender($connection);
308 | $sender->register();
309 | ... code
310 | $sender->unregister();
311 |
312 | ```
313 |
314 | ## Debugging ##
315 |
316 | If you want to know what's going on at a protocol level then add the following constant to your code:
317 |
318 | ```php
319 | <?php
320 | define('AMQP_DEBUG', true);
321 |
322 | ... more code
323 |
324 | ?>
325 | ```
326 |
327 | ## Benchmarks ##
328 |
329 | To run the publishing/consume benchmark type:
330 |
331 | ```bash
332 | make benchmark
333 | ```
334 |
335 | ## Tests and Contributing
336 |
337 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
338 |
339 | ## Using AMQP 0.8 ##
340 |
341 | If you still want to use the old version of the protocol then you can do it by setting the following constant in your configuration code:
342 |
343 | ```php
344 | define('AMQP_PROTOCOL', '0.8');
345 | ```
346 |
347 | The default value is `'0.9.1'`.
348 |
349 | ## Providing your own autoloader ##
350 |
351 | If for some reason you don't want to use composer, then you need to have an autoloader in place fo the library classes. People have [reported](https://github.com/videlalvaro/php-amqplib/issues/61#issuecomment-37855050) to use this [autoloader](https://gist.github.com/jwage/221634) with success.
352 |
353 | ## Original README: ##
354 |
355 | Below is the original README file content. Credits goes to the original authors.
356 |
357 | PHP library implementing Advanced Message Queuing Protocol (AMQP).
358 |
359 | The library is port of python code of py-amqplib
360 | http://barryp.org/software/py-amqplib/
361 |
362 | It have been tested with RabbitMQ server.
363 |
364 | Project home page: http://code.google.com/p/php-amqplib/
365 |
366 | For discussion, please join the group:
367 |
368 | http://groups.google.com/group/php-amqplib-devel
369 |
370 | For bug reports, please use bug tracking system at the project page.
371 |
372 | Patches are very welcome!
373 |
374 | Author: Vadim Zaliva <lord@crocodile.org>
375 |
376 | [ico-version]: https://img.shields.io/packagist/v/php-amqplib/php-amqplib.svg?style=flat-square
377 | [ico-license]: https://img.shields.io/badge/license-LGPL_2.1-brightgreen.svg?style=flat-square
378 | [ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/php-amqplib/php-amqplib.svg?style=flat-square
379 | [ico-code-quality]: https://img.shields.io/scrutinizer/g/php-amqplib/php-amqplib.svg?style=flat-square
380 | [ico-downloads]: https://img.shields.io/packagist/dt/php-amqplib/php-amqplib.svg?style=flat-square
381 |
382 | [link-packagist]: https://packagist.org/packages/php-amqplib/php-amqplib
383 | [link-scrutinizer]: https://scrutinizer-ci.com/g/php-amqplib/php-amqplib/code-structure
384 | [link-code-quality]: https://scrutinizer-ci.com/g/php-amqplib/php-amqplib
385 | [link-downloads]: https://packagist.org/packages/php-amqplib/php-amqplib
386 | [link-author]: https://github.com/php-amqplib
387 | [link-contributors]: ../../contributors
388 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | project:
4 | default:
5 | target: auto
6 | threshold: 0.5
7 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "php-amqplib/php-amqplib",
3 | "replace": {
4 | "videlalvaro/php-amqplib": "self.version"
5 | },
6 | "type": "library",
7 | "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.",
8 | "keywords": ["rabbitmq", "message", "queue"],
9 | "homepage": "https://github.com/php-amqplib/php-amqplib/",
10 | "authors": [
11 | {
12 | "name": "Alvaro Videla",
13 | "role": "Original Maintainer"
14 | },
15 | {
16 | "name": "Raúl Araya",
17 | "email": "nubeiro@gmail.com",
18 | "role": "Maintainer"
19 | },
20 | {
21 | "name": "Luke Bakken",
22 | "email": "luke@bakken.io",
23 | "role": "Maintainer"
24 | },
25 | {
26 | "name": "Ramūnas Dronga",
27 | "email": "github@ramuno.lt",
28 | "role": "Maintainer"
29 | }
30 | ],
31 | "require": {
32 | "php": "^7.2||^8.0",
33 | "ext-sockets": "*",
34 | "ext-mbstring": "*",
35 | "phpseclib/phpseclib": "^2.0|^3.0"
36 | },
37 | "require-dev": {
38 | "ext-curl": "*",
39 | "phpunit/phpunit": "^7.5|^9.5",
40 | "squizlabs/php_codesniffer": "^3.6",
41 | "nategood/httpful": "^0.2.20"
42 | },
43 | "conflict": {
44 | "php": "7.4.0 - 7.4.1"
45 | },
46 | "autoload": {
47 | "psr-4": {
48 | "PhpAmqpLib\\": "PhpAmqpLib/"
49 | }
50 | },
51 | "autoload-dev": {
52 | "psr-4": {
53 | "PhpAmqpLib\\Tests\\Functional\\": "tests/Functional",
54 | "PhpAmqpLib\\Tests\\Unit\\": "tests/Unit"
55 | }
56 | },
57 | "license": "LGPL-2.1-or-later",
58 | "extra": {
59 | "branch-alias": {
60 | "dev-master": "3.0-dev"
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/phpcs.xml.dist:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 | xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
4 |
5 | <arg name="basepath" value="."/>
6 | <arg name="cache" value=".phpcs-cache"/>
7 | <arg name="colors"/>
8 | <arg value="p"/>
9 | <arg name="extensions" value="php"/>
10 | <arg name="tab-width" value="4"/>
11 | <arg name="report-width" value="120"/>
12 |
13 | <rule ref="PEAR.Functions.ValidDefaultValue">
14 | <!-- We should preserve BC with possible child classes until next major version -->
15 | <exclude-pattern>PhpAmqpLib/Helper/Protocol/Protocol091.php</exclude-pattern>
16 | <exclude-pattern>PhpAmqpLib/Helper/Protocol/Protocol080.php</exclude-pattern>
17 | <exclude-pattern>PhpAmqpLib/Connection/AbstractConnection.php</exclude-pattern>
18 | </rule>
19 |
20 | <rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
21 | <!-- We should preserve BC with possible child classes
22 | and public method usage until next major version -->
23 | <exclude-pattern>PhpAmqpLib/Wire/AMQPReader.php</exclude-pattern>
24 | <exclude-pattern>PhpAmqpLib/Wire/IO/SocketIO.php</exclude-pattern>
25 | <exclude-pattern>PhpAmqpLib/Wire/IO/StreamIO.php</exclude-pattern>
26 | <exclude-pattern>PhpAmqpLib/Wire/IO/AbstractIO.php</exclude-pattern>
27 | <exclude-pattern>PhpAmqpLib/Wire/AMQPWriter.php</exclude-pattern>
28 | <exclude-pattern>PhpAmqpLib/Connection/AMQPSocketConnection.php</exclude-pattern>
29 | <exclude-pattern>PhpAmqpLib/Connection/AMQPSSLConnection.php</exclude-pattern>
30 | <exclude-pattern>PhpAmqpLib/Connection/AMQPStreamConnection.php</exclude-pattern>
31 | <exclude-pattern>PhpAmqpLib/Connection/AMQPLazyConnection.php</exclude-pattern>
32 | <exclude-pattern>PhpAmqpLib/Connection/AMQPLazySocketConnection.php</exclude-pattern>
33 | <exclude-pattern>PhpAmqpLib/Connection/AMQPLazySSLConnection.php</exclude-pattern>
34 | <exclude-pattern>PhpAmqpLib/Connection/AbstractConnection.php</exclude-pattern>
35 | <exclude-pattern>PhpAmqpLib/Message/AMQPMessage.php</exclude-pattern>
36 | <exclude-pattern>PhpAmqpLib/Helper/Protocol/Wait091.php</exclude-pattern>
37 | <exclude-pattern>PhpAmqpLib/Helper/Protocol/MethodMap091.php</exclude-pattern>
38 | <exclude-pattern>PhpAmqpLib/Helper/Protocol/Wait080.php</exclude-pattern>
39 | <exclude-pattern>PhpAmqpLib/Helper/Protocol/MethodMap080.php</exclude-pattern>
40 | <exclude-pattern>PhpAmqpLib/Helper/DebugHelper.php</exclude-pattern>
41 | <exclude-pattern>PhpAmqpLib/Helper/MiscHelper.php</exclude-pattern>
42 | <exclude-pattern>PhpAmqpLib/Channel/AMQPChannel.php</exclude-pattern>
43 | <exclude-pattern>PhpAmqpLib/Channel/AbstractChannel.php</exclude-pattern>
44 | </rule>
45 |
46 | <file>PhpAmqpLib/</file>
47 | </ruleset>
48 |
--------------------------------------------------------------------------------
/test.env:
--------------------------------------------------------------------------------
1 | TEST_RABBITMQ_HOST=php-amqplib-rabbitmq
2 | TOXIPROXY_HOST=php-amqplib-toxiproxy
3 | TOXIPROXY_AMQP_PORT=5673
4 |
--------------------------------------------------------------------------------