From f468186ef4eefbbc11679d8bb0c84e9327409946 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Thu, 5 Oct 2023 08:10:20 +0200 Subject: [PATCH] Rebuild into async only with sync API --- benchmark/consumer.php | 8 +- benchmark/consumer_async.php | 46 - benchmark/producer.php | 2 + benchmark/producer_async.php | 60 - composer.json | 23 +- examples/worker-async.php | 91 - examples/worker.php | 66 + spec/generate.php | 1 + src/Bunny/AbstractClient.php | 515 ---- src/Bunny/Async/Client.php | 347 --- src/Bunny/Client.php | 278 -- src/Bunny/ClientMethods.php | 2581 ----------------- src/Bunny/Message.php | 82 - src/{Bunny => }/Channel.php | 189 +- src/{Bunny => }/ChannelMethods.php | 39 +- src/{Bunny => }/ChannelModeEnum.php | 0 src/{Bunny => }/ChannelStateEnum.php | 0 src/Channels.php | 39 + src/Client.php | 306 ++ src/{Bunny => }/ClientStateEnum.php | 0 src/Connection.php | 1945 +++++++++++++ src/{Bunny => }/Constants.php | 0 .../Exception/BufferUnderflowException.php | 2 + src/{Bunny => }/Exception/BunnyException.php | 0 .../Exception/ChannelException.php | 0 src/{Bunny => }/Exception/ClientException.php | 0 .../Exception/FrameEndInvalidException.php | 0 .../Exception/InvalidClassException.php | 0 .../Exception/InvalidMethodException.php | 0 .../Exception/ProtocolException.php | 0 src/Message.php | 51 + src/{Bunny => }/Protocol/AbstractFrame.php | 0 src/{Bunny => }/Protocol/Buffer.php | 0 src/{Bunny => }/Protocol/ContentBodyFrame.php | 0 .../Protocol/ContentHeaderFrame.php | 0 src/{Bunny => }/Protocol/HeartbeatFrame.php | 0 .../Protocol/MethodAccessRequestFrame.php | 0 .../Protocol/MethodAccessRequestOkFrame.php | 0 .../Protocol/MethodBasicAckFrame.php | 0 .../Protocol/MethodBasicCancelFrame.php | 0 .../Protocol/MethodBasicCancelOkFrame.php | 0 .../Protocol/MethodBasicConsumeFrame.php | 0 .../Protocol/MethodBasicConsumeOkFrame.php | 0 .../Protocol/MethodBasicDeliverFrame.php | 0 .../Protocol/MethodBasicGetEmptyFrame.php | 0 .../Protocol/MethodBasicGetFrame.php | 0 .../Protocol/MethodBasicGetOkFrame.php | 0 .../Protocol/MethodBasicNackFrame.php | 0 .../Protocol/MethodBasicPublishFrame.php | 0 .../Protocol/MethodBasicQosFrame.php | 0 .../Protocol/MethodBasicQosOkFrame.php | 0 .../Protocol/MethodBasicRecoverAsyncFrame.php | 0 .../Protocol/MethodBasicRecoverFrame.php | 0 .../Protocol/MethodBasicRecoverOkFrame.php | 0 .../Protocol/MethodBasicRejectFrame.php | 0 .../Protocol/MethodBasicReturnFrame.php | 0 .../Protocol/MethodChannelCloseFrame.php | 0 .../Protocol/MethodChannelCloseOkFrame.php | 0 .../Protocol/MethodChannelFlowFrame.php | 0 .../Protocol/MethodChannelFlowOkFrame.php | 0 .../Protocol/MethodChannelOpenFrame.php | 0 .../Protocol/MethodChannelOpenOkFrame.php | 0 .../Protocol/MethodConfirmSelectFrame.php | 0 .../Protocol/MethodConfirmSelectOkFrame.php | 0 .../Protocol/MethodConnectionBlockedFrame.php | 0 .../Protocol/MethodConnectionCloseFrame.php | 0 .../Protocol/MethodConnectionCloseOkFrame.php | 0 .../Protocol/MethodConnectionOpenFrame.php | 0 .../Protocol/MethodConnectionOpenOkFrame.php | 0 .../Protocol/MethodConnectionSecureFrame.php | 0 .../MethodConnectionSecureOkFrame.php | 0 .../Protocol/MethodConnectionStartFrame.php | 0 .../Protocol/MethodConnectionStartOkFrame.php | 0 .../Protocol/MethodConnectionTuneFrame.php | 0 .../Protocol/MethodConnectionTuneOkFrame.php | 0 .../MethodConnectionUnblockedFrame.php | 0 .../Protocol/MethodExchangeBindFrame.php | 0 .../Protocol/MethodExchangeBindOkFrame.php | 0 .../Protocol/MethodExchangeDeclareFrame.php | 0 .../Protocol/MethodExchangeDeclareOkFrame.php | 0 .../Protocol/MethodExchangeDeleteFrame.php | 0 .../Protocol/MethodExchangeDeleteOkFrame.php | 0 .../Protocol/MethodExchangeUnbindFrame.php | 0 .../Protocol/MethodExchangeUnbindOkFrame.php | 0 src/{Bunny => }/Protocol/MethodFrame.php | 0 .../Protocol/MethodQueueBindFrame.php | 0 .../Protocol/MethodQueueBindOkFrame.php | 0 .../Protocol/MethodQueueDeclareFrame.php | 0 .../Protocol/MethodQueueDeclareOkFrame.php | 0 .../Protocol/MethodQueueDeleteFrame.php | 0 .../Protocol/MethodQueueDeleteOkFrame.php | 0 .../Protocol/MethodQueuePurgeFrame.php | 0 .../Protocol/MethodQueuePurgeOkFrame.php | 0 .../Protocol/MethodQueueUnbindFrame.php | 0 .../Protocol/MethodQueueUnbindOkFrame.php | 0 .../Protocol/MethodTxCommitFrame.php | 0 .../Protocol/MethodTxCommitOkFrame.php | 0 .../Protocol/MethodTxRollbackFrame.php | 0 .../Protocol/MethodTxRollbackOkFrame.php | 0 .../Protocol/MethodTxSelectFrame.php | 0 .../Protocol/MethodTxSelectOkFrame.php | 0 src/{Bunny => }/Protocol/ProtocolReader.php | 2 +- .../Protocol/ProtocolReaderGenerated.php | 2 +- src/{Bunny => }/Protocol/ProtocolWriter.php | 2 +- .../Protocol/ProtocolWriterGenerated.php | 0 test/Bunny/AsyncClientTest.php | 355 --- test/Bunny/ChannelTest.php | 75 +- test/Bunny/ClientTest.php | 136 +- test/Bunny/SSLTest.php | 32 - test/Library/AsynchronousClientHelper.php | 36 - test/Library/SynchronousClientHelper.php | 45 - test/scripts/bunny-consumer.php | 10 +- tutorial/1-hello-world/receive-async.php | 31 - tutorial/1-hello-world/receive.php | 15 +- tutorial/1-hello-world/send-async.php | 32 - tutorial/1-hello-world/send.php | 39 +- tutorial/2-work-queues/new_task-async.php | 40 - tutorial/2-work-queues/new_task.php | 4 +- tutorial/2-work-queues/worker-async.php | 35 - tutorial/2-work-queues/worker.php | 6 +- .../3-publish-subscribe/emit_log-async.php | 37 - tutorial/3-publish-subscribe/emit_log.php | 4 +- .../receive_logs-async.php | 36 - tutorial/3-publish-subscribe/receive_logs.php | 6 +- tutorial/4-routing/emit_log-async.php | 38 - tutorial/4-routing/emit_log.php | 4 +- tutorial/4-routing/receive_logs-async.php | 48 - tutorial/4-routing/receive_logs.php | 7 +- tutorial/5-topics/emit_log_topic-async.php | 38 - tutorial/5-topics/emit_log_topic.php | 4 +- .../5-topics/receive_logs_topic-async.php | 48 - tutorial/5-topics/receive_logs_topic.php | 7 +- tutorial/6-rpc/rpc_client-async.php | 65 - tutorial/6-rpc/rpc_client.php | 29 +- tutorial/6-rpc/rpc_server-async.php | 48 - tutorial/6-rpc/rpc_server.php | 10 +- 136 files changed, 2689 insertions(+), 5308 deletions(-) delete mode 100644 benchmark/consumer_async.php delete mode 100644 benchmark/producer_async.php delete mode 100644 examples/worker-async.php create mode 100644 examples/worker.php delete mode 100644 src/Bunny/AbstractClient.php delete mode 100644 src/Bunny/Async/Client.php delete mode 100644 src/Bunny/Client.php delete mode 100644 src/Bunny/ClientMethods.php delete mode 100644 src/Bunny/Message.php rename src/{Bunny => }/Channel.php (77%) rename src/{Bunny => }/ChannelMethods.php (84%) rename src/{Bunny => }/ChannelModeEnum.php (100%) rename src/{Bunny => }/ChannelStateEnum.php (100%) create mode 100644 src/Channels.php create mode 100644 src/Client.php rename src/{Bunny => }/ClientStateEnum.php (100%) create mode 100644 src/Connection.php rename src/{Bunny => }/Constants.php (100%) rename src/{Bunny => }/Exception/BufferUnderflowException.php (87%) rename src/{Bunny => }/Exception/BunnyException.php (100%) rename src/{Bunny => }/Exception/ChannelException.php (100%) rename src/{Bunny => }/Exception/ClientException.php (100%) rename src/{Bunny => }/Exception/FrameEndInvalidException.php (100%) rename src/{Bunny => }/Exception/InvalidClassException.php (100%) rename src/{Bunny => }/Exception/InvalidMethodException.php (100%) rename src/{Bunny => }/Exception/ProtocolException.php (100%) create mode 100644 src/Message.php rename src/{Bunny => }/Protocol/AbstractFrame.php (100%) rename src/{Bunny => }/Protocol/Buffer.php (100%) rename src/{Bunny => }/Protocol/ContentBodyFrame.php (100%) rename src/{Bunny => }/Protocol/ContentHeaderFrame.php (100%) rename src/{Bunny => }/Protocol/HeartbeatFrame.php (100%) rename src/{Bunny => }/Protocol/MethodAccessRequestFrame.php (100%) rename src/{Bunny => }/Protocol/MethodAccessRequestOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicAckFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicCancelFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicCancelOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicConsumeFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicConsumeOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicDeliverFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicGetEmptyFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicGetFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicGetOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicNackFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicPublishFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicQosFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicQosOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicRecoverAsyncFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicRecoverFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicRecoverOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicRejectFrame.php (100%) rename src/{Bunny => }/Protocol/MethodBasicReturnFrame.php (100%) rename src/{Bunny => }/Protocol/MethodChannelCloseFrame.php (100%) rename src/{Bunny => }/Protocol/MethodChannelCloseOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodChannelFlowFrame.php (100%) rename src/{Bunny => }/Protocol/MethodChannelFlowOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodChannelOpenFrame.php (100%) rename src/{Bunny => }/Protocol/MethodChannelOpenOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConfirmSelectFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConfirmSelectOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionBlockedFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionCloseFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionCloseOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionOpenFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionOpenOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionSecureFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionSecureOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionStartFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionStartOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionTuneFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionTuneOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodConnectionUnblockedFrame.php (100%) rename src/{Bunny => }/Protocol/MethodExchangeBindFrame.php (100%) rename src/{Bunny => }/Protocol/MethodExchangeBindOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodExchangeDeclareFrame.php (100%) rename src/{Bunny => }/Protocol/MethodExchangeDeclareOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodExchangeDeleteFrame.php (100%) rename src/{Bunny => }/Protocol/MethodExchangeDeleteOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodExchangeUnbindFrame.php (100%) rename src/{Bunny => }/Protocol/MethodExchangeUnbindOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodFrame.php (100%) rename src/{Bunny => }/Protocol/MethodQueueBindFrame.php (100%) rename src/{Bunny => }/Protocol/MethodQueueBindOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodQueueDeclareFrame.php (100%) rename src/{Bunny => }/Protocol/MethodQueueDeclareOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodQueueDeleteFrame.php (100%) rename src/{Bunny => }/Protocol/MethodQueueDeleteOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodQueuePurgeFrame.php (100%) rename src/{Bunny => }/Protocol/MethodQueuePurgeOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodQueueUnbindFrame.php (100%) rename src/{Bunny => }/Protocol/MethodQueueUnbindOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodTxCommitFrame.php (100%) rename src/{Bunny => }/Protocol/MethodTxCommitOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodTxRollbackFrame.php (100%) rename src/{Bunny => }/Protocol/MethodTxRollbackOkFrame.php (100%) rename src/{Bunny => }/Protocol/MethodTxSelectFrame.php (100%) rename src/{Bunny => }/Protocol/MethodTxSelectOkFrame.php (100%) rename src/{Bunny => }/Protocol/ProtocolReader.php (100%) rename src/{Bunny => }/Protocol/ProtocolReaderGenerated.php (100%) rename src/{Bunny => }/Protocol/ProtocolWriter.php (100%) rename src/{Bunny => }/Protocol/ProtocolWriterGenerated.php (100%) delete mode 100644 test/Bunny/AsyncClientTest.php delete mode 100644 test/Library/AsynchronousClientHelper.php delete mode 100644 tutorial/1-hello-world/receive-async.php delete mode 100644 tutorial/1-hello-world/send-async.php delete mode 100644 tutorial/2-work-queues/new_task-async.php delete mode 100644 tutorial/2-work-queues/worker-async.php delete mode 100644 tutorial/3-publish-subscribe/emit_log-async.php delete mode 100644 tutorial/3-publish-subscribe/receive_logs-async.php delete mode 100644 tutorial/4-routing/emit_log-async.php delete mode 100644 tutorial/4-routing/receive_logs-async.php delete mode 100644 tutorial/5-topics/emit_log_topic-async.php delete mode 100644 tutorial/5-topics/receive_logs_topic-async.php delete mode 100644 tutorial/6-rpc/rpc_client-async.php delete mode 100644 tutorial/6-rpc/rpc_server-async.php diff --git a/benchmark/consumer.php b/benchmark/consumer.php index b245f78..7d40df4 100644 --- a/benchmark/consumer.php +++ b/benchmark/consumer.php @@ -1,6 +1,8 @@ run(function (Message $msg, Channel $ch, Client $c) use (&$t, &$count) { +$ch->consume(function (Message $msg, Channel $ch, Client $c) use (&$t, &$count) { if ($t === null) { $t = microtime(true); } if ($msg->content === "quit") { printf("Pid: %s, Count: %s, Time: %.4f\n", getmypid(), $count, microtime(true) - $t); - $c->stop(); + $c->disconnect(); } else { ++$count; } }, "bench_queue", "", false, true); - -$c->disconnect(); diff --git a/benchmark/consumer_async.php b/benchmark/consumer_async.php deleted file mode 100644 index 470ae05..0000000 --- a/benchmark/consumer_async.php +++ /dev/null @@ -1,46 +0,0 @@ -connect()->then(function (Client $c) { - return $c->channel(); - -})->then(function (Channel $ch) { - return Promise\all([ - $ch, - $ch->queueDeclare("bench_queue"), - $ch->exchangeDeclare("bench_exchange"), - $ch->queueBind("bench_queue", "bench_exchange"), - ]); - -})->then(function ($r) { - /** @var Channel $ch */ - $ch = $r[0]; - - $t = null; - $count = 0; - - return $ch->consume(function (Message $msg, Channel $ch, Client $c) use (&$t, &$count) { - if ($t === null) { - $t = microtime(true); - } - - if ($msg->content === "quit") { - printf("Pid: %s, Count: %s, Time: %.4f\n", getmypid(), $count, microtime(true) - $t); - $c->disconnect()->then(function () use ($c){ - $c->stop(); - }); - - } else { - ++$count; - } - }, "bench_queue", "", false, true); -}); - -$c->run(); diff --git a/benchmark/producer.php b/benchmark/producer.php index 5322632..f020ca7 100644 --- a/benchmark/producer.php +++ b/benchmark/producer.php @@ -1,6 +1,8 @@ connect()->then(function (Client $c) { - return $c->channel(); - -})->then(function (Channel $ch) { - return Promise\all([ - $ch, - $ch->queueDeclare("bench_queue"), - $ch->exchangeDeclare("bench_exchange"), - $ch->queueBind("bench_queue", "bench_exchange"), - ]); - -})->then(function ($r) use ($body, &$time, &$max) { - /** @var Channel $ch */ - $ch = $r[0]; - - $promises = []; - - for ($i = 0; $i < $max; $i++) { - $promises[] = $ch->publish($body, [], "bench_exchange"); - } - - $promises[] = $ch->publish("quit", [], "bench_exchange"); - - return Promise\all($promises); - -})->then(function () use ($c) { - return $c->disconnect(); - -})->then(function () use ($c, &$time) { - echo microtime(true) - $time, "\n"; - $c->stop(); -}); - -$c->run(); diff --git a/composer.json b/composer.json index 7121f38..ac5f8be 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "bunny/bunny", "type": "library", - "description": "Performant pure-PHP AMQP (RabbitMQ) sync/async (ReactPHP) library", + "description": "Performant pure-PHP AMQP (RabbitMQ) non-blocking ReactPHP library", "keywords": [ "rabbitmq", "rabbit", @@ -25,22 +25,29 @@ } ], "require": { - "php": "^7.1 || ^8.0", - "react/event-loop": "^1.0 || ^0.5 || ^0.4", - "react/promise": "~2.2" + "php": "^8.1", + "react/event-loop": "^1.5", + "react/promise": "^3.1", + "react/stream": "^1.3", + "react/async": "^4.2", + "react/socket": "^1.15", + "react/promise-timer": "^1.10", + "evenement/evenement": "^3", + "react/child-process": "^0.6.5", + "react/promise-stream": "^1.7" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.5 || ^7.5.20", - "symfony/process": "^6.1 || ^4.4", - "phpstan/phpstan": "^1.10" + "phpunit/phpunit": "^9.6", + "phpstan/phpstan": "^1.10", + "wyrihaximus/react-phpunit-run-tests-in-fiber": "^1" }, "suggest": { "ext-pcntl": "For using synchronous AMQP/RabbitMQ client" }, "autoload": { "psr-4": { - "Bunny\\": "src/Bunny/" + "Bunny\\": "src/" } }, "autoload-dev": { diff --git a/examples/worker-async.php b/examples/worker-async.php deleted file mode 100644 index 3742e0b..0000000 --- a/examples/worker-async.php +++ /dev/null @@ -1,91 +0,0 @@ - "rabbitmq.example.com", - "port" => 5672, - "vhost" => "/", - "user" => "appuser", - "password" => "apppass", -]; - -$client = new Client($loop, $clientConfig); -$client->connect()->then(function (Client $client) { - return $client->channel(); -}, function($reason) { - $reasonMsg = ""; - if (is_string($reason)) { - $reasonMsg = $reason; - } else if ($reason instanceof Throwable) { - $reasonMsg = $reason->getMessage(); - } - print "Rejected: {$reasonMsg}\n"; -})->then(function (Channel $channel) { - return $channel->qos(0, 1)->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) { - return $channel->queueDeclare('test', false, true, false, false)->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) use (&$channelRef) { - $channelRef = $channel; - echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; - $channel->consume( - function (Message $message, Channel $channel, Client $client) { - echo " [x] Received ", $message->content, "\n"; - - // Do some work - we generate password hashes with a high cost - // sleep() gets interrupted by Ctrl+C so it's not very good for demos - // Performing multiple work units demonstrates that nothing is skipped - for ($i = 0; $i < 3; $i++) { - print "WU {$i}\n"; - password_hash(random_bytes(255), PASSWORD_BCRYPT, ["cost" => 15]); - } - echo " [x] Done ", $message->content, "\n"; - - $channel->ack($message)->then(function() use ($message) { - print "ACK :: {$message->content}\n"; - }, function($reason) { - $reasonMsg = ""; - if (is_string($reason)) { - $reasonMsg = $reason; - } else if ($reason instanceof Throwable) { - $reasonMsg = $reason->getMessage(); - } - print "ACK FAILED! - {$reasonMsg}\n"; - })->done(); - }, - 'test' - )->then(function (MethodBasicConsumeOkFrame $response) use (&$consumerTag) { - $consumerTag = $response->consumerTag; - })->done(); - -})->done(); - -// Capture signals - SIGINT = Ctrl+C; SIGTERM = `kill` -$loop->addSignal(SIGINT, function (int $signal) use (&$channelRef, &$consumerTag) { - print "Consumer cancelled\n"; - $channelRef->cancel($consumerTag)->done(function() { - exit(); - }); -}); -$loop->addSignal(SIGTERM, function (int $signal) use (&$channelRef, &$consumerTag) { - print "Consumer cancelled\n"; - $channelRef->cancel($consumerTag)->done(function() { - exit(); - }); -}); - -$loop->run(); diff --git a/examples/worker.php b/examples/worker.php new file mode 100644 index 0000000..a68d9d8 --- /dev/null +++ b/examples/worker.php @@ -0,0 +1,66 @@ +cancel($consumerTag); + + Loop::addTimer(3, static function () { + Loop::stop(); + }); +}); +Loop::addSignal(SIGTERM, function (int $signal) use (&$channel, &$consumerTag) { + print 'Consumer cancelled\n'; + $channel->cancel($consumerTag); + + Loop::addTimer(3, static function () { + Loop::stop(); + }); +}); + +$clientConfig = [ + "host" => "rabbitmq.example.com", + "port" => 5672, + "vhost" => "/", + "user" => "appuser", + "password" => "apppass", +]; + +$client = new Client($clientConfig); +$channel = $client->channel(); +$channel->qos(0, 13); +$channel->queueDeclare('hello', false, false, false, false); +$channelRef = $channel; +echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; +$response = $channel->consume( + async(function (Message $message, Channel $channel) { + echo ' [x] Received ', $message->content, "\n"; + + // Do some work - we generate password hashes with a high cost + // sleep() gets interrupted by Ctrl+C so it's not very good for demos + // Performing multiple work units demonstrates that nothing is skipped + for ($i = 0; $i < 3; $i++) { + print 'WU {$i}\n'; + password_hash(random_bytes(255), PASSWORD_BCRYPT, ['cost' => 15]); + } + echo ' [x] Done ', $message->content, "\n"; + + $channel->ack($message); + }), + 'hello', + noAck: true, +); +$consumerTag = $response->consumerTag; diff --git a/spec/generate.php b/spec/generate.php index d8c98a4..81fca69 100644 --- a/spec/generate.php +++ b/spec/generate.php @@ -1,6 +1,7 @@  "exchangeDelete". - * Methods from "basic" class are not prefixed with "basic" - e.g. "basic.publish" is just "publish". - * - * @author Jakub Kulhan - */ -abstract class AbstractClient -{ - - use ClientMethods; - - /** @var array */ - protected $options; - - /** @var resource|null */ - protected $stream; - - /** @var int */ - protected $state = ClientStateEnum::NOT_CONNECTED; - - /** @var Buffer */ - protected $readBuffer; - - /** @var Buffer */ - protected $writeBuffer; - - /** @var ProtocolReader */ - protected $reader; - - /** @var ProtocolWriter */ - protected $writer; - - /** @var AbstractFrame[] */ - protected $queue; - - /** @var Channel[] */ - protected $channels = []; - - /** @var Promise\PromiseInterface|null */ - protected $disconnectPromise; - - /** @var int */ - protected $frameMax = 0xFFFF; - - /** @var int */ - protected $nextChannelId = 1; - - /** @var int */ - protected $channelMax = 0xFFFF; - - /** @var float microtime of last read*/ - protected $lastRead = 0.0; - - /** @var float microtime of last write */ - protected $lastWrite = 0.0; - - /** - * Constructor. - * - * @param array $options - */ - public function __construct(array $options = []) - { - if (!isset($options["host"])) { - $options["host"] = "127.0.0.1"; - } - - if (!isset($options["port"])) { - $options["port"] = 5672; - } - - if (!isset($options["vhost"])) { - if (isset($options["virtual_host"])) { - $options["vhost"] = $options["virtual_host"]; - unset($options["virtual_host"]); - } elseif (isset($options["path"])) { - $options["vhost"] = $options["path"]; - unset($options["path"]); - } else { - $options["vhost"] = "/"; - } - } - - if (!isset($options["user"])) { - if (isset($options["username"])) { - $options["user"] = $options["username"]; - unset($options["username"]); - } else { - $options["user"] = "guest"; - } - } - - if (!isset($options["password"])) { - if (isset($options["pass"])) { - $options["password"] = $options["pass"]; - unset($options["pass"]); - } else { - $options["password"] = "guest"; - } - } - - if (!isset($options["timeout"])) { - $options["timeout"] = 1; - } - - if (!isset($options["heartbeat"])) { - $options["heartbeat"] = 60.0; - } elseif ($options["heartbeat"] >= 2**15) { - throw new InvalidArgumentException("Heartbeat too high: the value is a signed int16."); - } - - if (is_callable($options['heartbeat_callback'] ?? null)) { - $this->options['heartbeat_callback'] = $options['heartbeat_callback']; - } - - $this->options = $options; - - $this->init(); - } - - /** - * Initializes instance. - */ - protected function init() - { - $this->state = ClientStateEnum::NOT_CONNECTED; - $this->readBuffer = new Buffer(); - $this->writeBuffer = new Buffer(); - $this->reader = new ProtocolReader(); - $this->writer = new ProtocolWriter(); - $this->queue = []; - } - - /** - * Returns AMQP protocol reader. - * - * @return ProtocolReader - */ - protected function getReader() - { - return $this->reader; - } - - /** - * Returns AMQP protocol writer. - * - * @return ProtocolWriter - */ - protected function getWriter() - { - return $this->writer; - } - - /** - * Returns read buffer. - * - * @return Buffer - */ - protected function getReadBuffer() - { - return $this->readBuffer; - } - - /** - * Returns write buffer. - * - * @return Buffer - */ - protected function getWriteBuffer() - { - return $this->writeBuffer; - } - - /** - * Enqueues given frame for later processing. - * - * @param AbstractFrame $frame - */ - protected function enqueue(AbstractFrame $frame) - { - $this->queue[] = $frame; - } - - /** - * Creates stream according to options passed in constructor. - * - * @return resource - */ - protected function getStream() - { - if ($this->stream === null) { - $streamScheme = 'tcp'; - - $context = stream_context_create(); - if (isset($this->options['ssl']) && is_array($this->options['ssl'])) { - if (!stream_context_set_option($context, ['ssl' => $this->options['ssl']])) { - throw new ClientException("Failed to set SSL-options."); - } - $streamScheme = 'ssl'; - } - - // see https://github.com/nrk/predis/blob/v1.0/src/Connection/StreamConnection.php - $uri = $streamScheme."://{$this->options["host"]}:{$this->options["port"]}"; - $flags = STREAM_CLIENT_CONNECT; - - if (isset($this->options["async_connect"]) && !!$this->options["async_connect"]) { - $flags |= STREAM_CLIENT_ASYNC_CONNECT; - } - - if (isset($this->options["persistent"]) && !!$this->options["persistent"]) { - $flags |= STREAM_CLIENT_PERSISTENT; - - if (!isset($this->options["path"])) { - throw new ClientException("If you need persistent connection, you have to specify 'path' option."); - } - - $uri .= (strpos($this->options["path"], "/") === 0) ? $this->options["path"] : "/" . $this->options["path"]; - } - - stream_context_set_option( - $context, [ - "socket" => [ - "tcp_nodelay" => true - ] - ]); - - $this->stream = @stream_socket_client($uri, $errno, $errstr, (float)$this->options["timeout"], $flags, $context); - - if (!$this->stream) { - throw new ClientException( - "Could not connect to {$this->options["host"]}:{$this->options["port"]}: {$errstr}.", - $errno - ); - } - - if (isset($this->options["read_write_timeout"])) { - $readWriteTimeout = (float)$this->options["read_write_timeout"]; - if ($readWriteTimeout < 0) { - $readWriteTimeout = -1; - } - $readWriteTimeoutSeconds = floor($readWriteTimeout); - $readWriteTimeoutMicroseconds = ($readWriteTimeout - $readWriteTimeoutSeconds) * 10e6; - stream_set_timeout($this->stream, $readWriteTimeoutSeconds, $readWriteTimeoutMicroseconds); - } - - if (isset($this->options["tcp_nodelay"]) && function_exists("socket_import_stream")) { - $socket = socket_import_stream($this->stream); - socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int)$this->options["tcp_nodelay"]); - } - - if ($this->options["async"]) { - stream_set_blocking($this->stream, 0); - } - } - - return $this->stream; - } - - /** - * Closes stream. - */ - protected function closeStream() - { - @fclose($this->stream); - $this->stream = null; - } - - /** - * Reads data from stream into {@link readBuffer}. - */ - protected function read() - { - $s = @fread($this->stream, $this->frameMax); - - if ($s === false) { - $info = stream_get_meta_data($this->stream); - - if (isset($info["timed_out"]) && $info["timed_out"]) { - $this->disconnect(Constants::STATUS_RESOURCE_ERROR, "Connection closed by server unexpectedly"); - throw new ClientException("Timeout reached while reading from stream."); - } - } - - if (@feof($this->stream)) { - $this->disconnect(Constants::STATUS_RESOURCE_ERROR, "Connection closed by server unexpectedly"); - throw new ClientException("Broken pipe or closed connection."); - } - - $this->readBuffer->append($s); - $this->lastRead = microtime(true); - } - - /** - * Writes data from {@link writeBuffer} to stream. - */ - protected function write() - { - if (($written = @fwrite($this->getStream(), $this->writeBuffer->read($this->writeBuffer->getLength()))) === false) { - throw new ClientException("Could not write data to socket."); - } - - if ($written === 0) { - throw new ClientException("Broken pipe or closed connection."); - } - - fflush($this->getStream()); // flush internal PHP buffers - - $this->writeBuffer->discard($written); - $this->lastWrite = microtime(true); - } - - /** - * Responds to authentication challenge - * - * @param MethodConnectionStartFrame $start - * @return boolean|Promise\PromiseInterface - */ - protected function authResponse(MethodConnectionStartFrame $start) - { - if (strpos($start->mechanisms, "AMQPLAIN") === false) { - throw new ClientException("Server does not support AMQPLAIN mechanism (supported: {$start->mechanisms})."); - } - - $responseBuffer = new Buffer(); - $this->writer->appendTable([ - "LOGIN" => $this->options["user"], - "PASSWORD" => $this->options["password"], - ], $responseBuffer); - $responseBuffer->discard(4); - - return $this->connectionStartOk([], "AMQPLAIN", $responseBuffer->read($responseBuffer->getLength()), "en_US"); - } - - /** - * Disconnects the client. - * - * Always returns a promise (even sync client) - * - * @param int $replyCode - * @param string $replyText - * @return Promise\PromiseInterface - */ - abstract public function disconnect($replyCode = 0, $replyText = ""); - - /** - * Returns true if client is connected to server. - * - * @return boolean - */ - public function isConnected() - { - return $this->state !== ClientStateEnum::NOT_CONNECTED && $this->state !== ClientStateEnum::ERROR; - } - - /** - * Returns current client state. - * - * @return int - */ - public function getState() - { - return $this->state; - } - - /** - * Creates and opens new channel. - * - * Channel gets first available channel id. - * - * @return Channel|Promise\PromiseInterface - */ - public function channel() - { - // since we not found any free channel, then make one - $channelId = $this->findChannelId(); - - $this->channels[$channelId] = new Channel($this, $channelId); - $response = $this->channelOpen($channelId); - - if ($response instanceof MethodChannelOpenOkFrame) { - return $this->channels[$channelId]; - - } elseif ($response instanceof Promise\PromiseInterface) { - return $response->then(function () use ($channelId) { - return $this->channels[$channelId]; - }); - - } else { - $this->state = ClientStateEnum::ERROR; - - throw new ClientException( - "channel.open unexpected response of type " . gettype($response) . - (is_object($response) ? "(" . get_class($response) . ")" : "") . - "." - ); - } - } - - /** - * Removes channel. - * - * @param int $channelId - * @return void - */ - public function removeChannel($channelId) - { - unset($this->channels[$channelId]); - } - - /** - * Callback after connection-level frame has been received. - * - * @param AbstractFrame $frame - */ - public function onFrameReceived(AbstractFrame $frame) - { - if ($frame instanceof MethodFrame) { - if ($frame instanceof MethodConnectionCloseFrame) { - $this->disconnect(Constants::STATUS_CONNECTION_FORCED, "Connection closed by server: ({$frame->replyCode}) " . $frame->replyText); - throw new ClientException("Connection closed by server: " . $frame->replyText, $frame->replyCode); - } else { - throw new ClientException("Unhandled method frame " . get_class($frame) . "."); - } - - } elseif ($frame instanceof ContentHeaderFrame) { - $this->disconnect(Constants::STATUS_UNEXPECTED_FRAME, "Got header frame on connection channel (#0)."); - - } elseif ($frame instanceof ContentBodyFrame) { - $this->disconnect(Constants::STATUS_UNEXPECTED_FRAME, "Got body frame on connection channel (#0)."); - - } elseif ($frame instanceof HeartbeatFrame) { - $this->lastRead = microtime(true); - - } else { - throw new ClientException("Unhandled frame " . get_class($frame) . "."); - } - } - - /** - * @return int - */ - protected function getFrameMax() - { - return $this->frameMax; - } - - /** - * Wait for messages on connection and process them. Will process messages for at most $maxSeconds. - * - * @param float $maxSeconds - * @return void - */ - abstract public function run($maxSeconds = null); - - - /** - * @return int - */ - protected function findChannelId() - { - // first check in range [next, max] ... - for ( - $channelId = $this->nextChannelId; - $channelId <= $this->channelMax; - ++$channelId - ) { - if (!isset($this->channels[$channelId])) { - $this->nextChannelId = $channelId + 1; - - return $channelId; - } - } - - // then check in range [min, next) ... - for ( - $channelId = 1; - $channelId < $this->nextChannelId; - ++$channelId - ) { - if (!isset($this->channels[$channelId])) { - $this->nextChannelId = $channelId + 1; - - return $channelId; - } - } - - throw new ClientException("No available channels"); - } - -} diff --git a/src/Bunny/Async/Client.php b/src/Bunny/Async/Client.php deleted file mode 100644 index 41ac1e0..0000000 --- a/src/Bunny/Async/Client.php +++ /dev/null @@ -1,347 +0,0 @@ - "exchangeDelete". - * Methods from "basic" class are not prefixed with "basic" - e.g. "basic.publish" is just "publish". - * - * Usage: - * - * $c = new Bunny\Async\Client([ - * "host" => "127.0.0.1", - * "port" => 5672, - * "vhost" => "/", - * "user" => "guest", - * "password" => "guest", - * ]); - * - * // $c->connect() returns React\Promise\PromiseInterface - * - * $c->connect()->then(function(Bunny\Async\Client $c) { - * // work with connected client - * - * }, function ($e) { - * // an exception occurred - * }); - * - * @author Jakub Kulhan - */ -class Client extends AbstractClient -{ - - /** @var LoopInterface */ - protected $eventLoop; - - /** @var Promise\PromiseInterface|null */ - protected $flushWriteBufferPromise; - - /** @var callable[] */ - protected $awaitCallbacks; - - /** @var Timer */ - protected $stopTimer; - - /** @var Timer */ - protected $heartbeatTimer; - - /** - * Constructor. - * - * @param LoopInterface $eventLoop - * @param array $options see {@link AbstractClient} for available options - */ - public function __construct(LoopInterface $eventLoop = null, array $options = []) - { - $options["async"] = true; - parent::__construct($options); - - if ($eventLoop === null) { - $eventLoop = Factory::create(); - } - - $this->eventLoop = $eventLoop; - } - - /** - * Destructor. - * - * Clean shutdown = disconnect if connected. - */ - public function __destruct() - { - if ($this->isConnected()) { - $this->disconnect(); - } - } - - /** - * Initializes instance. - */ - protected function init() - { - parent::init(); - $this->flushWriteBufferPromise = null; - $this->awaitCallbacks = []; - $this->disconnectPromise = null; - } - - /** - * Calls {@link eventLoop}'s run() method. Processes messages for at most $maxSeconds. - * - * @param float $maxSeconds - */ - public function run($maxSeconds = null) - { - if ($maxSeconds !== null) { - $this->stopTimer = $this->eventLoop->addTimer($maxSeconds, function () { - $this->stop(); - }); - } - - $this->eventLoop->run(); - } - - /** - * Calls {@link eventLoop}'s stop() method. - */ - public function stop() - { - if ($this->stopTimer) { - $this->eventLoop->cancelTimer($this->stopTimer); - $this->stopTimer = null; - } - - $this->eventLoop->stop(); - } - - /** - * Reads data from stream to read buffer. - */ - protected function feedReadBuffer() - { - throw new \LogicException("feedReadBuffer() in async client does not make sense."); - } - - /** - * Asynchronously sends buffered data over the wire. - * - * - Calls {@link eventLoops}'s addWriteStream() with client's stream. - * - Consecutive calls will return the same instance of promise. - * - * @return Promise\PromiseInterface - */ - protected function flushWriteBuffer() - { - if ($this->flushWriteBufferPromise) { - return $this->flushWriteBufferPromise; - - } else { - $deferred = new Promise\Deferred(); - - $this->eventLoop->addWriteStream($this->getStream(), function ($stream) use ($deferred) { - try { - $this->write(); - - if ($this->writeBuffer->isEmpty()) { - $this->eventLoop->removeWriteStream($stream); - $this->flushWriteBufferPromise = null; - $deferred->resolve(true); - } - - } catch (\Exception $e) { - $this->eventLoop->removeWriteStream($stream); - $this->flushWriteBufferPromise = null; - $deferred->reject($e); - } - }); - - return $this->flushWriteBufferPromise = $deferred->promise(); - } - } - - /** - * Connects to AMQP server. - * - * Calling connect() multiple times will result in error. - * - * @return Promise\PromiseInterface - */ - public function connect() - { - if ($this->state !== ClientStateEnum::NOT_CONNECTED) { - return Promise\reject(new ClientException("Client already connected/connecting.")); - } - - $this->state = ClientStateEnum::CONNECTING; - $this->writer->appendProtocolHeader($this->writeBuffer); - - try { - $this->eventLoop->addReadStream($this->getStream(), [$this, "onDataAvailable"]); - } catch (\Exception $e) { - return Promise\reject($e); - } - - return $this->flushWriteBuffer()->then(function () { - return $this->awaitConnectionStart(); - - })->then(function (MethodConnectionStartFrame $start) { - return $this->authResponse($start); - - })->then(function () { - return $this->awaitConnectionTune(); - - })->then(function (MethodConnectionTuneFrame $tune) { - $this->frameMax = $tune->frameMax; - if ($tune->channelMax > 0) { - $this->channelMax = $tune->channelMax; - } - return $this->connectionTuneOk($tune->channelMax, $tune->frameMax, $this->options["heartbeat"]); - - })->then(function () { - return $this->connectionOpen($this->options["vhost"]); - - })->then(function () { - $this->heartbeatTimer = $this->eventLoop->addTimer($this->options["heartbeat"], [$this, "onHeartbeat"]); - - $this->state = ClientStateEnum::CONNECTED; - return $this; - - }); - } - - /** - * Disconnects client from server. - * - * - Calling disconnect() if client is not connected will result in error. - * - Calling disconnect() multiple times will result in the same promise. - * - * @param int $replyCode - * @param string $replyText - * @return Promise\PromiseInterface - */ - public function disconnect($replyCode = 0, $replyText = "") - { - if ($this->state === ClientStateEnum::DISCONNECTING) { - return $this->disconnectPromise; - } - - if ($this->state !== ClientStateEnum::CONNECTED) { - return Promise\reject(new ClientException("Client is not connected.")); - } - - $this->state = ClientStateEnum::DISCONNECTING; - - $promises = []; - - if ($replyCode === 0) { - foreach ($this->channels as $channel) { - $promises[] = $channel->close($replyCode, $replyText); - } - } - else{ - foreach($this->channels as $channel){ - $this->removeChannel($channel->getChannelId()); - } - } - - if ($this->heartbeatTimer) { - $this->eventLoop->cancelTimer($this->heartbeatTimer); - $this->heartbeatTimer = null; - } - - return $this->disconnectPromise = Promise\all($promises)->then(function () use ($replyCode, $replyText) { - if (!empty($this->channels)) { - throw new \LogicException("All channels have to be closed by now."); - } - if($replyCode !== 0){ - return null; - } - return $this->connectionClose($replyCode, $replyText, 0, 0); - })->then(function () { - $this->eventLoop->removeReadStream($this->getStream()); - $this->closeStream(); - $this->init(); - return $this; - }); - } - - /** - * Adds callback to process incoming frames. - * - * Callback is passed instance of {@link \Bunny\Protocol|AbstractFrame}. If callback returns TRUE, frame is said to - * be handled and further handlers (other await callbacks, default handler) won't be called. - * - * @param callable $callback - */ - public function addAwaitCallback(callable $callback) - { - $this->awaitCallbacks[] = $callback; - } - - /** - * {@link eventLoop}'s read stream callback notifying client that data from server arrived. - */ - public function onDataAvailable() - { - $this->read(); - - while (($frame = $this->reader->consumeFrame($this->readBuffer)) !== null) { - foreach ($this->awaitCallbacks as $k => $callback) { - if ($callback($frame) === true) { - unset($this->awaitCallbacks[$k]); - continue 2; // CONTINUE WHILE LOOP - } - } - - if ($frame->channel === 0) { - $this->onFrameReceived($frame); - - } else { - if (!isset($this->channels[$frame->channel])) { - throw new ClientException( - "Received frame #{$frame->type} on closed channel #{$frame->channel}." - ); - } - - $this->channels[$frame->channel]->onFrameReceived($frame); - } - } - } - - /** - * Callback when heartbeat timer timed out. - */ - public function onHeartbeat() - { - $now = microtime(true); - $nextHeartbeat = ($this->lastWrite ?: $now) + $this->options["heartbeat"]; - - if ($now >= $nextHeartbeat) { - $this->writer->appendFrame(new HeartbeatFrame(), $this->writeBuffer); - $this->flushWriteBuffer()->done(function () { - $this->heartbeatTimer = $this->eventLoop->addTimer($this->options["heartbeat"], [$this, "onHeartbeat"]); - }); - - if (is_callable($this->options['heartbeat_callback'] ?? null)) { - $this->options['heartbeat_callback']->call($this); - } - } else { - $this->heartbeatTimer = $this->eventLoop->addTimer($nextHeartbeat - $now, [$this, "onHeartbeat"]); - } - } - -} diff --git a/src/Bunny/Client.php b/src/Bunny/Client.php deleted file mode 100644 index c059963..0000000 --- a/src/Bunny/Client.php +++ /dev/null @@ -1,278 +0,0 @@ - "exchangeDelete". - * Methods from "basic" class are not prefixed with "basic" - e.g. "basic.publish" is just "publish". - * - * Usage: - * - * $c = new Bunny\Client([ - * "host" => "127.0.0.1", - * "port" => 5672, - * "vhost" => "/", - * "user" => "guest", - * "password" => "guest", - * ]); - * - * $c->connect(); - * // work with connected client, e.g. $c->channel() - * - * @author Jakub Kulhan - */ -class Client extends AbstractClient -{ - - /** @var boolean */ - protected $running = true; - - /** - * Constructor. - * - * @param array $options - */ - public function __construct(array $options = []) - { - $options["async"] = false; - parent::__construct($options); - } - - /** - * Destructor. - * - * Clean shutdown = disconnect if connected. - */ - public function __destruct() - { - if ($this->isConnected()) { - $this->disconnect()->done(function () { - $this->stop(); - }); - - // has to re-check if connected, because disconnect() can set connection state immediately - if ($this->isConnected()) { - $this->run(); - } - } - } - - /** - * Reads data from stream to {@link readBuffer}. - * - * @return boolean - */ - protected function feedReadBuffer() - { - $this->read(); - return true; - } - - /** - * Writes all data from {@link writeBuffer} to stream. - * - * @return boolean - */ - protected function flushWriteBuffer() - { - while (!$this->writeBuffer->isEmpty()) { - $this->write(); - } - return true; - } - - /** - * Synchronously connects to AMQP server. - * - * @throws \Exception - * @return self - */ - public function connect() - { - if ($this->state !== ClientStateEnum::NOT_CONNECTED) { - throw new ClientException("Client already connected/connecting."); - } - - try { - $this->state = ClientStateEnum::CONNECTING; - - $this->writer->appendProtocolHeader($this->writeBuffer); - $this->flushWriteBuffer(); - $this->authResponse($this->awaitConnectionStart()); - $tune = $this->awaitConnectionTune(); - $this->connectionTuneOk($tune->channelMax, $tune->frameMax, $this->options["heartbeat"]); // FIXME: options heartbeat - $this->frameMax = $tune->frameMax; - if ($tune->channelMax > 0) { - $this->channelMax = $tune->channelMax; - } - $this->connectionOpen($this->options["vhost"]); - - $this->state = ClientStateEnum::CONNECTED; - - return $this; - - } catch (\Exception $e) { - $this->state = ClientStateEnum::ERROR; - throw $e; - } - } - - /** - * Disconnects from AMQP server. - * - * @param int $replyCode - * @param string $replyText - * @return Promise\PromiseInterface - */ - public function disconnect($replyCode = 0, $replyText = "") - { - if ($this->state === ClientStateEnum::DISCONNECTING) { - return $this->disconnectPromise; - } - - if ($this->state !== ClientStateEnum::CONNECTED) { - return Promise\reject(new ClientException("Client is not connected.")); - } - - $this->state = ClientStateEnum::DISCONNECTING; - - $promises = []; - - if ($replyCode === 0) { - foreach ($this->channels as $channel) { - $promises[] = $channel->close(); - } - } - - return $this->disconnectPromise = Promise\all($promises)->then(function () use ($replyCode, $replyText) { - if (!empty($this->channels)) { - throw new \LogicException("All channels have to be closed by now."); - } - - $this->connectionClose($replyCode, $replyText, 0, 0); - $this->closeStream(); - $this->init(); - return $this; - }); - } - - /** - * Runs it's own event loop, processes frames as they arrive. Processes messages for at most $maxSeconds. - * - * @param float $maxSeconds - */ - public function run($maxSeconds = null) - { - if (!$this->isConnected()) { - throw new ClientException("Client has to be connected."); - } - - $this->running = true; - $startTime = microtime(true); - $stopTime = null; - if ($maxSeconds !== null) { - $stopTime = $startTime + $maxSeconds; - } - - do { - if (!empty($this->queue)) { - $frame = array_shift($this->queue); - - } else { - if (($frame = $this->reader->consumeFrame($this->readBuffer)) === null) { - $now = microtime(true); - $nextStreamSelectTimeout = ($this->lastWrite ?: $now) + $this->options["heartbeat"]; - if (!isset($nextHeartbeat)) { - $nextHeartbeat = $now + $this->options["heartbeat"]; - } - if ($stopTime !== null && $stopTime < $nextStreamSelectTimeout) { - $nextStreamSelectTimeout = $stopTime; - } - $tvSec = max(intval($nextStreamSelectTimeout - $now), 0); - $tvUsec = max(intval(($nextStreamSelectTimeout - $now - $tvSec) * 1000000), 0); - - $r = [$this->getStream()]; - $w = null; - $e = null; - - if (($n = @stream_select($r, $w, $e, $tvSec, $tvUsec)) === false) { - $lastError = error_get_last(); - - // Note: The word "Unable" within the stream_select error message was spelled "unable" in PHP - // versions < 8. - if ($lastError !== null && - preg_match("/^stream_select\\(\\): [Uu]nable to select \\[(\\d+)\\]:/", $lastError["message"], $m) && - intval($m[1]) === PCNTL_EINTR - ) { - // got interrupted by signal, dispatch signals & continue - pcntl_signal_dispatch(); - $n = 0; - - } else { - throw new ClientException(sprintf( - "stream_select() failed: %s", - $lastError ? $lastError["message"] : "Unknown error." - )); - } - } - - $now = microtime(true); - - if ($now >= $nextHeartbeat) { - $nextHeartbeat = $now + $this->options["heartbeat"]; - $this->writer->appendFrame(new HeartbeatFrame(), $this->writeBuffer); - $this->flushWriteBuffer(); - - if (is_callable($this->options['heartbeat_callback'] ?? null)) { - $this->options['heartbeat_callback']->call($this); - } - } - - if ($stopTime !== null && $now >= $stopTime) { - break; - } - - if ($n > 0) { - $this->feedReadBuffer(); - } - - continue; - } - } - - /** @var AbstractFrame $frame */ - - if ($frame->channel === 0) { - $this->onFrameReceived($frame); - - } else { - if (!isset($this->channels[$frame->channel])) { - throw new ClientException( - "Received frame #{$frame->type} on closed channel #{$frame->channel}." - ); - } - - $this->channels[$frame->channel]->onFrameReceived($frame); - } - - - } while ($this->running); - } - - /** - * Stops client's event loop. - */ - public function stop() - { - $this->running = false; - } - -} diff --git a/src/Bunny/ClientMethods.php b/src/Bunny/ClientMethods.php deleted file mode 100644 index 48dc8bb..0000000 --- a/src/Bunny/ClientMethods.php +++ /dev/null @@ -1,2581 +0,0 @@ - - */ -trait ClientMethods -{ - - /** @var array */ - private $cache = []; - - /** - * Returns AMQP protocol reader. - * - * @return Protocol\ProtocolReader - */ - abstract protected function getReader(); - - /** - * Returns read buffer. - * - * @return Buffer - */ - abstract protected function getReadBuffer(); - - /** - * Returns AMQP protocol writer. - * - * @return Protocol\ProtocolWriter - */ - abstract protected function getWriter(); - - /** - * Returns write buffer. - * - * @return Buffer - */ - abstract protected function getWriteBuffer(); - - /** - * Reads data from stream to read buffer. - */ - abstract protected function feedReadBuffer(); - - /** - * Writes all data from write buffer to stream. - * - * @return boolean|PromiseInterface - */ - abstract protected function flushWriteBuffer(); - - /** - * Enqueues given frame for later processing. - * - * @param Protocol\AbstractFrame $frame - */ - abstract protected function enqueue(Protocol\AbstractFrame $frame); - - /** - * Returns frame max size. - * - * @return int - */ - abstract protected function getFrameMax(); - - /** - * @param int $channel - * - * @return Protocol\ContentHeaderFrame|PromiseInterface - */ - public function awaitContentHeader($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\ContentHeaderFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\ContentHeaderFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - /** - * @param int $channel - * - * @return Protocol\ContentBodyFrame|PromiseInterface - */ - public function awaitContentBody($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\ContentBodyFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\ContentBodyFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - /** - * @return Protocol\MethodConnectionStartFrame|PromiseInterface - */ - public function awaitConnectionStart() - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred) { - if ($frame instanceof Protocol\MethodConnectionStartFrame) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodConnectionStartFrame) { - return $frame; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function connectionStartOk($clientProperties, $mechanism, $response, $locale = 'en_US') - { - $buffer = new Buffer(); - $buffer->appendUint16(10); - $buffer->appendUint16(11); - $this->getWriter()->appendTable($clientProperties, $buffer); - $buffer->appendUint8(strlen($mechanism)); $buffer->append($mechanism); - $buffer->appendUint32(strlen($response)); $buffer->append($response); - $buffer->appendUint8(strlen($locale)); $buffer->append($locale); - $frame = new Protocol\MethodFrame(10, 11); - $frame->channel = 0; - $frame->payloadSize = $buffer->getLength(); - $frame->payload = $buffer; - $this->getWriter()->appendFrame($frame, $this->getWriteBuffer()); - return $this->flushWriteBuffer(); - } - - /** - * @return Protocol\MethodConnectionSecureFrame|PromiseInterface - */ - public function awaitConnectionSecure() - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred) { - if ($frame instanceof Protocol\MethodConnectionSecureFrame) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodConnectionSecureFrame) { - return $frame; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function connectionSecureOk($response) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16(0); - $buffer->appendUint32(8 + strlen($response)); - $buffer->appendUint16(10); - $buffer->appendUint16(21); - $buffer->appendUint32(strlen($response)); $buffer->append($response); - $buffer->appendUint8(206); - return $this->flushWriteBuffer(); - } - - /** - * @return Protocol\MethodConnectionTuneFrame|PromiseInterface - */ - public function awaitConnectionTune() - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred) { - if ($frame instanceof Protocol\MethodConnectionTuneFrame) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodConnectionTuneFrame) { - return $frame; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function connectionTuneOk($channelMax = 0, $frameMax = 0, $heartbeat = 0) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16(0); - $buffer->appendUint32(12); - $buffer->appendUint16(10); - $buffer->appendUint16(31); - $buffer->appendInt16($channelMax); - $buffer->appendInt32($frameMax); - $buffer->appendInt16($heartbeat); - $buffer->appendUint8(206); - return $this->flushWriteBuffer(); - } - - public function connectionOpen($virtualHost = '/', $capabilities = '', $insist = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16(0); - $buffer->appendUint32(7 + strlen($virtualHost) + strlen($capabilities)); - $buffer->appendUint16(10); - $buffer->appendUint16(40); - $buffer->appendUint8(strlen($virtualHost)); $buffer->append($virtualHost); - $buffer->appendUint8(strlen($capabilities)); $buffer->append($capabilities); - $this->getWriter()->appendBits([$insist], $buffer); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitConnectionOpenOk(); - } - - /** - * @return Protocol\MethodConnectionOpenOkFrame|PromiseInterface - */ - public function awaitConnectionOpenOk() - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred) { - if ($frame instanceof Protocol\MethodConnectionOpenOkFrame) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodConnectionOpenOkFrame) { - return $frame; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function connectionClose($replyCode, $replyText, $closeClassId, $closeMethodId) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16(0); - $buffer->appendUint32(11 + strlen($replyText)); - $buffer->appendUint16(10); - $buffer->appendUint16(50); - $buffer->appendInt16($replyCode); - $buffer->appendUint8(strlen($replyText)); $buffer->append($replyText); - $buffer->appendInt16($closeClassId); - $buffer->appendInt16($closeMethodId); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitConnectionCloseOk(); - } - - /** - * @return Protocol\MethodConnectionCloseFrame|PromiseInterface - */ - public function awaitConnectionClose() - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred) { - if ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodConnectionCloseFrame) { - return $frame; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function connectionCloseOk() - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16(0); - $buffer->appendUint32(4); - $buffer->appendUint16(10); - $buffer->appendUint16(51); - $buffer->appendUint8(206); - return $this->flushWriteBuffer(); - } - - /** - * @return Protocol\MethodConnectionCloseOkFrame|PromiseInterface - */ - public function awaitConnectionCloseOk() - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred) { - if ($frame instanceof Protocol\MethodConnectionCloseOkFrame) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodConnectionCloseOkFrame) { - return $frame; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - /** - * @return Protocol\MethodConnectionBlockedFrame|PromiseInterface - */ - public function awaitConnectionBlocked() - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred) { - if ($frame instanceof Protocol\MethodConnectionBlockedFrame) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodConnectionBlockedFrame) { - return $frame; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - /** - * @return Protocol\MethodConnectionUnblockedFrame|PromiseInterface - */ - public function awaitConnectionUnblocked() - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred) { - if ($frame instanceof Protocol\MethodConnectionUnblockedFrame) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodConnectionUnblockedFrame) { - return $frame; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function channelOpen($channel, $outOfBand = '') - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(5 + strlen($outOfBand)); - $buffer->appendUint16(20); - $buffer->appendUint16(10); - $buffer->appendUint8(strlen($outOfBand)); $buffer->append($outOfBand); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitChannelOpenOk($channel); - } - - /** - * @param int $channel - * - * @return Protocol\MethodChannelOpenOkFrame|PromiseInterface - */ - public function awaitChannelOpenOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodChannelOpenOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodChannelOpenOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function channelFlow($channel, $active) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(5); - $buffer->appendUint16(20); - $buffer->appendUint16(20); - $this->getWriter()->appendBits([$active], $buffer); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitChannelFlowOk($channel); - } - - /** - * @param int $channel - * - * @return Protocol\MethodChannelFlowFrame|PromiseInterface - */ - public function awaitChannelFlow($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodChannelFlowFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodChannelFlowFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function channelFlowOk($channel, $active) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(5); - $buffer->appendUint16(20); - $buffer->appendUint16(21); - $this->getWriter()->appendBits([$active], $buffer); - $buffer->appendUint8(206); - return $this->flushWriteBuffer(); - } - - /** - * @param int $channel - * - * @return Protocol\MethodChannelFlowOkFrame|PromiseInterface - */ - public function awaitChannelFlowOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodChannelFlowOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodChannelFlowOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function channelClose($channel, $replyCode, $replyText, $closeClassId, $closeMethodId) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(11 + strlen($replyText)); - $buffer->appendUint16(20); - $buffer->appendUint16(40); - $buffer->appendInt16($replyCode); - $buffer->appendUint8(strlen($replyText)); $buffer->append($replyText); - $buffer->appendInt16($closeClassId); - $buffer->appendInt16($closeMethodId); - $buffer->appendUint8(206); - return $this->flushWriteBuffer(); - } - - /** - * @param int $channel - * - * @return Protocol\MethodChannelCloseFrame|PromiseInterface - */ - public function awaitChannelClose($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function channelCloseOk($channel) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(4); - $buffer->appendUint16(20); - $buffer->appendUint16(41); - $buffer->appendUint8(206); - return $this->flushWriteBuffer(); - } - - /** - * @param int $channel - * - * @return Protocol\MethodChannelCloseOkFrame|PromiseInterface - */ - public function awaitChannelCloseOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodChannelCloseOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodChannelCloseOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function accessRequest($channel, $realm = '/data', $exclusive = false, $passive = true, $active = true, $write = true, $read = true) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(6 + strlen($realm)); - $buffer->appendUint16(30); - $buffer->appendUint16(10); - $buffer->appendUint8(strlen($realm)); $buffer->append($realm); - $this->getWriter()->appendBits([$exclusive, $passive, $active, $write, $read], $buffer); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitAccessRequestOk($channel); - } - - /** - * @param int $channel - * - * @return Protocol\MethodAccessRequestOkFrame|PromiseInterface - */ - public function awaitAccessRequestOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodAccessRequestOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodAccessRequestOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function exchangeDeclare($channel, $exchange, $exchangeType = 'direct', $passive = false, $durable = false, $autoDelete = false, $internal = false, $nowait = false, $arguments = []) - { - $buffer = new Buffer(); - $buffer->appendUint16(40); - $buffer->appendUint16(10); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); - $buffer->appendUint8(strlen($exchangeType)); $buffer->append($exchangeType); - $this->getWriter()->appendBits([$passive, $durable, $autoDelete, $internal, $nowait], $buffer); - $this->getWriter()->appendTable($arguments, $buffer); - $frame = new Protocol\MethodFrame(40, 10); - $frame->channel = $channel; - $frame->payloadSize = $buffer->getLength(); - $frame->payload = $buffer; - $this->getWriter()->appendFrame($frame, $this->getWriteBuffer()); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitExchangeDeclareOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodExchangeDeclareOkFrame|PromiseInterface - */ - public function awaitExchangeDeclareOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodExchangeDeclareOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodExchangeDeclareOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function exchangeDelete($channel, $exchange, $ifUnused = false, $nowait = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(8 + strlen($exchange)); - $buffer->appendUint16(40); - $buffer->appendUint16(20); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); - $this->getWriter()->appendBits([$ifUnused, $nowait], $buffer); - $buffer->appendUint8(206); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitExchangeDeleteOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodExchangeDeleteOkFrame|PromiseInterface - */ - public function awaitExchangeDeleteOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodExchangeDeleteOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodExchangeDeleteOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function exchangeBind($channel, $destination, $source, $routingKey = '', $nowait = false, $arguments = []) - { - $buffer = new Buffer(); - $buffer->appendUint16(40); - $buffer->appendUint16(30); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($destination)); $buffer->append($destination); - $buffer->appendUint8(strlen($source)); $buffer->append($source); - $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); - $this->getWriter()->appendBits([$nowait], $buffer); - $this->getWriter()->appendTable($arguments, $buffer); - $frame = new Protocol\MethodFrame(40, 30); - $frame->channel = $channel; - $frame->payloadSize = $buffer->getLength(); - $frame->payload = $buffer; - $this->getWriter()->appendFrame($frame, $this->getWriteBuffer()); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitExchangeBindOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodExchangeBindOkFrame|PromiseInterface - */ - public function awaitExchangeBindOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodExchangeBindOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodExchangeBindOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function exchangeUnbind($channel, $destination, $source, $routingKey = '', $nowait = false, $arguments = []) - { - $buffer = new Buffer(); - $buffer->appendUint16(40); - $buffer->appendUint16(40); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($destination)); $buffer->append($destination); - $buffer->appendUint8(strlen($source)); $buffer->append($source); - $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); - $this->getWriter()->appendBits([$nowait], $buffer); - $this->getWriter()->appendTable($arguments, $buffer); - $frame = new Protocol\MethodFrame(40, 40); - $frame->channel = $channel; - $frame->payloadSize = $buffer->getLength(); - $frame->payload = $buffer; - $this->getWriter()->appendFrame($frame, $this->getWriteBuffer()); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitExchangeUnbindOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodExchangeUnbindOkFrame|PromiseInterface - */ - public function awaitExchangeUnbindOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodExchangeUnbindOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodExchangeUnbindOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function queueDeclare($channel, $queue = '', $passive = false, $durable = false, $exclusive = false, $autoDelete = false, $nowait = false, $arguments = []) - { - $buffer = new Buffer(); - $buffer->appendUint16(50); - $buffer->appendUint16(10); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($queue)); $buffer->append($queue); - $this->getWriter()->appendBits([$passive, $durable, $exclusive, $autoDelete, $nowait], $buffer); - $this->getWriter()->appendTable($arguments, $buffer); - $frame = new Protocol\MethodFrame(50, 10); - $frame->channel = $channel; - $frame->payloadSize = $buffer->getLength(); - $frame->payload = $buffer; - $this->getWriter()->appendFrame($frame, $this->getWriteBuffer()); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitQueueDeclareOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodQueueDeclareOkFrame|PromiseInterface - */ - public function awaitQueueDeclareOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodQueueDeclareOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodQueueDeclareOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function queueBind($channel, $queue, $exchange, $routingKey = '', $nowait = false, $arguments = []) - { - $buffer = new Buffer(); - $buffer->appendUint16(50); - $buffer->appendUint16(20); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($queue)); $buffer->append($queue); - $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); - $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); - $this->getWriter()->appendBits([$nowait], $buffer); - $this->getWriter()->appendTable($arguments, $buffer); - $frame = new Protocol\MethodFrame(50, 20); - $frame->channel = $channel; - $frame->payloadSize = $buffer->getLength(); - $frame->payload = $buffer; - $this->getWriter()->appendFrame($frame, $this->getWriteBuffer()); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitQueueBindOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodQueueBindOkFrame|PromiseInterface - */ - public function awaitQueueBindOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodQueueBindOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodQueueBindOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function queuePurge($channel, $queue = '', $nowait = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(8 + strlen($queue)); - $buffer->appendUint16(50); - $buffer->appendUint16(30); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($queue)); $buffer->append($queue); - $this->getWriter()->appendBits([$nowait], $buffer); - $buffer->appendUint8(206); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitQueuePurgeOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodQueuePurgeOkFrame|PromiseInterface - */ - public function awaitQueuePurgeOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodQueuePurgeOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodQueuePurgeOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function queueDelete($channel, $queue = '', $ifUnused = false, $ifEmpty = false, $nowait = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(8 + strlen($queue)); - $buffer->appendUint16(50); - $buffer->appendUint16(40); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($queue)); $buffer->append($queue); - $this->getWriter()->appendBits([$ifUnused, $ifEmpty, $nowait], $buffer); - $buffer->appendUint8(206); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitQueueDeleteOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodQueueDeleteOkFrame|PromiseInterface - */ - public function awaitQueueDeleteOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodQueueDeleteOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodQueueDeleteOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function queueUnbind($channel, $queue, $exchange, $routingKey = '', $arguments = []) - { - $buffer = new Buffer(); - $buffer->appendUint16(50); - $buffer->appendUint16(50); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($queue)); $buffer->append($queue); - $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); - $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); - $this->getWriter()->appendTable($arguments, $buffer); - $frame = new Protocol\MethodFrame(50, 50); - $frame->channel = $channel; - $frame->payloadSize = $buffer->getLength(); - $frame->payload = $buffer; - $this->getWriter()->appendFrame($frame, $this->getWriteBuffer()); - $this->flushWriteBuffer(); - return $this->awaitQueueUnbindOk($channel); - } - - /** - * @param int $channel - * - * @return Protocol\MethodQueueUnbindOkFrame|PromiseInterface - */ - public function awaitQueueUnbindOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodQueueUnbindOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodQueueUnbindOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function qos($channel, $prefetchSize = 0, $prefetchCount = 0, $global = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(11); - $buffer->appendUint16(60); - $buffer->appendUint16(10); - $buffer->appendInt32($prefetchSize); - $buffer->appendInt16($prefetchCount); - $this->getWriter()->appendBits([$global], $buffer); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitQosOk($channel); - } - - /** - * @param int $channel - * - * @return Protocol\MethodBasicQosOkFrame|PromiseInterface - */ - public function awaitQosOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodBasicQosOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodBasicQosOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function consume($channel, $queue = '', $consumerTag = '', $noLocal = false, $noAck = false, $exclusive = false, $nowait = false, $arguments = []) - { - $buffer = new Buffer(); - $buffer->appendUint16(60); - $buffer->appendUint16(20); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($queue)); $buffer->append($queue); - $buffer->appendUint8(strlen($consumerTag)); $buffer->append($consumerTag); - $this->getWriter()->appendBits([$noLocal, $noAck, $exclusive, $nowait], $buffer); - $this->getWriter()->appendTable($arguments, $buffer); - $frame = new Protocol\MethodFrame(60, 20); - $frame->channel = $channel; - $frame->payloadSize = $buffer->getLength(); - $frame->payload = $buffer; - $this->getWriter()->appendFrame($frame, $this->getWriteBuffer()); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitConsumeOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodBasicConsumeOkFrame|PromiseInterface - */ - public function awaitConsumeOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodBasicConsumeOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodBasicConsumeOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function cancel($channel, $consumerTag, $nowait = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(6 + strlen($consumerTag)); - $buffer->appendUint16(60); - $buffer->appendUint16(30); - $buffer->appendUint8(strlen($consumerTag)); $buffer->append($consumerTag); - $this->getWriter()->appendBits([$nowait], $buffer); - $buffer->appendUint8(206); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitCancelOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodBasicCancelOkFrame|PromiseInterface - */ - public function awaitCancelOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodBasicCancelOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodBasicCancelOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function publish($channel, $body, array $headers = [], $exchange = '', $routingKey = '', $mandatory = false, $immediate = false) - { - $buffer = $this->getWriteBuffer(); - $ck = serialize([$channel, $headers, $exchange, $routingKey, $mandatory, $immediate]); - $c = isset($this->cache[$ck]) ? $this->cache[$ck] : null; - $flags = 0; $off0 = 0; $len0 = 0; $off1 = 0; $len1 = 0; $contentTypeLength = null; $contentType = null; $contentEncodingLength = null; $contentEncoding = null; $headersBuffer = null; $deliveryMode = null; $priority = null; $correlationIdLength = null; $correlationId = null; $replyToLength = null; $replyTo = null; $expirationLength = null; $expiration = null; $messageIdLength = null; $messageId = null; $timestamp = null; $typeLength = null; $type = null; $userIdLength = null; $userId = null; $appIdLength = null; $appId = null; $clusterIdLength = null; $clusterId = null; - if ($c) { $buffer->append($c[0]); } - else { - $off0 = $buffer->getLength(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(9 + strlen($exchange) + strlen($routingKey)); - $buffer->appendUint16(60); - $buffer->appendUint16(40); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); - $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); - $this->getWriter()->appendBits([$mandatory, $immediate], $buffer); - $buffer->appendUint8(206); - $s = 14; - if (isset($headers['content-type'])) { - $flags |= 32768; - $contentType = $headers['content-type']; - $s += 1; - $s += $contentTypeLength = strlen($contentType); - unset($headers['content-type']); - } - if (isset($headers['content-encoding'])) { - $flags |= 16384; - $contentEncoding = $headers['content-encoding']; - $s += 1; - $s += $contentEncodingLength = strlen($contentEncoding); - unset($headers['content-encoding']); - } - if (isset($headers['delivery-mode'])) { - $flags |= 4096; - $deliveryMode = $headers['delivery-mode']; - $s += 1; - unset($headers['delivery-mode']); - } - if (isset($headers['priority'])) { - $flags |= 2048; - $priority = $headers['priority']; - $s += 1; - unset($headers['priority']); - } - if (isset($headers['correlation-id'])) { - $flags |= 1024; - $correlationId = $headers['correlation-id']; - $s += 1; - $s += $correlationIdLength = strlen($correlationId); - unset($headers['correlation-id']); - } - if (isset($headers['reply-to'])) { - $flags |= 512; - $replyTo = $headers['reply-to']; - $s += 1; - $s += $replyToLength = strlen($replyTo); - unset($headers['reply-to']); - } - if (isset($headers['expiration'])) { - $flags |= 256; - $expiration = $headers['expiration']; - $s += 1; - $s += $expirationLength = strlen($expiration); - unset($headers['expiration']); - } - if (isset($headers['message-id'])) { - $flags |= 128; - $messageId = $headers['message-id']; - $s += 1; - $s += $messageIdLength = strlen($messageId); - unset($headers['message-id']); - } - if (isset($headers['timestamp'])) { - $flags |= 64; - $timestamp = $headers['timestamp']; - $s += 8; - unset($headers['timestamp']); - } - if (isset($headers['type'])) { - $flags |= 32; - $type = $headers['type']; - $s += 1; - $s += $typeLength = strlen($type); - unset($headers['type']); - } - if (isset($headers['user-id'])) { - $flags |= 16; - $userId = $headers['user-id']; - $s += 1; - $s += $userIdLength = strlen($userId); - unset($headers['user-id']); - } - if (isset($headers['app-id'])) { - $flags |= 8; - $appId = $headers['app-id']; - $s += 1; - $s += $appIdLength = strlen($appId); - unset($headers['app-id']); - } - if (isset($headers['cluster-id'])) { - $flags |= 4; - $clusterId = $headers['cluster-id']; - $s += 1; - $s += $clusterIdLength = strlen($clusterId); - unset($headers['cluster-id']); - } - if (!empty($headers)) { - $flags |= 8192; - $this->getWriter()->appendTable($headers, $headersBuffer = new Buffer()); - $s += $headersBuffer->getLength(); - } - $buffer->appendUint8(2); - $buffer->appendUint16($channel); - $buffer->appendUint32($s); - $buffer->appendUint16(60); - $buffer->appendUint16(0); - $len0 = $buffer->getLength() - $off0; - } - $buffer->appendUint64(strlen($body)); - if ($c) { $buffer->append($c[1]); } - else { - $off1 = $buffer->getLength(); - $buffer->appendUint16($flags); - if ($flags & 32768) { - $buffer->appendUint8($contentTypeLength); $buffer->append($contentType); - } - if ($flags & 16384) { - $buffer->appendUint8($contentEncodingLength); $buffer->append($contentEncoding); - } - if ($flags & 8192) { - $buffer->append($headersBuffer); - } - if ($flags & 4096) { - $buffer->appendUint8($deliveryMode); - } - if ($flags & 2048) { - $buffer->appendUint8($priority); - } - if ($flags & 1024) { - $buffer->appendUint8($correlationIdLength); $buffer->append($correlationId); - } - if ($flags & 512) { - $buffer->appendUint8($replyToLength); $buffer->append($replyTo); - } - if ($flags & 256) { - $buffer->appendUint8($expirationLength); $buffer->append($expiration); - } - if ($flags & 128) { - $buffer->appendUint8($messageIdLength); $buffer->append($messageId); - } - if ($flags & 64) { - $this->getWriter()->appendTimestamp($timestamp, $buffer); - } - if ($flags & 32) { - $buffer->appendUint8($typeLength); $buffer->append($type); - } - if ($flags & 16) { - $buffer->appendUint8($userIdLength); $buffer->append($userId); - } - if ($flags & 8) { - $buffer->appendUint8($appIdLength); $buffer->append($appId); - } - if ($flags & 4) { - $buffer->appendUint8($clusterIdLength); $buffer->append($clusterId); - } - $buffer->appendUint8(206); - $len1 = $buffer->getLength() - $off1; - } - if (!$c) { - $this->cache[$ck] = [$buffer->read($len0, $off0), $buffer->read($len1, $off1)]; - if (count($this->cache) > 100) { reset($this->cache); unset($this->cache[key($this->cache)]); } - } - for ($payloadMax = $this->getFrameMax() - 8 /* frame preface and frame end */, $i = 0, $l = strlen($body); $i < $l; $i += $payloadMax) { - $payloadSize = $l - $i; if ($payloadSize > $payloadMax) { $payloadSize = $payloadMax; } - $buffer->appendUint8(3); - $buffer->appendUint16($channel); - $buffer->appendUint32($payloadSize); - $buffer->append(substr($body, $i, $payloadSize)); - $buffer->appendUint8(206); - } - return $this->flushWriteBuffer(); - } - - /** - * @param int $channel - * - * @return Protocol\MethodBasicReturnFrame|PromiseInterface - */ - public function awaitReturn($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodBasicReturnFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodBasicReturnFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - /** - * @param int $channel - * - * @return Protocol\MethodBasicDeliverFrame|PromiseInterface - */ - public function awaitDeliver($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodBasicDeliverFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodBasicDeliverFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function get($channel, $queue = '', $noAck = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(8 + strlen($queue)); - $buffer->appendUint16(60); - $buffer->appendUint16(70); - $buffer->appendInt16(0); - $buffer->appendUint8(strlen($queue)); $buffer->append($queue); - $this->getWriter()->appendBits([$noAck], $buffer); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitGetOk($channel); - } - - /** - * @param int $channel - * - * @return Protocol\MethodBasicGetOkFrame|Protocol\MethodBasicGetEmptyFrame|PromiseInterface - */ - public function awaitGetOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodBasicGetOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodBasicGetEmptyFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodBasicGetOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodBasicGetEmptyFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function ack($channel, $deliveryTag = 0, $multiple = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(13); - $buffer->appendUint16(60); - $buffer->appendUint16(80); - $buffer->appendInt64($deliveryTag); - $this->getWriter()->appendBits([$multiple], $buffer); - $buffer->appendUint8(206); - return $this->flushWriteBuffer(); - } - - /** - * @param int $channel - * - * @return Protocol\MethodBasicAckFrame|PromiseInterface - */ - public function awaitAck($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodBasicAckFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodBasicAckFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function reject($channel, $deliveryTag, $requeue = true) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(13); - $buffer->appendUint16(60); - $buffer->appendUint16(90); - $buffer->appendInt64($deliveryTag); - $this->getWriter()->appendBits([$requeue], $buffer); - $buffer->appendUint8(206); - return $this->flushWriteBuffer(); - } - - public function recoverAsync($channel, $requeue = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(5); - $buffer->appendUint16(60); - $buffer->appendUint16(100); - $this->getWriter()->appendBits([$requeue], $buffer); - $buffer->appendUint8(206); - return $this->flushWriteBuffer(); - } - - public function recover($channel, $requeue = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(5); - $buffer->appendUint16(60); - $buffer->appendUint16(110); - $this->getWriter()->appendBits([$requeue], $buffer); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitRecoverOk($channel); - } - - /** - * @param int $channel - * - * @return Protocol\MethodBasicRecoverOkFrame|PromiseInterface - */ - public function awaitRecoverOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodBasicRecoverOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodBasicRecoverOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function nack($channel, $deliveryTag = 0, $multiple = false, $requeue = true) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(13); - $buffer->appendUint16(60); - $buffer->appendUint16(120); - $buffer->appendInt64($deliveryTag); - $this->getWriter()->appendBits([$multiple, $requeue], $buffer); - $buffer->appendUint8(206); - return $this->flushWriteBuffer(); - } - - /** - * @param int $channel - * - * @return Protocol\MethodBasicNackFrame|PromiseInterface - */ - public function awaitNack($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodBasicNackFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodBasicNackFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function txSelect($channel) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(4); - $buffer->appendUint16(90); - $buffer->appendUint16(10); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitTxSelectOk($channel); - } - - /** - * @param int $channel - * - * @return Protocol\MethodTxSelectOkFrame|PromiseInterface - */ - public function awaitTxSelectOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodTxSelectOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodTxSelectOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function txCommit($channel) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(4); - $buffer->appendUint16(90); - $buffer->appendUint16(20); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitTxCommitOk($channel); - } - - /** - * @param int $channel - * - * @return Protocol\MethodTxCommitOkFrame|PromiseInterface - */ - public function awaitTxCommitOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodTxCommitOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodTxCommitOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function txRollback($channel) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(4); - $buffer->appendUint16(90); - $buffer->appendUint16(30); - $buffer->appendUint8(206); - $this->flushWriteBuffer(); - return $this->awaitTxRollbackOk($channel); - } - - /** - * @param int $channel - * - * @return Protocol\MethodTxRollbackOkFrame|PromiseInterface - */ - public function awaitTxRollbackOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodTxRollbackOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodTxRollbackOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - - public function confirmSelect($channel, $nowait = false) - { - $buffer = $this->getWriteBuffer(); - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(5); - $buffer->appendUint16(85); - $buffer->appendUint16(10); - $this->getWriter()->appendBits([$nowait], $buffer); - $buffer->appendUint8(206); - if ($nowait) { - return $this->flushWriteBuffer(); - } else { - $this->flushWriteBuffer(); - return $this->awaitConfirmSelectOk($channel); - } - } - - /** - * @param int $channel - * - * @return Protocol\MethodConfirmSelectOkFrame|PromiseInterface - */ - public function awaitConfirmSelectOk($channel) - { - if ($this instanceof Async\Client) { - $deferred = new Deferred(); - $this->addAwaitCallback(function ($frame) use ($deferred, $channel) { - if ($frame instanceof Protocol\MethodConfirmSelectOkFrame && $frame->channel === $channel) { - $deferred->resolve($frame); - return true; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel)->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk()->done(function () use ($frame, $deferred) { - $deferred->reject(new ClientException($frame->replyText, $frame->replyCode)); - }); - return true; - } - return false; - }); - return $deferred->promise(); - } else { - for (;;) { - while (($frame = $this->getReader()->consumeFrame($this->getReadBuffer())) === null) { - $this->feedReadBuffer(); - } - if ($frame instanceof Protocol\MethodConfirmSelectOkFrame && $frame->channel === $channel) { - return $frame; - } elseif ($frame instanceof Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { - $this->channelCloseOk($channel); - throw new ClientException($frame->replyText, $frame->replyCode); - } elseif ($frame instanceof Protocol\MethodConnectionCloseFrame) { - $this->connectionCloseOk(); - throw new ClientException($frame->replyText, $frame->replyCode); - } else { - $this->enqueue($frame); - } - } - } - throw new \LogicException('This statement should be never reached.'); - } - -} diff --git a/src/Bunny/Message.php b/src/Bunny/Message.php deleted file mode 100644 index 6bdf985..0000000 --- a/src/Bunny/Message.php +++ /dev/null @@ -1,82 +0,0 @@ - - */ -class Message -{ - - /** @var string */ - public $consumerTag; - - /** @var int */ - public $deliveryTag; - - /** @var boolean */ - public $redelivered; - - /** @var string */ - public $exchange; - - /** @var string */ - public $routingKey; - - /** @var array */ - public $headers; - - /** @var string */ - public $content; - - /** - * Constructor. - * - * @param string $consumerTag - * @param string $deliveryTag - * @param boolean $redelivered - * @param string $exchange - * @param string $routingKey - * @param array $headers - * @param string $content - */ - public function __construct($consumerTag, $deliveryTag, $redelivered, $exchange, $routingKey, array $headers, $content) - { - $this->consumerTag = $consumerTag; - $this->deliveryTag = $deliveryTag; - $this->redelivered = $redelivered; - $this->exchange = $exchange; - $this->routingKey = $routingKey; - $this->headers = $headers; - $this->content = $content; - } - - /** - * Returns header or default value. - * - * @param string $name - * @param mixed $default - * @return mixed - */ - public function getHeader($name, $default = null) - { - if (isset($this->headers[$name])) { - return $this->headers[$name]; - } else { - return $default; - } - } - - /** - * Returns TRUE if message has given header. - * - * @param string $name - * @return boolean - */ - public function hasHeader($name) - { - return isset($this->headers[$name]); - } - -} diff --git a/src/Bunny/Channel.php b/src/Channel.php similarity index 77% rename from src/Bunny/Channel.php rename to src/Channel.php index 8dcaddf..a9b2452 100644 --- a/src/Bunny/Channel.php +++ b/src/Channel.php @@ -17,8 +17,11 @@ use Bunny\Protocol\MethodChannelCloseFrame; use Bunny\Protocol\MethodChannelCloseOkFrame; use Bunny\Protocol\MethodFrame; +use Evenement\EventEmitterInterface; +use Evenement\EventEmitterTrait; use React\Promise\Deferred; use React\Promise\PromiseInterface; +use function React\Async\async; /** * AMQP channel. @@ -28,8 +31,9 @@ * * @author Jakub Kulhan */ -class Channel +final class Channel implements EventEmitterInterface { + use EventEmitterTrait; use ChannelMethods { ChannelMethods::consume as private consumeImpl; @@ -45,78 +49,59 @@ class Channel ChannelMethods::confirmSelect as private confirmSelectImpl; } - /** @var AbstractClient */ - protected $client; - - /** @var int */ - protected $channelId; - /** @var callable[] */ - protected $returnCallbacks = []; + private $returnCallbacks = []; /** @var callable[] */ - protected $deliverCallbacks = []; + private $deliverCallbacks = []; /** @var callable[] */ - protected $ackCallbacks = []; + private $ackCallbacks = []; /** @var MethodBasicReturnFrame */ - protected $returnFrame; + private $returnFrame; /** @var MethodBasicDeliverFrame */ - protected $deliverFrame; + private $deliverFrame; /** @var MethodBasicGetOkFrame */ - protected $getOkFrame; + private $getOkFrame; /** @var ContentHeaderFrame */ - protected $headerFrame; + private $headerFrame; /** @var int */ - protected $bodySizeRemaining; + private $bodySizeRemaining; /** @var Buffer */ - protected $bodyBuffer; + private $bodyBuffer; /** @var int */ - protected $state = ChannelStateEnum::READY; + private $state = ChannelStateEnum::READY; /** @var int */ - protected $mode = ChannelModeEnum::REGULAR; + private $mode = ChannelModeEnum::REGULAR; /** @var Deferred */ - protected $closeDeferred; + private $closeDeferred; /** @var PromiseInterface */ - protected $closePromise; + private $closePromise; /** @var Deferred */ - protected $getDeferred; + private $getDeferred; /** @var int */ - protected $deliveryTag; + private $deliveryTag; - /** - * Constructor. - * - * @param AbstractClient $client - * @param int $channelId - */ - public function __construct(AbstractClient $client, $channelId) + public function __construct(private Connection $connection, private Client $client, readonly public int $channelId) { - $this->client = $client; - $this->channelId = $channelId; $this->bodyBuffer = new Buffer(); } - /** - * Returns underlying client instance. - * - * @return AbstractClient - */ public function getClient() { - return $this->client; + return $this->connection; } /** @@ -228,10 +213,10 @@ public function close($replyCode = 0, $replyText = "") $this->state = ChannelStateEnum::CLOSING; - $this->client->channelClose($this->channelId, $replyCode, $replyText, 0, 0); + $this->connection->channelClose($this->channelId, $replyCode, $replyText, 0, 0); $this->closeDeferred = new Deferred(); return $this->closePromise = $this->closeDeferred->promise()->then(function () { - $this->client->removeChannel($this->channelId); + $this->emit('close'); }); } @@ -246,7 +231,7 @@ public function close($replyCode = 0, $replyText = "") * @param bool $exclusive * @param bool $nowait * @param array $arguments - * @return MethodBasicConsumeOkFrame|PromiseInterface + * @return MethodBasicConsumeOkFrame */ public function consume(callable $callback, $queue = "", $consumerTag = "", $noLocal = false, $noAck = false, $exclusive = false, $nowait = false, $arguments = []) { @@ -256,52 +241,13 @@ public function consume(callable $callback, $queue = "", $consumerTag = "", $noL $this->deliverCallbacks[$response->consumerTag] = $callback; return $response; - } elseif ($response instanceof PromiseInterface) { - return $response->then(function (MethodBasicConsumeOkFrame $response) use ($callback) { - $this->deliverCallbacks[$response->consumerTag] = $callback; - return $response; - }); - - } else { - throw new ChannelException( - "basic.consume unexpected response of type " . gettype($response) . - (is_object($response) ? " (" . get_class($response) . ")" : "") . - "." - ); } - } - - /** - * Convenience method that registers consumer and then starts client event loop. - * - * @param callable $callback - * @param string $queue - * @param string $consumerTag - * @param bool $noLocal - * @param bool $noAck - * @param bool $exclusive - * @param bool $nowait - * @param array $arguments - */ - public function run(callable $callback, $queue = "", $consumerTag = "", $noLocal = false, $noAck = false, $exclusive = false, $nowait = false, $arguments = []) - { - $response = $this->consume($callback, $queue, $consumerTag, $noLocal, $noAck, $exclusive, $nowait, $arguments); - - if ($response instanceof MethodBasicConsumeOkFrame) { - $this->client->run(); - } elseif ($response instanceof PromiseInterface) { - $response->done(function () { - $this->client->run(); - }); - - } else { - throw new ChannelException( - "Unexpected response of type " . gettype($response) . - (is_object($response) ? " (" . get_class($response) . ")" : "") . - "." - ); - } + throw new ChannelException( + "basic.consume unexpected response of type " . gettype($response) . + (is_object($response) ? " (" . get_class($response) . ")" : "") . + "." + ); } /** @@ -346,7 +292,7 @@ public function reject(Message $message, $requeue = true) * * @param string $queue * @param bool $noAck - * @return Message|PromiseInterface|null + * @return Message|null */ public function get($queue = "", $noAck = false) { @@ -356,47 +302,26 @@ public function get($queue = "", $noAck = false) $response = $this->getImpl($queue, $noAck); - if ($response instanceof PromiseInterface) { - $this->getDeferred = new Deferred(); - - $response->done(function ($frame) { - if ($frame instanceof MethodBasicGetEmptyFrame) { - // deferred has to be first nullified and then resolved, otherwise results in race condition - $deferred = $this->getDeferred; - $this->getDeferred = null; - $deferred->resolve(null); - - } elseif ($frame instanceof MethodBasicGetOkFrame) { - $this->getOkFrame = $frame; - $this->state = ChannelStateEnum::AWAITING_HEADER; - - } else { - throw new \LogicException("This statement should never be reached."); - } - }); - - return $this->getDeferred->promise(); - - } elseif ($response instanceof MethodBasicGetEmptyFrame) { + if ($response instanceof MethodBasicGetEmptyFrame) { return null; } elseif ($response instanceof MethodBasicGetOkFrame) { $this->state = ChannelStateEnum::AWAITING_HEADER; - $headerFrame = $this->getClient()->awaitContentHeader($this->getChannelId()); + $headerFrame = $this->connection->awaitContentHeader($this->getChannelId()); $this->headerFrame = $headerFrame; $this->bodySizeRemaining = $headerFrame->bodySize; $this->state = ChannelStateEnum::AWAITING_BODY; while ($this->bodySizeRemaining > 0) { - $bodyFrame = $this->getClient()->awaitContentBody($this->getChannelId()); + $bodyFrame = $this->connection->awaitContentBody($this->getChannelId()); $this->bodyBuffer->append($bodyFrame->payload); $this->bodySizeRemaining -= $bodyFrame->payloadSize; if ($this->bodySizeRemaining < 0) { $this->state = ChannelStateEnum::ERROR; - $this->client->disconnect(Constants::STATUS_SYNTAX_ERROR, $errorMessage = "Body overflow, received " . (-$this->bodySizeRemaining) . " more bytes."); + $this->connection->disconnect(Constants::STATUS_SYNTAX_ERROR, $errorMessage = "Body overflow, received " . (-$this->bodySizeRemaining) . " more bytes."); throw new ChannelException($errorMessage); } } @@ -438,13 +363,7 @@ public function publish($body, array $headers = [], $exchange = '', $routingKey $response = $this->publishImpl($body, $headers, $exchange, $routingKey, $mandatory, $immediate); if ($this->mode === ChannelModeEnum::CONFIRM) { - if ($response instanceof PromiseInterface) { - return $response->then(function () { - return ++$this->deliveryTag; - }); - } else { - return ++$this->deliveryTag; - } + return ++$this->deliveryTag; } else { return $response; } @@ -455,7 +374,7 @@ public function publish($body, array $headers = [], $exchange = '', $routingKey * * @param string $consumerTag * @param bool $nowait - * @return bool|Protocol\MethodBasicCancelOkFrame|PromiseInterface + * @return bool|\Bunny\Protocol\MethodBasicCancelOkFrame|PromiseInterface */ public function cancel($consumerTag, $nowait = false) { @@ -467,7 +386,7 @@ public function cancel($consumerTag, $nowait = false) /** * Changes channel to transactional mode. All messages are published to queues only after {@link txCommit()} is called. * - * @return Protocol\MethodTxSelectOkFrame|PromiseInterface + * @return \Bunny\Protocol\MethodTxSelectOkFrame|PromiseInterface */ public function txSelect() { @@ -492,7 +411,7 @@ public function txSelect() /** * Commit transaction. * - * @return Protocol\MethodTxCommitOkFrame|PromiseInterface + * @return \Bunny\Protocol\MethodTxCommitOkFrame|PromiseInterface */ public function txCommit() { @@ -506,7 +425,7 @@ public function txCommit() /** * Rollback transaction. * - * @return Protocol\MethodTxRollbackOkFrame|PromiseInterface + * @return \Bunny\Protocol\MethodTxRollbackOkFrame|PromiseInterface */ public function txRollback() { @@ -521,7 +440,7 @@ public function txRollback() * Changes channel to confirm mode. Broker then asynchronously sends 'basic.ack's for published messages. * * @param bool $nowait - * @return Protocol\MethodConfirmSelectOkFrame|PromiseInterface + * @return \Bunny\Protocol\MethodConfirmSelectOkFrame|PromiseInterface */ public function confirmSelect(callable $callback = null, $nowait = false) { @@ -546,7 +465,7 @@ public function confirmSelect(callable $callback = null, $nowait = false) private function enterConfirmMode(callable $callback = null) { $this->mode = ChannelModeEnum::CONFIRM; - $this->deliveryTag = 0; +// $this->deliveryTag = 0; if ($callback) { $this->addAckListener($callback); @@ -585,8 +504,8 @@ public function onFrameReceived(AbstractFrame $frame) throw new \LogicException("Unhandled channel state."); } - $this->client->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); - + $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); +var_export($this); throw new ChannelException("Unexpected frame: " . $msg); } @@ -597,8 +516,8 @@ public function onFrameReceived(AbstractFrame $frame) $this->closeDeferred->resolve($this->channelId); } - // break reference cycle, must be called after resolving promise - $this->client = null; +// // break reference cycle, must be called after resolving promise +// $this->client = null; // break consumers' reference cycle $this->deliverCallbacks = []; @@ -622,8 +541,8 @@ public function onFrameReceived(AbstractFrame $frame) } elseif ($frame instanceof MethodChannelCloseFrame) { throw new ChannelException("Channel closed by server: " . $frame->replyText, $frame->replyCode); - } else { - throw new ChannelException("Unhandled method frame " . get_class($frame) . "."); +// } else { +// throw new ChannelException("Unhandled method frame " . get_class($frame) . "."); } } elseif ($frame instanceof ContentHeaderFrame) { @@ -643,7 +562,7 @@ public function onFrameReceived(AbstractFrame $frame) throw new \LogicException("Unhandled channel state."); } - $this->client->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); + $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); throw new ChannelException("Unexpected frame: " . $msg); } @@ -675,7 +594,7 @@ public function onFrameReceived(AbstractFrame $frame) throw new \LogicException("Unhandled channel state."); } - $this->client->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); + $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); throw new ChannelException("Unexpected frame: " . $msg); } @@ -685,7 +604,7 @@ public function onFrameReceived(AbstractFrame $frame) if ($this->bodySizeRemaining < 0) { $this->state = ChannelStateEnum::ERROR; - $this->client->disconnect(Constants::STATUS_SYNTAX_ERROR, "Body overflow, received " . (-$this->bodySizeRemaining) . " more bytes."); + $this->connection->disconnect(Constants::STATUS_SYNTAX_ERROR, "Body overflow, received " . (-$this->bodySizeRemaining) . " more bytes."); } elseif ($this->bodySizeRemaining === 0) { $this->state = ChannelStateEnum::READY; @@ -693,7 +612,7 @@ public function onFrameReceived(AbstractFrame $frame) } } elseif ($frame instanceof HeartbeatFrame) { - $this->client->disconnect(Constants::STATUS_UNEXPECTED_FRAME, "Got heartbeat on non-zero channel."); + $this->connection->disconnect(Constants::STATUS_UNEXPECTED_FRAME, "Got heartbeat on non-zero channel."); throw new ChannelException("Unexpected heartbeat frame."); } else { @@ -704,7 +623,7 @@ public function onFrameReceived(AbstractFrame $frame) /** * Callback after content body has been completely received. */ - protected function onBodyComplete() + private function onBodyComplete() { if ($this->returnFrame) { $content = $this->bodyBuffer->consume($this->bodyBuffer->getLength()); @@ -719,7 +638,7 @@ protected function onBodyComplete() ); foreach ($this->returnCallbacks as $callback) { - $callback($message, $this->returnFrame); + async(fn () => $callback($message, $this->returnFrame))(); } $this->returnFrame = null; diff --git a/src/Bunny/ChannelMethods.php b/src/ChannelMethods.php similarity index 84% rename from src/Bunny/ChannelMethods.php rename to src/ChannelMethods.php index 38555c7..565cd0c 100644 --- a/src/Bunny/ChannelMethods.php +++ b/src/ChannelMethods.php @@ -1,7 +1,6 @@ + */ + private array $channels = []; + + public function set(int $channelid, Channel $channel) + { + $this->channels[$channelid] = $channel; + } + + public function has(int $channelid): bool + { + return array_key_exists($channelid, $this->channels); + } + + public function get(int $channelid): Channel + { + return $this->channels[$channelid]; + } + + public function unset(int $channelid) + { + unset($this->channels[$channelid]); + } + + + /** + * @return iterable + */ + public function all(): iterable + { + yield from $this->channels; + } +} diff --git a/src/Client.php b/src/Client.php new file mode 100644 index 0000000..3db445c --- /dev/null +++ b/src/Client.php @@ -0,0 +1,306 @@ + "exchangeDelete". + * Methods from "basic" class are not prefixed with "basic" - e.g. "basic.publish" is just "publish". + * + * Usage: + * + * $c = new Bunny\Client([ + * "host" => "127.0.0.1", + * "port" => 5672, + * "vhost" => "/", + * "user" => "guest", + * "password" => "guest", + * ]); + * + * // client is lazy and will connect once you open a channel, e.g. $c->channel() + * + * @author Jakub Kulhan + */ +final class Client +{ + private readonly array $options; + + private readonly Connector $connector; + + /** @var int */ + private int $state = ClientStateEnum::NOT_CONNECTED; + + private ?Connection $connection = null; + + private Channels $channels; + + /** @var int */ + public int $frameMax = 0xFFFF; + + /** @var int */ + private int $nextChannelId = 1; + + /** @var int */ + private int $channelMax = 0xFFFF; + + /** + * Constructor. + * + * @param array $options + */ + public function __construct(array $options = []) + { + if (!isset($options["host"])) { + $options["host"] = "127.0.0.1"; + } + + if (!isset($options["port"])) { + $options["port"] = 5672; + } + + if (!isset($options["vhost"])) { + if (isset($options["virtual_host"])) { + $options["vhost"] = $options["virtual_host"]; + unset($options["virtual_host"]); + } elseif (isset($options["path"])) { + $options["vhost"] = $options["path"]; + unset($options["path"]); + } else { + $options["vhost"] = "/"; + } + } + + if (!isset($options["user"])) { + if (isset($options["username"])) { + $options["user"] = $options["username"]; + unset($options["username"]); + } else { + $options["user"] = "guest"; + } + } + + if (!isset($options["password"])) { + if (isset($options["pass"])) { + $options["password"] = $options["pass"]; + unset($options["pass"]); + } else { + $options["password"] = "guest"; + } + } + + if (!isset($options["timeout"])) { + $options["timeout"] = 1; + } + + if (!isset($options["heartbeat"])) { + $options["heartbeat"] = 60.0; + } elseif ($options["heartbeat"] >= 2**15) { + throw new \InvalidArgumentException("Heartbeat too high: the value is a signed int16."); + } + + if (!(is_callable($options['heartbeat_callback'] ?? null))) { + unset($options['heartbeat_callback']); + } + + + if (isset($options['ssl']) && is_array($options['ssl'])) { + $options['tls'] = $options['ssl']; + } + + + $this->options = $options; + $this->connector = new Connector($this->options); + + + $this->state = ClientStateEnum::NOT_CONNECTED; + $this->channels = new Channels(); + } + + /** + * Creates and opens new channel. + * + * Channel gets first available channel id. + */ + public function channel(): Channel + { + + if (!$this->isConnected()) { + $this->connect(); + } + + $channelId = $this->findChannelId(); + + $channel = new Channel($this->connection, $this, $channelId); + $channel->once('close', function () use ($channelId) { + $this->channels->unset($channelId); + }); + $this->channels->set($channelId, $channel); + $response = $this->connection->channelOpen($channelId); + + if ($response instanceof MethodChannelOpenOkFrame) { + return $channel; + } + + $this->state = ClientStateEnum::ERROR; + + throw new ClientException( + "channel.open unexpected response of type " . gettype($response) . + (is_object($response) ? "(" . get_class($response) . ")" : "") . + "." + ); + } + + /** + * Connects to AMQP server. + * + * Calling connect() multiple times will result in error. + */ + public function connect(): self + { + if ($this->state !== ClientStateEnum::NOT_CONNECTED) { + throw new ClientException("Client already connected/connecting."); + } + + $this->state = ClientStateEnum::CONNECTING; + + $streamScheme = 'tcp'; + if (isset($this->options['tls']) && is_array($this->options['tls'])) { + $streamScheme = 'tls'; + } + $uri = $streamScheme . "://{$this->options["host"]}:{$this->options["port"]}"; + + try { + $connection = await($this->connector->connect($uri)); + } catch (\Throwable $thrown) { + throw new ClientException('Could not connect to ' . $uri . ': ' . $thrown->getMessage(), $thrown->getCode(), $thrown); + } + + $this->connection = new Connection( + $this, + $connection, + new Buffer(), + new Buffer(), + new ProtocolReader(), + new ProtocolWriter(), + $this->channels, + $this->options, + ); + $this->connection->appendProtocolHeader(); + $this->connection->flushWriteBuffer(); + $start = $this->connection->awaitConnectionStart(); + $this->authResponse($start); + $tune = $this->connection->awaitConnectionTune(); + $this->frameMax = $tune->frameMax; + if ($tune->channelMax > 0) { + $this->channelMax = $tune->channelMax; + } + $this->connection->connectionTuneOk($tune->channelMax, $tune->frameMax, $this->options["heartbeat"]); + $this->connection->connectionOpen($this->options["vhost"]); + $this->connection->startHeathbeatTimer(); + + $this->state = ClientStateEnum::CONNECTED; + + return $this; + } + + /** + * Responds to authentication challenge + * + * @param MethodConnectionStartFrame $start + */ + protected function authResponse(MethodConnectionStartFrame $start): void + { + if (strpos($start->mechanisms, "AMQPLAIN") === false) { + throw new ClientException("Server does not support AMQPLAIN mechanism (supported: {$start->mechanisms})."); + } + + $responseBuffer = new Buffer(); + (new ProtocolWriter())->appendTable([ + "LOGIN" => $this->options["user"], + "PASSWORD" => $this->options["password"], + ], $responseBuffer); + $responseBuffer->discard(4); + + $this->connection->connectionStartOk([], "AMQPLAIN", $responseBuffer->read($responseBuffer->getLength()), "en_US"); + } + + /** + * Disconnects the client. + */ + public function disconnect(int $replyCode = 0, string $replyText = ""): void + { + if ($this->state === ClientStateEnum::DISCONNECTING) { + return; + } + + if ($this->state !== ClientStateEnum::CONNECTED) { + throw new ClientException("Client is not connected."); + } + + $this->state = ClientStateEnum::DISCONNECTING; + + $promises = []; + foreach ($this->channels->all() as $channelId => $channel) { + $promises[] = $channel->close($replyCode, $replyText); + } + await(all($promises)); + + $this->connection->disconnect($replyCode, $replyText, 0, 0); + + $this->state = ClientStateEnum::NOT_CONNECTED; + } + + /** + * Returns true if client is connected to server. + */ + public function isConnected(): bool + { + return $this->state !== ClientStateEnum::NOT_CONNECTED && $this->state !== ClientStateEnum::ERROR; + } + + /** + * @return int + */ + private function findChannelId(): int + { + // first check in range [next, max] ... + for ( + $channelId = $this->nextChannelId; + $channelId <= $this->channelMax; + ++$channelId + ) { + if (!$this->channels->has($channelId)) { + $this->nextChannelId = $channelId + 1; + + return $channelId; + } + } + + // then check in range [min, next) ... + for ( + $channelId = 1; + $channelId < $this->nextChannelId; + ++$channelId + ) { + if (!$this->channels->has($channelId)) { + $this->nextChannelId = $channelId + 1; + + return $channelId; + } + } + + throw new ClientException("No available channels"); + } +} diff --git a/src/Bunny/ClientStateEnum.php b/src/ClientStateEnum.php similarity index 100% rename from src/Bunny/ClientStateEnum.php rename to src/ClientStateEnum.php diff --git a/src/Connection.php b/src/Connection.php new file mode 100644 index 0000000..e719cbc --- /dev/null +++ b/src/Connection.php @@ -0,0 +1,1945 @@ + */ + private array $awaitList = []; + + private ?TimerInterface $queueProcessFutureTick = null; + + public function __construct( + private readonly Client $client, + private readonly ConnectionInterface $connection, + private readonly Buffer $readBuffer, + private readonly Buffer $writeBuffer, + private readonly ProtocolReader $reader, + private readonly ProtocolWriter $writer, + private readonly Channels $channels, + private readonly array $options = [], + ) + { + $this->connection->on('data', function (string $data): void { + $this->readBuffer->append($data); + + while (($frame = $this->reader->consumeFrame($this->readBuffer)) !== null) { + $frameInAwaitList = false; + foreach ($this->awaitList as $index => $frameHandler) { + if ($frameHandler['filter']($frame)) { + unset($this->awaitList[$index]); + $frameHandler['promise']->resolve($frame); + $frameInAwaitList = true; + } + } + + if ($frameInAwaitList) { + continue; + } + + if ($frame->channel === 0) { + $this->onFrameReceived($frame); + continue; + } + + if (!$this->channels->has($frame->channel)) { + throw new ClientException( + "Received frame #{$frame->type} on closed channel #{$frame->channel}." + ); + } + + $this->channels->get($frame->channel)->onFrameReceived($frame); + } + }); + } + + public function disconnect(int $code, string $reason) + { + $this->connectionClose($code, $reason, 0, 0); + $this->connection->close(); + + if ($this->heartbeatTimer === null) { + return; + } + + Loop::cancelTimer($this->heartbeatTimer); + } + + /** + * Callback after connection-level frame has been received. + * + * @param AbstractFrame $frame + */ + private function onFrameReceived(AbstractFrame $frame) + { + if ($frame instanceof MethodFrame) { + if ($frame instanceof MethodConnectionCloseFrame) { + $this->disconnect(Constants::STATUS_CONNECTION_FORCED, "Connection closed by server: ({$frame->replyCode}) " . $frame->replyText); + throw new ClientException("Connection closed by server: " . $frame->replyText, $frame->replyCode); +// } else { +// throw new ClientException("Unhandled method frame " . get_class($frame) . "."); + } + } elseif ($frame instanceof ContentHeaderFrame) { + $this->disconnect(Constants::STATUS_UNEXPECTED_FRAME, "Got header frame on connection channel (#0)."); + } elseif ($frame instanceof ContentBodyFrame) { + $this->disconnect(Constants::STATUS_UNEXPECTED_FRAME, "Got body frame on connection channel (#0)."); + } elseif ($frame instanceof HeartbeatFrame) { +// $this->lastRead = microtime(true); + } else { + throw new ClientException("Unhandled frame " . get_class($frame) . "."); + } + } + + public function appendProtocolHeader(): void + { + $this->writer->appendProtocolHeader($this->writeBuffer); + } + + public function flushWriteBuffer(): void + { + $data = $this->writeBuffer->read($this->writeBuffer->getLength()); + $this->writeBuffer->discard(strlen($data)); + + $this->lastWrite = microtime(true); + if (!$this->connection->write($data)) { + await(new Promise(function (callable $resolve): void { + $this->connection->once('drain', static fn () => $resolve(null)); + })); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\ContentHeaderFrame + */ + public function awaitContentHeader($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\ContentHeaderFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\ContentBodyFrame + */ + public function awaitContentBody($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\ContentBodyFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + /** + * @return \Bunny\Protocol\MethodConnectionStartFrame + */ + public function awaitConnectionStart() + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame): bool { + if ($frame instanceof \Bunny\Protocol\MethodConnectionStartFrame) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function connectionStartOk($clientProperties, $mechanism, $response, $locale = 'en_US') + { + $buffer = new Buffer(); + $buffer->appendUint16(10); + $buffer->appendUint16(11); + $this->writer->appendTable($clientProperties, $buffer); + $buffer->appendUint8(strlen($mechanism)); $buffer->append($mechanism); + $buffer->appendUint32(strlen($response)); $buffer->append($response); + $buffer->appendUint8(strlen($locale)); $buffer->append($locale); + $frame = new \Bunny\Protocol\MethodFrame(10, 11); + $frame->channel = 0; + $frame->payloadSize = $buffer->getLength(); + $frame->payload = $buffer; + $this->writer->appendFrame($frame, $this->writeBuffer); + $this->flushWriteBuffer(); + } + + /** + * @return \Bunny\Protocol\MethodConnectionSecureFrame + */ + public function awaitConnectionSecure() + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame): bool { + if ($frame instanceof \Bunny\Protocol\MethodConnectionSecureFrame) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function connectionSecureOk($response) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16(0); + $buffer->appendUint32(8 + strlen($response)); + $buffer->appendUint16(10); + $buffer->appendUint16(21); + $buffer->appendUint32(strlen($response)); $buffer->append($response); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + } + + /** + * @return \Bunny\Protocol\MethodConnectionTuneFrame + */ + public function awaitConnectionTune() + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame): bool { + if ($frame instanceof \Bunny\Protocol\MethodConnectionTuneFrame) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function connectionTuneOk($channelMax = 0, $frameMax = 0, $heartbeat = 0) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16(0); + $buffer->appendUint32(12); + $buffer->appendUint16(10); + $buffer->appendUint16(31); + $buffer->appendInt16($channelMax); + $buffer->appendInt32($frameMax); + $buffer->appendInt16($heartbeat); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + } + + public function connectionOpen($virtualHost = '/', $capabilities = '', $insist = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16(0); + $buffer->appendUint32(7 + strlen($virtualHost) + strlen($capabilities)); + $buffer->appendUint16(10); + $buffer->appendUint16(40); + $buffer->appendUint8(strlen($virtualHost)); $buffer->append($virtualHost); + $buffer->appendUint8(strlen($capabilities)); $buffer->append($capabilities); + $this->writer->appendBits([$insist], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitConnectionOpenOk(); + } + + /** + * @return \Bunny\Protocol\MethodConnectionOpenOkFrame + */ + public function awaitConnectionOpenOk() + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame): bool { + if ($frame instanceof \Bunny\Protocol\MethodConnectionOpenOkFrame) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function connectionClose($replyCode, $replyText, $closeClassId, $closeMethodId) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16(0); + $buffer->appendUint32(11 + strlen($replyText)); + $buffer->appendUint16(10); + $buffer->appendUint16(50); + $buffer->appendInt16($replyCode); + $buffer->appendUint8(strlen($replyText)); $buffer->append($replyText); + $buffer->appendInt16($closeClassId); + $buffer->appendInt16($closeMethodId); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitConnectionCloseOk(); + } + + /** + * @return \Bunny\Protocol\MethodConnectionCloseFrame + */ + public function awaitConnectionClose() + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame): bool { + if ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function connectionCloseOk() + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16(0); + $buffer->appendUint32(4); + $buffer->appendUint16(10); + $buffer->appendUint16(51); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + } + + /** + * @return \Bunny\Protocol\MethodConnectionCloseOkFrame + */ + public function awaitConnectionCloseOk() + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame): bool { + if ($frame instanceof \Bunny\Protocol\MethodConnectionCloseOkFrame) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + /** + * @return \Bunny\Protocol\MethodConnectionBlockedFrame + */ + public function awaitConnectionBlocked() + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame): bool { + if ($frame instanceof \Bunny\Protocol\MethodConnectionBlockedFrame) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + /** + * @return \Bunny\Protocol\MethodConnectionUnblockedFrame + */ + public function awaitConnectionUnblocked() + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame): bool { + if ($frame instanceof \Bunny\Protocol\MethodConnectionUnblockedFrame) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function channelOpen($channel, $outOfBand = '') + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(5 + strlen($outOfBand)); + $buffer->appendUint16(20); + $buffer->appendUint16(10); + $buffer->appendUint8(strlen($outOfBand)); $buffer->append($outOfBand); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitChannelOpenOk($channel); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodChannelOpenOkFrame + */ + public function awaitChannelOpenOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodChannelOpenOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function channelFlow($channel, $active) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(5); + $buffer->appendUint16(20); + $buffer->appendUint16(20); + $this->writer->appendBits([$active], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitChannelFlowOk($channel); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodChannelFlowFrame + */ + public function awaitChannelFlow($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodChannelFlowFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function channelFlowOk($channel, $active) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(5); + $buffer->appendUint16(20); + $buffer->appendUint16(21); + $this->writer->appendBits([$active], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodChannelFlowOkFrame + */ + public function awaitChannelFlowOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodChannelFlowOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function channelClose($channel, $replyCode, $replyText, $closeClassId, $closeMethodId) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(11 + strlen($replyText)); + $buffer->appendUint16(20); + $buffer->appendUint16(40); + $buffer->appendInt16($replyCode); + $buffer->appendUint8(strlen($replyText)); $buffer->append($replyText); + $buffer->appendInt16($closeClassId); + $buffer->appendInt16($closeMethodId); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodChannelCloseFrame + */ + public function awaitChannelClose($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function channelCloseOk($channel) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(4); + $buffer->appendUint16(20); + $buffer->appendUint16(41); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodChannelCloseOkFrame + */ + public function awaitChannelCloseOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodChannelCloseOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function accessRequest($channel, $realm = '/data', $exclusive = false, $passive = true, $active = true, $write = true, $read = true) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(6 + strlen($realm)); + $buffer->appendUint16(30); + $buffer->appendUint16(10); + $buffer->appendUint8(strlen($realm)); $buffer->append($realm); + $this->writer->appendBits([$exclusive, $passive, $active, $write, $read], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitAccessRequestOk($channel); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodAccessRequestOkFrame + */ + public function awaitAccessRequestOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodAccessRequestOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function exchangeDeclare($channel, $exchange, $exchangeType = 'direct', $passive = false, $durable = false, $autoDelete = false, $internal = false, $nowait = false, $arguments = []) + { + $buffer = new Buffer(); + $buffer->appendUint16(40); + $buffer->appendUint16(10); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); + $buffer->appendUint8(strlen($exchangeType)); $buffer->append($exchangeType); + $this->writer->appendBits([$passive, $durable, $autoDelete, $internal, $nowait], $buffer); + $this->writer->appendTable($arguments, $buffer); + $frame = new \Bunny\Protocol\MethodFrame(40, 10); + $frame->channel = $channel; + $frame->payloadSize = $buffer->getLength(); + $frame->payload = $buffer; + $this->writer->appendFrame($frame, $this->writeBuffer); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitExchangeDeclareOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodExchangeDeclareOkFrame + */ + public function awaitExchangeDeclareOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodExchangeDeclareOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function exchangeDelete($channel, $exchange, $ifUnused = false, $nowait = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(8 + strlen($exchange)); + $buffer->appendUint16(40); + $buffer->appendUint16(20); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); + $this->writer->appendBits([$ifUnused, $nowait], $buffer); + $buffer->appendUint8(206); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitExchangeDeleteOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodExchangeDeleteOkFrame + */ + public function awaitExchangeDeleteOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodExchangeDeleteOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function exchangeBind($channel, $destination, $source, $routingKey = '', $nowait = false, $arguments = []) + { + $buffer = new Buffer(); + $buffer->appendUint16(40); + $buffer->appendUint16(30); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($destination)); $buffer->append($destination); + $buffer->appendUint8(strlen($source)); $buffer->append($source); + $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); + $this->writer->appendBits([$nowait], $buffer); + $this->writer->appendTable($arguments, $buffer); + $frame = new \Bunny\Protocol\MethodFrame(40, 30); + $frame->channel = $channel; + $frame->payloadSize = $buffer->getLength(); + $frame->payload = $buffer; + $this->writer->appendFrame($frame, $this->writeBuffer); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitExchangeBindOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodExchangeBindOkFrame + */ + public function awaitExchangeBindOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodExchangeBindOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function exchangeUnbind($channel, $destination, $source, $routingKey = '', $nowait = false, $arguments = []) + { + $buffer = new Buffer(); + $buffer->appendUint16(40); + $buffer->appendUint16(40); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($destination)); $buffer->append($destination); + $buffer->appendUint8(strlen($source)); $buffer->append($source); + $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); + $this->writer->appendBits([$nowait], $buffer); + $this->writer->appendTable($arguments, $buffer); + $frame = new \Bunny\Protocol\MethodFrame(40, 40); + $frame->channel = $channel; + $frame->payloadSize = $buffer->getLength(); + $frame->payload = $buffer; + $this->writer->appendFrame($frame, $this->writeBuffer); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitExchangeUnbindOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodExchangeUnbindOkFrame + */ + public function awaitExchangeUnbindOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodExchangeUnbindOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function queueDeclare($channel, $queue = '', $passive = false, $durable = false, $exclusive = false, $autoDelete = false, $nowait = false, $arguments = []) + { + $buffer = new Buffer(); + $buffer->appendUint16(50); + $buffer->appendUint16(10); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($queue)); $buffer->append($queue); + $this->writer->appendBits([$passive, $durable, $exclusive, $autoDelete, $nowait], $buffer); + $this->writer->appendTable($arguments, $buffer); + $frame = new \Bunny\Protocol\MethodFrame(50, 10); + $frame->channel = $channel; + $frame->payloadSize = $buffer->getLength(); + $frame->payload = $buffer; + $this->writer->appendFrame($frame, $this->writeBuffer); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitQueueDeclareOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodQueueDeclareOkFrame + */ + public function awaitQueueDeclareOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodQueueDeclareOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function queueBind($channel, $queue, $exchange, $routingKey = '', $nowait = false, $arguments = []) + { + $buffer = new Buffer(); + $buffer->appendUint16(50); + $buffer->appendUint16(20); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($queue)); $buffer->append($queue); + $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); + $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); + $this->writer->appendBits([$nowait], $buffer); + $this->writer->appendTable($arguments, $buffer); + $frame = new \Bunny\Protocol\MethodFrame(50, 20); + $frame->channel = $channel; + $frame->payloadSize = $buffer->getLength(); + $frame->payload = $buffer; + $this->writer->appendFrame($frame, $this->writeBuffer); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitQueueBindOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodQueueBindOkFrame + */ + public function awaitQueueBindOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodQueueBindOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function queuePurge($channel, $queue = '', $nowait = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(8 + strlen($queue)); + $buffer->appendUint16(50); + $buffer->appendUint16(30); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($queue)); $buffer->append($queue); + $this->writer->appendBits([$nowait], $buffer); + $buffer->appendUint8(206); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitQueuePurgeOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodQueuePurgeOkFrame + */ + public function awaitQueuePurgeOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodQueuePurgeOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function queueDelete($channel, $queue = '', $ifUnused = false, $ifEmpty = false, $nowait = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(8 + strlen($queue)); + $buffer->appendUint16(50); + $buffer->appendUint16(40); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($queue)); $buffer->append($queue); + $this->writer->appendBits([$ifUnused, $ifEmpty, $nowait], $buffer); + $buffer->appendUint8(206); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitQueueDeleteOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodQueueDeleteOkFrame + */ + public function awaitQueueDeleteOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodQueueDeleteOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function queueUnbind($channel, $queue, $exchange, $routingKey = '', $arguments = []) + { + $buffer = new Buffer(); + $buffer->appendUint16(50); + $buffer->appendUint16(50); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($queue)); $buffer->append($queue); + $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); + $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); + $this->writer->appendTable($arguments, $buffer); + $frame = new \Bunny\Protocol\MethodFrame(50, 50); + $frame->channel = $channel; + $frame->payloadSize = $buffer->getLength(); + $frame->payload = $buffer; + $this->writer->appendFrame($frame, $this->writeBuffer); + $this->flushWriteBuffer(); + return $this->awaitQueueUnbindOk($channel); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodQueueUnbindOkFrame + */ + public function awaitQueueUnbindOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodQueueUnbindOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function qos($channel, $prefetchSize = 0, $prefetchCount = 0, $global = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(11); + $buffer->appendUint16(60); + $buffer->appendUint16(10); + $buffer->appendInt32($prefetchSize); + $buffer->appendInt16($prefetchCount); + $this->writer->appendBits([$global], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitQosOk($channel); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodBasicQosOkFrame + */ + public function awaitQosOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodBasicQosOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function consume($channel, $queue = '', $consumerTag = '', $noLocal = false, $noAck = false, $exclusive = false, $nowait = false, $arguments = []) + { + $buffer = new Buffer(); + $buffer->appendUint16(60); + $buffer->appendUint16(20); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($queue)); $buffer->append($queue); + $buffer->appendUint8(strlen($consumerTag)); $buffer->append($consumerTag); + $this->writer->appendBits([$noLocal, $noAck, $exclusive, $nowait], $buffer); + $this->writer->appendTable($arguments, $buffer); + $frame = new \Bunny\Protocol\MethodFrame(60, 20); + $frame->channel = $channel; + $frame->payloadSize = $buffer->getLength(); + $frame->payload = $buffer; + $this->writer->appendFrame($frame, $this->writeBuffer); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitConsumeOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodBasicConsumeOkFrame + */ + public function awaitConsumeOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodBasicConsumeOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function cancel($channel, $consumerTag, $nowait = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(6 + strlen($consumerTag)); + $buffer->appendUint16(60); + $buffer->appendUint16(30); + $buffer->appendUint8(strlen($consumerTag)); $buffer->append($consumerTag); + $this->writer->appendBits([$nowait], $buffer); + $buffer->appendUint8(206); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitCancelOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodBasicCancelOkFrame + */ + public function awaitCancelOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodBasicCancelOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function publish($channel, $body, array $headers = [], $exchange = '', $routingKey = '', $mandatory = false, $immediate = false) + { + $buffer = $this->writeBuffer; + $ck = serialize([$channel, $headers, $exchange, $routingKey, $mandatory, $immediate]); + $c = isset($this->cache[$ck]) ? $this->cache[$ck] : null; + $flags = 0; $off0 = 0; $len0 = 0; $off1 = 0; $len1 = 0; $contentTypeLength = null; $contentType = null; $contentEncodingLength = null; $contentEncoding = null; $headersBuffer = null; $deliveryMode = null; $priority = null; $correlationIdLength = null; $correlationId = null; $replyToLength = null; $replyTo = null; $expirationLength = null; $expiration = null; $messageIdLength = null; $messageId = null; $timestamp = null; $typeLength = null; $type = null; $userIdLength = null; $userId = null; $appIdLength = null; $appId = null; $clusterIdLength = null; $clusterId = null; + if ($c) { $buffer->append($c[0]); } + else { + $off0 = $buffer->getLength(); + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(9 + strlen($exchange) + strlen($routingKey)); + $buffer->appendUint16(60); + $buffer->appendUint16(40); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); + $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); + $this->writer->appendBits([$mandatory, $immediate], $buffer); + $buffer->appendUint8(206); + $s = 14; + if (isset($headers['content-type'])) { + $flags |= 32768; + $contentType = $headers['content-type']; + $s += 1; + $s += $contentTypeLength = strlen($contentType); + unset($headers['content-type']); + } + if (isset($headers['content-encoding'])) { + $flags |= 16384; + $contentEncoding = $headers['content-encoding']; + $s += 1; + $s += $contentEncodingLength = strlen($contentEncoding); + unset($headers['content-encoding']); + } + if (isset($headers['delivery-mode'])) { + $flags |= 4096; + $deliveryMode = $headers['delivery-mode']; + $s += 1; + unset($headers['delivery-mode']); + } + if (isset($headers['priority'])) { + $flags |= 2048; + $priority = $headers['priority']; + $s += 1; + unset($headers['priority']); + } + if (isset($headers['correlation-id'])) { + $flags |= 1024; + $correlationId = $headers['correlation-id']; + $s += 1; + $s += $correlationIdLength = strlen($correlationId); + unset($headers['correlation-id']); + } + if (isset($headers['reply-to'])) { + $flags |= 512; + $replyTo = $headers['reply-to']; + $s += 1; + $s += $replyToLength = strlen($replyTo); + unset($headers['reply-to']); + } + if (isset($headers['expiration'])) { + $flags |= 256; + $expiration = $headers['expiration']; + $s += 1; + $s += $expirationLength = strlen($expiration); + unset($headers['expiration']); + } + if (isset($headers['message-id'])) { + $flags |= 128; + $messageId = $headers['message-id']; + $s += 1; + $s += $messageIdLength = strlen($messageId); + unset($headers['message-id']); + } + if (isset($headers['timestamp'])) { + $flags |= 64; + $timestamp = $headers['timestamp']; + $s += 8; + unset($headers['timestamp']); + } + if (isset($headers['type'])) { + $flags |= 32; + $type = $headers['type']; + $s += 1; + $s += $typeLength = strlen($type); + unset($headers['type']); + } + if (isset($headers['user-id'])) { + $flags |= 16; + $userId = $headers['user-id']; + $s += 1; + $s += $userIdLength = strlen($userId); + unset($headers['user-id']); + } + if (isset($headers['app-id'])) { + $flags |= 8; + $appId = $headers['app-id']; + $s += 1; + $s += $appIdLength = strlen($appId); + unset($headers['app-id']); + } + if (isset($headers['cluster-id'])) { + $flags |= 4; + $clusterId = $headers['cluster-id']; + $s += 1; + $s += $clusterIdLength = strlen($clusterId); + unset($headers['cluster-id']); + } + if (!empty($headers)) { + $flags |= 8192; + $this->writer->appendTable($headers, $headersBuffer = new Buffer()); + $s += $headersBuffer->getLength(); + } + $buffer->appendUint8(2); + $buffer->appendUint16($channel); + $buffer->appendUint32($s); + $buffer->appendUint16(60); + $buffer->appendUint16(0); + $len0 = $buffer->getLength() - $off0; + } + $buffer->appendUint64(strlen($body)); + if ($c) { $buffer->append($c[1]); } + else { + $off1 = $buffer->getLength(); + $buffer->appendUint16($flags); + if ($flags & 32768) { + $buffer->appendUint8($contentTypeLength); $buffer->append($contentType); + } + if ($flags & 16384) { + $buffer->appendUint8($contentEncodingLength); $buffer->append($contentEncoding); + } + if ($flags & 8192) { + $buffer->append($headersBuffer); + } + if ($flags & 4096) { + $buffer->appendUint8($deliveryMode); + } + if ($flags & 2048) { + $buffer->appendUint8($priority); + } + if ($flags & 1024) { + $buffer->appendUint8($correlationIdLength); $buffer->append($correlationId); + } + if ($flags & 512) { + $buffer->appendUint8($replyToLength); $buffer->append($replyTo); + } + if ($flags & 256) { + $buffer->appendUint8($expirationLength); $buffer->append($expiration); + } + if ($flags & 128) { + $buffer->appendUint8($messageIdLength); $buffer->append($messageId); + } + if ($flags & 64) { + $this->writer->appendTimestamp($timestamp, $buffer); + } + if ($flags & 32) { + $buffer->appendUint8($typeLength); $buffer->append($type); + } + if ($flags & 16) { + $buffer->appendUint8($userIdLength); $buffer->append($userId); + } + if ($flags & 8) { + $buffer->appendUint8($appIdLength); $buffer->append($appId); + } + if ($flags & 4) { + $buffer->appendUint8($clusterIdLength); $buffer->append($clusterId); + } + $buffer->appendUint8(206); + $len1 = $buffer->getLength() - $off1; + } + if (!$c) { + $this->cache[$ck] = [$buffer->read($len0, $off0), $buffer->read($len1, $off1)]; + if (count($this->cache) > 100) { reset($this->cache); unset($this->cache[key($this->cache)]); } + } + for ($payloadMax = $this->client->frameMax - 8 /* frame preface and frame end */, $i = 0, $l = strlen($body); $i < $l; $i += $payloadMax) { + $payloadSize = $l - $i; if ($payloadSize > $payloadMax) { $payloadSize = $payloadMax; } + $buffer->appendUint8(3); + $buffer->appendUint16($channel); + $buffer->appendUint32($payloadSize); + $buffer->append(substr($body, $i, $payloadSize)); + $buffer->appendUint8(206); + } + $this->flushWriteBuffer(); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodBasicReturnFrame + */ + public function awaitReturn($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodBasicReturnFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodBasicDeliverFrame + */ + public function awaitDeliver($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodBasicDeliverFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function get($channel, $queue = '', $noAck = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(8 + strlen($queue)); + $buffer->appendUint16(60); + $buffer->appendUint16(70); + $buffer->appendInt16(0); + $buffer->appendUint8(strlen($queue)); $buffer->append($queue); + $this->writer->appendBits([$noAck], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitGetOk($channel); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodBasicGetOkFrame|\Bunny\Protocol\MethodBasicGetEmptyFrame + */ + public function awaitGetOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodBasicGetOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodBasicGetEmptyFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function ack($channel, $deliveryTag = 0, $multiple = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(13); + $buffer->appendUint16(60); + $buffer->appendUint16(80); + $buffer->appendInt64($deliveryTag); + $this->writer->appendBits([$multiple], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodBasicAckFrame + */ + public function awaitAck($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodBasicAckFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function reject($channel, $deliveryTag, $requeue = true) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(13); + $buffer->appendUint16(60); + $buffer->appendUint16(90); + $buffer->appendInt64($deliveryTag); + $this->writer->appendBits([$requeue], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + } + + public function recoverAsync($channel, $requeue = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(5); + $buffer->appendUint16(60); + $buffer->appendUint16(100); + $this->writer->appendBits([$requeue], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + } + + public function recover($channel, $requeue = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(5); + $buffer->appendUint16(60); + $buffer->appendUint16(110); + $this->writer->appendBits([$requeue], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitRecoverOk($channel); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodBasicRecoverOkFrame + */ + public function awaitRecoverOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodBasicRecoverOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function nack($channel, $deliveryTag = 0, $multiple = false, $requeue = true) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(13); + $buffer->appendUint16(60); + $buffer->appendUint16(120); + $buffer->appendInt64($deliveryTag); + $this->writer->appendBits([$multiple, $requeue], $buffer); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodBasicNackFrame + */ + public function awaitNack($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodBasicNackFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function txSelect($channel) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(4); + $buffer->appendUint16(90); + $buffer->appendUint16(10); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitTxSelectOk($channel); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodTxSelectOkFrame + */ + public function awaitTxSelectOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodTxSelectOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function txCommit($channel) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(4); + $buffer->appendUint16(90); + $buffer->appendUint16(20); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitTxCommitOk($channel); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodTxCommitOkFrame + */ + public function awaitTxCommitOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodTxCommitOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function txRollback($channel) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(4); + $buffer->appendUint16(90); + $buffer->appendUint16(30); + $buffer->appendUint8(206); + $this->flushWriteBuffer(); + return $this->awaitTxRollbackOk($channel); + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodTxRollbackOkFrame + */ + public function awaitTxRollbackOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodTxRollbackOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function confirmSelect($channel, $nowait = false) + { + $buffer = $this->writeBuffer; + $buffer->appendUint8(1); + $buffer->appendUint16($channel); + $buffer->appendUint32(5); + $buffer->appendUint16(85); + $buffer->appendUint16(10); + $this->writer->appendBits([$nowait], $buffer); + $buffer->appendUint8(206); + if ($nowait) { + $this->flushWriteBuffer(); + } else { + $this->flushWriteBuffer(); + return $this->awaitConfirmSelectOk($channel); + } + } + + /** + * @param int $channel + * + * @return \Bunny\Protocol\MethodConfirmSelectOkFrame + */ + public function awaitConfirmSelectOk($channel) + { + $deferred = new Deferred(); + $this->awaitList[] = [ + 'filter' => function (AbstractFrame $frame) use ($channel): bool { + if ($frame instanceof \Bunny\Protocol\MethodConfirmSelectOkFrame && $frame->channel === $channel) { + return true; + } elseif ($frame instanceof \Bunny\Protocol\MethodChannelCloseFrame && $frame->channel === $channel) { + $this->channelCloseOk($channel); + throw new ClientException($frame->replyText, $frame->replyCode); + } elseif ($frame instanceof \Bunny\Protocol\MethodConnectionCloseFrame) { + $this->connectionCloseOk(); + throw new ClientException($frame->replyText, $frame->replyCode); + } + + return false; + }, + 'promise' => $deferred, + ]; + + return await($deferred->promise()); + } + + public function startHeathbeatTimer(): void + { + $this->heartbeatTimer = Loop::addTimer($this->options["heartbeat"], [$this, "onHeartbeat"]); + $this->connection->on('drain', [$this, "onHeartbeat"]); + } + + /** + * Callback when heartbeat timer timed out. + */ + public function onHeartbeat() + { + $now = microtime(true); + $nextHeartbeat = ($this->lastWrite ?: $now) + $this->options["heartbeat"]; + + if ($now >= $nextHeartbeat) { + $this->writer->appendFrame(new HeartbeatFrame(), $this->writeBuffer); + $this->flushWriteBuffer(); + + $this->heartbeatTimer = Loop::addTimer($this->options["heartbeat"], [$this, "onHeartbeat"]); + if (is_callable($this->options['heartbeat_callback'] ?? null)) { + $this->options['heartbeat_callback']->call($this); + } + } else { + $this->heartbeatTimer = Loop::addTimer($nextHeartbeat - $now, [$this, "onHeartbeat"]); + } + } +} diff --git a/src/Bunny/Constants.php b/src/Constants.php similarity index 100% rename from src/Bunny/Constants.php rename to src/Constants.php diff --git a/src/Bunny/Exception/BufferUnderflowException.php b/src/Exception/BufferUnderflowException.php similarity index 87% rename from src/Bunny/Exception/BufferUnderflowException.php rename to src/Exception/BufferUnderflowException.php index 2bc1c8d..b2835e5 100644 --- a/src/Bunny/Exception/BufferUnderflowException.php +++ b/src/Exception/BufferUnderflowException.php @@ -1,6 +1,8 @@ + */ +final class Message +{ + + public function __construct( + public string|null $consumerTag, + public int|null $deliveryTag, + public bool|null $redelivered, + public string $exchange, + public string $routingKey, + public array $headers, + public string $content, + ) + { + } + + /** + * Returns header or default value. + * + * @param string $name + * @param mixed $default + * @return mixed + */ + public function getHeader(string $name, mixed $default = null): mixed + { + if (array_key_exists($name, $this->headers)) { + return $this->headers[$name]; + } else { + return $default; + } + } + + /** + * Returns TRUE if message has given header. + * + * @param string $name + * @return boolean + */ + public function hasHeader(string $name): bool + { + return array_key_exists($name, $this->headers); + } + +} diff --git a/src/Bunny/Protocol/AbstractFrame.php b/src/Protocol/AbstractFrame.php similarity index 100% rename from src/Bunny/Protocol/AbstractFrame.php rename to src/Protocol/AbstractFrame.php diff --git a/src/Bunny/Protocol/Buffer.php b/src/Protocol/Buffer.php similarity index 100% rename from src/Bunny/Protocol/Buffer.php rename to src/Protocol/Buffer.php diff --git a/src/Bunny/Protocol/ContentBodyFrame.php b/src/Protocol/ContentBodyFrame.php similarity index 100% rename from src/Bunny/Protocol/ContentBodyFrame.php rename to src/Protocol/ContentBodyFrame.php diff --git a/src/Bunny/Protocol/ContentHeaderFrame.php b/src/Protocol/ContentHeaderFrame.php similarity index 100% rename from src/Bunny/Protocol/ContentHeaderFrame.php rename to src/Protocol/ContentHeaderFrame.php diff --git a/src/Bunny/Protocol/HeartbeatFrame.php b/src/Protocol/HeartbeatFrame.php similarity index 100% rename from src/Bunny/Protocol/HeartbeatFrame.php rename to src/Protocol/HeartbeatFrame.php diff --git a/src/Bunny/Protocol/MethodAccessRequestFrame.php b/src/Protocol/MethodAccessRequestFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodAccessRequestFrame.php rename to src/Protocol/MethodAccessRequestFrame.php diff --git a/src/Bunny/Protocol/MethodAccessRequestOkFrame.php b/src/Protocol/MethodAccessRequestOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodAccessRequestOkFrame.php rename to src/Protocol/MethodAccessRequestOkFrame.php diff --git a/src/Bunny/Protocol/MethodBasicAckFrame.php b/src/Protocol/MethodBasicAckFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicAckFrame.php rename to src/Protocol/MethodBasicAckFrame.php diff --git a/src/Bunny/Protocol/MethodBasicCancelFrame.php b/src/Protocol/MethodBasicCancelFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicCancelFrame.php rename to src/Protocol/MethodBasicCancelFrame.php diff --git a/src/Bunny/Protocol/MethodBasicCancelOkFrame.php b/src/Protocol/MethodBasicCancelOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicCancelOkFrame.php rename to src/Protocol/MethodBasicCancelOkFrame.php diff --git a/src/Bunny/Protocol/MethodBasicConsumeFrame.php b/src/Protocol/MethodBasicConsumeFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicConsumeFrame.php rename to src/Protocol/MethodBasicConsumeFrame.php diff --git a/src/Bunny/Protocol/MethodBasicConsumeOkFrame.php b/src/Protocol/MethodBasicConsumeOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicConsumeOkFrame.php rename to src/Protocol/MethodBasicConsumeOkFrame.php diff --git a/src/Bunny/Protocol/MethodBasicDeliverFrame.php b/src/Protocol/MethodBasicDeliverFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicDeliverFrame.php rename to src/Protocol/MethodBasicDeliverFrame.php diff --git a/src/Bunny/Protocol/MethodBasicGetEmptyFrame.php b/src/Protocol/MethodBasicGetEmptyFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicGetEmptyFrame.php rename to src/Protocol/MethodBasicGetEmptyFrame.php diff --git a/src/Bunny/Protocol/MethodBasicGetFrame.php b/src/Protocol/MethodBasicGetFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicGetFrame.php rename to src/Protocol/MethodBasicGetFrame.php diff --git a/src/Bunny/Protocol/MethodBasicGetOkFrame.php b/src/Protocol/MethodBasicGetOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicGetOkFrame.php rename to src/Protocol/MethodBasicGetOkFrame.php diff --git a/src/Bunny/Protocol/MethodBasicNackFrame.php b/src/Protocol/MethodBasicNackFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicNackFrame.php rename to src/Protocol/MethodBasicNackFrame.php diff --git a/src/Bunny/Protocol/MethodBasicPublishFrame.php b/src/Protocol/MethodBasicPublishFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicPublishFrame.php rename to src/Protocol/MethodBasicPublishFrame.php diff --git a/src/Bunny/Protocol/MethodBasicQosFrame.php b/src/Protocol/MethodBasicQosFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicQosFrame.php rename to src/Protocol/MethodBasicQosFrame.php diff --git a/src/Bunny/Protocol/MethodBasicQosOkFrame.php b/src/Protocol/MethodBasicQosOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicQosOkFrame.php rename to src/Protocol/MethodBasicQosOkFrame.php diff --git a/src/Bunny/Protocol/MethodBasicRecoverAsyncFrame.php b/src/Protocol/MethodBasicRecoverAsyncFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicRecoverAsyncFrame.php rename to src/Protocol/MethodBasicRecoverAsyncFrame.php diff --git a/src/Bunny/Protocol/MethodBasicRecoverFrame.php b/src/Protocol/MethodBasicRecoverFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicRecoverFrame.php rename to src/Protocol/MethodBasicRecoverFrame.php diff --git a/src/Bunny/Protocol/MethodBasicRecoverOkFrame.php b/src/Protocol/MethodBasicRecoverOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicRecoverOkFrame.php rename to src/Protocol/MethodBasicRecoverOkFrame.php diff --git a/src/Bunny/Protocol/MethodBasicRejectFrame.php b/src/Protocol/MethodBasicRejectFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicRejectFrame.php rename to src/Protocol/MethodBasicRejectFrame.php diff --git a/src/Bunny/Protocol/MethodBasicReturnFrame.php b/src/Protocol/MethodBasicReturnFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodBasicReturnFrame.php rename to src/Protocol/MethodBasicReturnFrame.php diff --git a/src/Bunny/Protocol/MethodChannelCloseFrame.php b/src/Protocol/MethodChannelCloseFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodChannelCloseFrame.php rename to src/Protocol/MethodChannelCloseFrame.php diff --git a/src/Bunny/Protocol/MethodChannelCloseOkFrame.php b/src/Protocol/MethodChannelCloseOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodChannelCloseOkFrame.php rename to src/Protocol/MethodChannelCloseOkFrame.php diff --git a/src/Bunny/Protocol/MethodChannelFlowFrame.php b/src/Protocol/MethodChannelFlowFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodChannelFlowFrame.php rename to src/Protocol/MethodChannelFlowFrame.php diff --git a/src/Bunny/Protocol/MethodChannelFlowOkFrame.php b/src/Protocol/MethodChannelFlowOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodChannelFlowOkFrame.php rename to src/Protocol/MethodChannelFlowOkFrame.php diff --git a/src/Bunny/Protocol/MethodChannelOpenFrame.php b/src/Protocol/MethodChannelOpenFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodChannelOpenFrame.php rename to src/Protocol/MethodChannelOpenFrame.php diff --git a/src/Bunny/Protocol/MethodChannelOpenOkFrame.php b/src/Protocol/MethodChannelOpenOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodChannelOpenOkFrame.php rename to src/Protocol/MethodChannelOpenOkFrame.php diff --git a/src/Bunny/Protocol/MethodConfirmSelectFrame.php b/src/Protocol/MethodConfirmSelectFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConfirmSelectFrame.php rename to src/Protocol/MethodConfirmSelectFrame.php diff --git a/src/Bunny/Protocol/MethodConfirmSelectOkFrame.php b/src/Protocol/MethodConfirmSelectOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConfirmSelectOkFrame.php rename to src/Protocol/MethodConfirmSelectOkFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionBlockedFrame.php b/src/Protocol/MethodConnectionBlockedFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionBlockedFrame.php rename to src/Protocol/MethodConnectionBlockedFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionCloseFrame.php b/src/Protocol/MethodConnectionCloseFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionCloseFrame.php rename to src/Protocol/MethodConnectionCloseFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionCloseOkFrame.php b/src/Protocol/MethodConnectionCloseOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionCloseOkFrame.php rename to src/Protocol/MethodConnectionCloseOkFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionOpenFrame.php b/src/Protocol/MethodConnectionOpenFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionOpenFrame.php rename to src/Protocol/MethodConnectionOpenFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionOpenOkFrame.php b/src/Protocol/MethodConnectionOpenOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionOpenOkFrame.php rename to src/Protocol/MethodConnectionOpenOkFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionSecureFrame.php b/src/Protocol/MethodConnectionSecureFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionSecureFrame.php rename to src/Protocol/MethodConnectionSecureFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionSecureOkFrame.php b/src/Protocol/MethodConnectionSecureOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionSecureOkFrame.php rename to src/Protocol/MethodConnectionSecureOkFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionStartFrame.php b/src/Protocol/MethodConnectionStartFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionStartFrame.php rename to src/Protocol/MethodConnectionStartFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionStartOkFrame.php b/src/Protocol/MethodConnectionStartOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionStartOkFrame.php rename to src/Protocol/MethodConnectionStartOkFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionTuneFrame.php b/src/Protocol/MethodConnectionTuneFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionTuneFrame.php rename to src/Protocol/MethodConnectionTuneFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionTuneOkFrame.php b/src/Protocol/MethodConnectionTuneOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionTuneOkFrame.php rename to src/Protocol/MethodConnectionTuneOkFrame.php diff --git a/src/Bunny/Protocol/MethodConnectionUnblockedFrame.php b/src/Protocol/MethodConnectionUnblockedFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodConnectionUnblockedFrame.php rename to src/Protocol/MethodConnectionUnblockedFrame.php diff --git a/src/Bunny/Protocol/MethodExchangeBindFrame.php b/src/Protocol/MethodExchangeBindFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodExchangeBindFrame.php rename to src/Protocol/MethodExchangeBindFrame.php diff --git a/src/Bunny/Protocol/MethodExchangeBindOkFrame.php b/src/Protocol/MethodExchangeBindOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodExchangeBindOkFrame.php rename to src/Protocol/MethodExchangeBindOkFrame.php diff --git a/src/Bunny/Protocol/MethodExchangeDeclareFrame.php b/src/Protocol/MethodExchangeDeclareFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodExchangeDeclareFrame.php rename to src/Protocol/MethodExchangeDeclareFrame.php diff --git a/src/Bunny/Protocol/MethodExchangeDeclareOkFrame.php b/src/Protocol/MethodExchangeDeclareOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodExchangeDeclareOkFrame.php rename to src/Protocol/MethodExchangeDeclareOkFrame.php diff --git a/src/Bunny/Protocol/MethodExchangeDeleteFrame.php b/src/Protocol/MethodExchangeDeleteFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodExchangeDeleteFrame.php rename to src/Protocol/MethodExchangeDeleteFrame.php diff --git a/src/Bunny/Protocol/MethodExchangeDeleteOkFrame.php b/src/Protocol/MethodExchangeDeleteOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodExchangeDeleteOkFrame.php rename to src/Protocol/MethodExchangeDeleteOkFrame.php diff --git a/src/Bunny/Protocol/MethodExchangeUnbindFrame.php b/src/Protocol/MethodExchangeUnbindFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodExchangeUnbindFrame.php rename to src/Protocol/MethodExchangeUnbindFrame.php diff --git a/src/Bunny/Protocol/MethodExchangeUnbindOkFrame.php b/src/Protocol/MethodExchangeUnbindOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodExchangeUnbindOkFrame.php rename to src/Protocol/MethodExchangeUnbindOkFrame.php diff --git a/src/Bunny/Protocol/MethodFrame.php b/src/Protocol/MethodFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodFrame.php rename to src/Protocol/MethodFrame.php diff --git a/src/Bunny/Protocol/MethodQueueBindFrame.php b/src/Protocol/MethodQueueBindFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodQueueBindFrame.php rename to src/Protocol/MethodQueueBindFrame.php diff --git a/src/Bunny/Protocol/MethodQueueBindOkFrame.php b/src/Protocol/MethodQueueBindOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodQueueBindOkFrame.php rename to src/Protocol/MethodQueueBindOkFrame.php diff --git a/src/Bunny/Protocol/MethodQueueDeclareFrame.php b/src/Protocol/MethodQueueDeclareFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodQueueDeclareFrame.php rename to src/Protocol/MethodQueueDeclareFrame.php diff --git a/src/Bunny/Protocol/MethodQueueDeclareOkFrame.php b/src/Protocol/MethodQueueDeclareOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodQueueDeclareOkFrame.php rename to src/Protocol/MethodQueueDeclareOkFrame.php diff --git a/src/Bunny/Protocol/MethodQueueDeleteFrame.php b/src/Protocol/MethodQueueDeleteFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodQueueDeleteFrame.php rename to src/Protocol/MethodQueueDeleteFrame.php diff --git a/src/Bunny/Protocol/MethodQueueDeleteOkFrame.php b/src/Protocol/MethodQueueDeleteOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodQueueDeleteOkFrame.php rename to src/Protocol/MethodQueueDeleteOkFrame.php diff --git a/src/Bunny/Protocol/MethodQueuePurgeFrame.php b/src/Protocol/MethodQueuePurgeFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodQueuePurgeFrame.php rename to src/Protocol/MethodQueuePurgeFrame.php diff --git a/src/Bunny/Protocol/MethodQueuePurgeOkFrame.php b/src/Protocol/MethodQueuePurgeOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodQueuePurgeOkFrame.php rename to src/Protocol/MethodQueuePurgeOkFrame.php diff --git a/src/Bunny/Protocol/MethodQueueUnbindFrame.php b/src/Protocol/MethodQueueUnbindFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodQueueUnbindFrame.php rename to src/Protocol/MethodQueueUnbindFrame.php diff --git a/src/Bunny/Protocol/MethodQueueUnbindOkFrame.php b/src/Protocol/MethodQueueUnbindOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodQueueUnbindOkFrame.php rename to src/Protocol/MethodQueueUnbindOkFrame.php diff --git a/src/Bunny/Protocol/MethodTxCommitFrame.php b/src/Protocol/MethodTxCommitFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodTxCommitFrame.php rename to src/Protocol/MethodTxCommitFrame.php diff --git a/src/Bunny/Protocol/MethodTxCommitOkFrame.php b/src/Protocol/MethodTxCommitOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodTxCommitOkFrame.php rename to src/Protocol/MethodTxCommitOkFrame.php diff --git a/src/Bunny/Protocol/MethodTxRollbackFrame.php b/src/Protocol/MethodTxRollbackFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodTxRollbackFrame.php rename to src/Protocol/MethodTxRollbackFrame.php diff --git a/src/Bunny/Protocol/MethodTxRollbackOkFrame.php b/src/Protocol/MethodTxRollbackOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodTxRollbackOkFrame.php rename to src/Protocol/MethodTxRollbackOkFrame.php diff --git a/src/Bunny/Protocol/MethodTxSelectFrame.php b/src/Protocol/MethodTxSelectFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodTxSelectFrame.php rename to src/Protocol/MethodTxSelectFrame.php diff --git a/src/Bunny/Protocol/MethodTxSelectOkFrame.php b/src/Protocol/MethodTxSelectOkFrame.php similarity index 100% rename from src/Bunny/Protocol/MethodTxSelectOkFrame.php rename to src/Protocol/MethodTxSelectOkFrame.php diff --git a/src/Bunny/Protocol/ProtocolReader.php b/src/Protocol/ProtocolReader.php similarity index 100% rename from src/Bunny/Protocol/ProtocolReader.php rename to src/Protocol/ProtocolReader.php index 127280e..36ce5c2 100644 --- a/src/Bunny/Protocol/ProtocolReader.php +++ b/src/Protocol/ProtocolReader.php @@ -1,8 +1,8 @@ helper = new AsynchronousClientHelper(); - } - - public function testConnect() - { - $loop = Factory::create(); - - $loop->addTimer(5, function () { - throw new TimeoutException(); - }); - - $client = $this->helper->createClient($loop); - - $this->assertFalse($client->isConnected()); - - $client->connect()->then(function (Client $client) { - $this->assertTrue($client->isConnected()); - - return $client->disconnect(); - })->then(function (Client $client) use ($loop) { - $this->assertFalse($client->isConnected()); - $loop->stop(); - })->done(); - - $loop->run(); - - $this->assertFalse($client->isConnected()); - } - - public function testConnectFailure() - { - $this->expectException(ClientException::class); - - $loop = Factory::create(); - - $loop->addTimer(5, function () { - throw new TimeoutException(); - }); - - $options = $this->helper->getDefaultOptions(); - - $options['vhost'] = 'bogus-vhost'; - - $client = $this->helper->createClient($loop, $options); - - $client->connect()->then(function () use ($loop) { - $this->fail("client should not connect"); - $loop->stop(); - })->done(); - - $loop->run(); - } - - public function testOpenChannel() - { - $loop = Factory::create(); - - $loop->addTimer(5, function () { - throw new TimeoutException(); - }); - - $client = $this->helper->createClient($loop); - $client->connect()->then(function (Client $client) { - return $client->channel(); - })->then(function (Channel $ch) { - return $ch->getClient()->disconnect(); - })->then(function () use ($loop) { - $loop->stop(); - })->done(); - - $loop->run(); - - $this->assertTrue(true); - } - - public function testOpenMultipleChannel() - { - $loop = Factory::create(); - - $loop->addTimer(5, function () { - throw new TimeoutException(); - }); - - $client = $this->helper->createClient($loop); - $client->connect()->then(function (Client $client) { - return Promise\all([ - $client->channel(), - $client->channel(), - $client->channel(), - ]); - })->then(function (array $chs) { - /** @var Channel[] $chs */ - $this->assertCount(3, $chs); - for ($i = 0, $l = count($chs); $i < $l; ++$i) { - $this->assertInstanceOf(Channel::class, $chs[$i]); - for ($j = 0; $j < $i; ++$j) { - $this->assertNotEquals($chs[$i]->getChannelId(), $chs[$j]->getChannelId()); - } - } - - return $chs[0]->getClient()->disconnect(); - - })->then(function () use ($loop) { - $loop->stop(); - })->done(); - - $loop->run(); - } - - public function testConflictingQueueDeclareRejects() - { - $loop = Factory::create(); - - $loop->addTimer(5, function () { - throw new TimeoutException(); - }); - - $client = $this->helper->createClient($loop); - $client->connect()->then(function (Client $client) { - return $client->channel(); - })->then(function (Channel $ch) { - return Promise\all([ - $ch->queueDeclare("conflict", false, false), - $ch->queueDeclare("conflict", false, true), - ]); - })->then(function () use ($loop) { - $this->fail("Promise should get rejected"); - $loop->stop(); - }, function (\Exception $e) use ($loop) { - $this->assertInstanceOf(ClientException::class, $e); - $loop->stop(); - })->done(); - - $loop->run(); - } - - public function testDisconnectWithBufferedMessages() - { - $loop = Factory::create(); - - $loop->addTimer(5, function () { - throw new TimeoutException(); - }); - - $processed = 0; - - $client = $this->helper->createClient($loop); - $client->connect()->then(function (Client $client) { - return $client->channel(); - })->then(function (Channel $channel) use ($client, $loop, &$processed) { - return Promise\all([ - $channel->qos(0, 1000), - $channel->queueDeclare("disconnect_test"), - $channel->consume(function (Message $message, Channel $channel) use ($client, $loop, &$processed) { - $channel->ack($message); - - ++$processed; - - $client->disconnect()->done(function () use ($loop) { - $loop->stop(); - }); - - }, "disconnect_test"), - $channel->publish(".", [], "", "disconnect_test"), - $channel->publish(".", [], "", "disconnect_test"), - $channel->publish(".", [], "", "disconnect_test"), - ]); - })->done(); - - $loop->run(); - - // all messages should be processed - $this->assertEquals(1, $processed); - - // Clean-up Queue - $client->connect()->then(function (Client $client) { - return $client->channel(); - })->then(function (Channel $channel) use ($client, $loop, &$processed) { - return Promise\all([ - $channel->queueDelete("disconnect_test"), - $client->disconnect()->done(function () use ($loop) { - $loop->stop(); - }) - ]); - })->done(); - - $loop->run(); - } - - public function testGet() - { - $loop = Factory::create(); - - $loop->addTimer(1, function () { - throw new TimeoutException(); - }); - - $client = $this->helper->createClient($loop); - /** @var Channel $channel */ - $channel = null; - $client->connect()->then(function (Client $client) { - return $client->channel(); - - })->then(function (Channel $ch) use (&$channel) { - $channel = $ch; - - return Promise\all([ - $channel->queueDeclare("get_test"), - $channel->publish(".", [], "", "get_test"), - ]); - - })->then(function () use (&$channel) { - return $channel->get("get_test", true); - - })->then(function (Message $message1 = null) use (&$channel) { - $this->assertNotNull($message1); - $this->assertInstanceOf(Message::class, $message1); - $this->assertEquals($message1->exchange, ""); - $this->assertEquals($message1->content, "."); - - return $channel->get("get_test", true); - - })->then(function (Message $message2 = null) use (&$channel) { - $this->assertNull($message2); - - return $channel->publish("..", [], "", "get_test"); - - })->then(function () use (&$channel) { - return $channel->get("get_test"); - - })->then(function (Message $message3 = null) use (&$channel) { - $this->assertNotNull($message3); - $this->assertInstanceOf(Message::class, $message3); - $this->assertEquals($message3->exchange, ""); - $this->assertEquals($message3->content, ".."); - - $channel->ack($message3); - - return $channel->getClient()->disconnect(); - - })->then(function () use ($loop) { - $loop->stop(); - })->done(); - - $loop->run(); - } - - public function testReturn() - { - $loop = Factory::create(); - - $loop->addTimer(1, function () { - throw new TimeoutException(); - }); - - $client = $this->helper->createClient($loop); - - /** @var Channel $channel */ - $channel = null; - /** @var Message $returnedMessage */ - $returnedMessage = null; - /** @var MethodBasicReturnFrame $returnedFrame */ - $returnedFrame = null; - - $client->connect()->then(function (Client $client) { - return $client->channel(); - - })->then(function (Channel $ch) use ($loop, &$channel, &$returnedMessage, &$returnedFrame) { - $channel = $ch; - - $channel->addReturnListener(function (Message $message, MethodBasicReturnFrame $frame) use ($loop, &$returnedMessage, &$returnedFrame) { - $returnedMessage = $message; - $returnedFrame = $frame; - $loop->stop(); - }); - - return $channel->publish("xxx", [], "", "404", true); - })->done(); - - $loop->run(); - - $this->assertNotNull($returnedMessage); - $this->assertInstanceOf(Message::class, $returnedMessage); - $this->assertEquals("xxx", $returnedMessage->content); - $this->assertEquals("", $returnedMessage->exchange); - $this->assertEquals("404", $returnedMessage->routingKey); - } - - public function testHeartBeatCallback() - { - $loop = Factory::create(); - - $loop->addTimer(3, function () { - throw new TimeoutException(); - }); - - $called = 0; - - $defaultOptions = $this->helper->getDefaultOptions(); - - $client = $this->helper->createClient($loop, array_merge($defaultOptions, [ - 'heartbeat' => 1.0, - 'heartbeat_callback' => function () use (&$called) { - $called += 1; - } - ])); - - $client->connect()->then(function (Client $client) { - sleep(1); - return $client->channel(); - })->then(function (Channel $ch) { - sleep(1); - return $ch->queueDeclare('hello', false, false, false, false)->then(function () use ($ch) { - return $ch; - }); - })->then(function (Channel $ch) { - return $ch->getClient()->disconnect(); - })->then(function () use ($loop) { - $loop->stop(); - })->done(); - - $loop->run(); - - $this->assertEquals(2, $called); - } - -} diff --git a/test/Bunny/ChannelTest.php b/test/Bunny/ChannelTest.php index 7c0b19f..452a4b3 100644 --- a/test/Bunny/ChannelTest.php +++ b/test/Bunny/ChannelTest.php @@ -6,11 +6,17 @@ namespace Bunny; +use Bunny\Channel; +use Bunny\Client; +use Bunny\Message; use Bunny\Test\Library\SynchronousClientHelper; use PHPUnit\Framework\TestCase; +use WyriHaximus\React\PHPUnit\RunTestsInFibersTrait; class ChannelTest extends TestCase { + use RunTestsInFibersTrait; + /** * @var SynchronousClientHelper */ @@ -29,13 +35,9 @@ public function testClose() $c->connect(); $promise = $c->channel()->close(); $this->assertInstanceOf("React\\Promise\\PromiseInterface", $promise); - $promise->done(function () use ($c) { - $c->stop(); - }); - $c->run(); $this->assertTrue($c->isConnected()); - $this->helper->disconnectClientWithEventLoop($c); + $c->disconnect(); $this->assertFalse($c->isConnected()); } @@ -44,11 +46,10 @@ public function testExchangeDeclare() $c = $this->helper->createClient(); $ch = $c->connect()->channel(); + $this->assertTrue($c->isConnected()); $ch->exchangeDeclare("test_exchange", "direct", false, false, true); - $c->disconnect(); - $this->assertTrue($c->isConnected()); - $this->helper->disconnectClientWithEventLoop($c); + $c->disconnect(); $this->assertFalse($c->isConnected()); } @@ -57,11 +58,10 @@ public function testQueueDeclare() $c = $this->helper->createClient(); $ch = $c->connect()->channel(); + $this->assertTrue($c->isConnected()); $ch->queueDeclare("test_queue", false, false, false, true); - $c->disconnect(); - $this->assertTrue($c->isConnected()); - $this->helper->disconnectClientWithEventLoop($c); + $c->disconnect(); $this->assertFalse($c->isConnected()); } @@ -70,13 +70,14 @@ public function testQueueBind() $c = $this->helper->createClient(); $ch = $c->connect()->channel(); + $this->assertTrue($c->isConnected()); $ch->exchangeDeclare("test_exchange", "direct", false, false, true); + $this->assertTrue($c->isConnected()); $ch->queueDeclare("test_queue", false, false, false, true); + $this->assertTrue($c->isConnected()); $ch->queueBind("test_queue", "test_exchange"); - $ch->getClient()->disconnect(); - $this->assertTrue($c->isConnected()); - $this->helper->disconnectClientWithEventLoop($c); + $c->disconnect(); $this->assertFalse($c->isConnected()); } @@ -85,11 +86,10 @@ public function testPublish() $c = $this->helper->createClient(); $ch = $c->connect()->channel(); + $this->assertTrue($c->isConnected()); $ch->publish("test publish", []); - $ch->getClient()->disconnect(); - $this->assertTrue($c->isConnected()); - $this->helper->disconnectClientWithEventLoop($c); + $c->disconnect(); $this->assertFalse($c->isConnected()); } @@ -98,35 +98,16 @@ public function testConsume() $c = $this->helper->createClient(); $ch = $c->connect()->channel(); + $this->assertTrue($c->isConnected()); $ch->queueDeclare("test_queue", false, false, false, true); + $this->assertTrue($c->isConnected()); $ch->consume(function (Message $msg, Channel $ch, Client $c) { $this->assertEquals("hi", $msg->content); - $c->stop(); }); + $this->assertTrue($c->isConnected()); $ch->publish("hi", [], "", "test_queue"); - $c->run(); - $c->disconnect(); - $this->assertTrue($c->isConnected()); - $this->helper->disconnectClientWithEventLoop($c); - $this->assertFalse($c->isConnected()); - } - - public function testRun() - { - $c = $this->helper->createClient(); - - $ch = $c->connect()->channel(); - $ch->queueDeclare("test_queue", false, false, false, true); - $ch->publish("hi again", [], "", "test_queue"); - $ch->run(function (Message $msg, Channel $ch, Client $c) { - $this->assertEquals("hi again", $msg->content); - $c->stop(); - }); $c->disconnect(); - - $this->assertTrue($c->isConnected()); - $this->helper->disconnectClientWithEventLoop($c); $this->assertFalse($c->isConnected()); } @@ -136,17 +117,15 @@ public function testHeaders() $ch = $c->connect()->channel(); $ch->queueDeclare("test_queue", false, false, false, true); - $ch->publish("hi html", ["content-type" => "text/html"], "", "test_queue"); - $ch->run(function (Message $msg, Channel $ch, Client $c) { + $ch->consume(function (Message $msg, Channel $ch, Client $c) { $this->assertTrue($msg->hasHeader("content-type")); $this->assertEquals("text/html", $msg->getHeader("content-type")); $this->assertEquals("hi html", $msg->content); - $c->stop(); }); - $c->disconnect(); + $ch->publish("hi html", ["content-type" => "text/html"], "", "test_queue"); $this->assertTrue($c->isConnected()); - $this->helper->disconnectClientWithEventLoop($c); + $c->disconnect(); $this->assertFalse($c->isConnected()); } @@ -158,15 +137,13 @@ public function testBigMessage() $ch = $c->connect()->channel(); $ch->queueDeclare("test_queue", false, false, false, true); - $ch->publish($body, [], "", "test_queue"); - $ch->run(function (Message $msg, Channel $ch, Client $c) use ($body) { + $ch->consume(function (Message $msg, Channel $ch, Client $c) use ($body) { $this->assertEquals($body, $msg->content); - $c->stop(); }); - $c->disconnect(); + $ch->publish($body, [], "", "test_queue"); $this->assertTrue($c->isConnected()); - $this->helper->disconnectClientWithEventLoop($c); + $c->disconnect(); $this->assertFalse($c->isConnected()); } } diff --git a/test/Bunny/ClientTest.php b/test/Bunny/ClientTest.php index b3a58c1..67b6487 100644 --- a/test/Bunny/ClientTest.php +++ b/test/Bunny/ClientTest.php @@ -14,13 +14,19 @@ use Bunny\Test\Library\Paths; use Bunny\Test\Library\SynchronousClientHelper; use PHPUnit\Framework\TestCase; -use Symfony\Component\Process\Exception\ProcessFailedException; -use Symfony\Component\Process\Process; - +use React\ChildProcess\Process; +use React\EventLoop\Loop; +use React\Promise\Promise; +use WyriHaximus\React\PHPUnit\RunTestsInFibersTrait; +use function React\Async\async; +use function React\Async\await; +use function React\Promise\Stream\buffer; use const SIGINT; class ClientTest extends TestCase { + use RunTestsInFibersTrait; + /** * @var SynchronousClientHelper */ @@ -70,7 +76,7 @@ public function testOpenChannel() $this->assertInstanceOf(Channel::class, $channel); $this->assertTrue($client->isConnected()); - $this->helper->disconnectClientWithEventLoop($client); + $client->disconnect(); $this->assertFalse($client->isConnected()); } @@ -86,20 +92,10 @@ public function testOpenMultipleChannel() $this->assertNotEquals($ch2->getChannelId(), $ch3->getChannelId()); $this->assertTrue($client->isConnected()); - $this->helper->disconnectClientWithEventLoop($client); + $client->disconnect(); $this->assertFalse($client->isConnected()); } - public function testRunMaxSeconds() - { - $client = $this->helper->createClient(); - $client->connect(); - $s = microtime(true); - $client->run(1.0); - $e = microtime(true); - $this->assertLessThan(2.0, $e - $s); - } - public function testDisconnectWithBufferedMessages() { $client = $this->helper->createClient(); @@ -110,27 +106,25 @@ public function testDisconnectWithBufferedMessages() $channel->qos(0, 1000); $channel->queueDeclare("disconnect_test"); - $channel->consume(function (Message $message, Channel $channel) use ($client, &$processed) { + $channel->consume(async(function (Message $message, Channel $channel) use ($client, &$processed) { $channel->ack($message); ++$processed; - $client->disconnect()->done(function () use ($client) { - $client->stop(); - }); - }); + $client->disconnect(); + })); $channel->publish(".", [], "", "disconnect_test"); $channel->publish(".", [], "", "disconnect_test"); $channel->publish(".", [], "", "disconnect_test"); - $client->run(5); + await(\React\Promise\Timer\sleep(5)); $this->assertEquals(1, $processed); $this->assertFalse($client->isConnected()); // Clean-up Queue $client = $this->helper->createClient(); - $client->connect(); $channel = $client->channel(); $channel->queueDelete("disconnect_test"); + $client->disconnect(); } /** @@ -142,24 +136,24 @@ public function testStopConsumerWithSigInt() $path = Paths::getTestsRootPath() . '/scripts/bunny-consumer.php'; - $process = new Process([$path, Environment::getTestRabbitMqConnectionUri(), $queueName, '0']); + $process = new Process($path . ' ' . Environment::getTestRabbitMqConnectionUri() . ' ' .$queueName . ' ' . '0'); - $process->start(); - - $signalSent = false; - $starttime = microtime(true); + Loop::futureTick(static function () use ($process): void { + $process->start(); + }); // Send SIGINT after 1.0 seconds - while ($process->isRunning()) { - if (!$signalSent && microtime(true) > $starttime + 1.0) { - $process->signal(SIGINT); - $signalSent = true; - } + Loop::addTimer(1, static function () use ($process): void { + $process->terminate(SIGINT); + }); - usleep(10000); - } + $termination = new Promise(static function (callable $resolve) use ($process): void { + $process->on('exit', static function ($code) use ($resolve): void { + $resolve($code === 0); + }); + }); - self::assertTrue($process->isSuccessful(), $process->getOutput() . "\n" . $process->getErrorOutput()); + self::assertTrue(await($termination), await(buffer($process->stdout)) . "\n" . await(buffer($process->stderr))); } public function testGet() @@ -183,25 +177,24 @@ public function testGet() $channel->publish("..", [], "", "get_test"); $channel->get("get_test"); - $client->disconnect()->then(function () use ($client) { - $client->connect(); + $client->disconnect(); + + await(\React\Promise\Timer\sleep(5)); - $channel = $client->channel(); - $message3 = $channel->get("get_test"); - $this->assertNotNull($message3); - $this->assertInstanceOf(Message::class, $message3); - $this->assertEquals($message3->exchange, ""); - $this->assertEquals($message3->content, ".."); + $client->connect(); - $channel->ack($message3); + $channel = $client->channel(); + $message3 = $channel->get("get_test"); + $this->assertNotNull($message3); + $this->assertInstanceOf(Message::class, $message3); + $this->assertEquals($message3->exchange, ""); + $this->assertEquals($message3->content, ".."); - return $client->disconnect(); + $channel->ack($message3); - })->then(function () use ($client) { - $client->stop(); - })->done(); + $client->disconnect(); - $client->run(5); + await(\React\Promise\Timer\sleep(5)); $this->assertFalse($client->isConnected()); } @@ -214,24 +207,19 @@ public function testReturn() /** @var Message $returnedMessage */ $returnedMessage = null; - /** @var MethodBasicReturnFrame $returnedFrame */ - $returnedFrame = null; $channel->addReturnListener(function ( Message $message, MethodBasicReturnFrame $frame ) use ( $client, - &$returnedMessage, - &$returnedFrame + &$returnedMessage ) { $returnedMessage = $message; - $returnedFrame = $frame; - $client->stop(); }); $channel->publish("xxx", [], "", "404", true); - $client->run(1); + await(\React\Promise\Timer\sleep(1)); $this->assertNotNull($returnedMessage); $this->assertInstanceOf(Message::class, $returnedMessage); @@ -240,7 +228,7 @@ public function testReturn() $this->assertEquals("404", $returnedMessage->routingKey); $this->assertTrue($client->isConnected()); - $this->helper->disconnectClientWithEventLoop($client); + $client->disconnect(); $this->assertFalse($client->isConnected()); } @@ -267,7 +255,7 @@ public function testTxs() $this->assertNull($nothing); $this->assertTrue($client->isConnected()); - $this->helper->disconnectClientWithEventLoop($client); + $client->disconnect(); $this->assertFalse($client->isConnected()); } @@ -281,6 +269,10 @@ public function testTxSelectCannotBeCalledMultipleTimes() $channel->txSelect(); $channel->txSelect(); + + $this->assertTrue($client->isConnected()); + $client->disconnect(); + $this->assertFalse($client->isConnected()); } public function testConfirmMode() @@ -290,21 +282,19 @@ public function testConfirmMode() $channel = $client->channel(); $deliveryTag = null; - $channel->confirmSelect(function (MethodBasicAckFrame $frame) use (&$deliveryTag, $client) { + $channel->confirmSelect(async(function (MethodBasicAckFrame $frame) use (&$deliveryTag, $client) { if ($frame->deliveryTag === $deliveryTag) { $deliveryTag = null; - $client->stop(); + $client->disconnect(); } - }); + })); - $deliveryTag = $channel->publish("."); + $deliveryTag = $channel->publish("tst_cfm_m"); - $client->run(1); + await(\React\Promise\Timer\sleep(1)); $this->assertNull($deliveryTag); - $this->assertTrue($client->isConnected()); - $this->helper->disconnectClientWithEventLoop($client); $this->assertFalse($client->isConnected()); } @@ -323,22 +313,20 @@ public function testEmptyMessage() $processed = 0; $channel->consume( - function (Message $message, Channel $channel) use ($client, &$processed) { + async(function (Message $message, Channel $channel) use ($client, &$processed) { $this->assertEmpty($message->content); $channel->ack($message); if (++$processed === 2) { - $client->disconnect()->done(function () use ($client) { - $client->stop(); - }); + $client->disconnect(); } - }, + }), "empty_body_message_test" ); $channel->publish("", [], "", "empty_body_message_test"); $channel->publish("", [], "", "empty_body_message_test"); - $client->run(1); + await(\React\Promise\Timer\sleep(0.01)); $this->assertFalse($client->isConnected()); } @@ -349,7 +337,7 @@ public function testHeartBeatCallback() $options = $this->helper->getDefaultOptions(); - $options['heartbeat'] = 1.0; + $options['heartbeat'] = 0.1; $options['heartbeat_callback'] = function () use (&$called) { $called += 1; }; @@ -357,7 +345,9 @@ public function testHeartBeatCallback() $client = $this->helper->createClient($options); $client->connect(); - $client->run(2); + + await(\React\Promise\Timer\sleep(0.2)); + $client->disconnect(); $this->assertGreaterThan(0, $called); diff --git a/test/Bunny/SSLTest.php b/test/Bunny/SSLTest.php index 19ecc7a..f8048e9 100644 --- a/test/Bunny/SSLTest.php +++ b/test/Bunny/SSLTest.php @@ -2,16 +2,10 @@ namespace Bunny; -use Bunny\Async\Client as AsyncClient; use Bunny\Exception\ClientException; -use Bunny\Test\Exception\TimeoutException; -use Bunny\Test\Library\AsynchronousClientHelper; use Bunny\Test\Library\Environment; use Bunny\Test\Library\SynchronousClientHelper; use PHPUnit\Framework\TestCase; - -use React\EventLoop\Factory; - use function dirname; use function file_exists; use function is_file; @@ -24,17 +18,11 @@ class SSLTest extends TestCase */ private $helper; - /** - * @var AsynchronousClientHelper - */ - private $asyncHelper; - protected function setUp(): void { parent::setUp(); $this->helper = new SynchronousClientHelper(); - $this->asyncHelper = new AsynchronousClientHelper(); } public function testConnect() @@ -48,26 +36,6 @@ public function testConnect() $this->assertTrue(true); } - public function testConnectAsync() { - $options = $this->getOptions(); - $loop = Factory::create(); - - $loop->addTimer(5, function () { - throw new TimeoutException(); - }); - - $client = $this->asyncHelper->createClient($loop, $options); - $client->connect()->then(function (AsyncClient $client) { - return $client->disconnect(); - })->then(function () use ($loop) { - $loop->stop(); - })->done(); - - $loop->run(); - - $this->assertTrue(true); - } - public function testConnectWithMissingClientCert() { $options = $this->getOptions(); diff --git a/test/Library/AsynchronousClientHelper.php b/test/Library/AsynchronousClientHelper.php deleted file mode 100644 index 1adb13a..0000000 --- a/test/Library/AsynchronousClientHelper.php +++ /dev/null @@ -1,36 +0,0 @@ -getDefaultOptions(), $options ?? []); - - return new Client($loop, $options); - } - - /** - * @return array - */ - public function getDefaultOptions(): array - { - $options = []; - - $options = array_merge($options, parseAmqpUri(Environment::getTestRabbitMqConnectionUri())); - - return $options; - } -} diff --git a/test/Library/SynchronousClientHelper.php b/test/Library/SynchronousClientHelper.php index fefdeda..c6fc506 100644 --- a/test/Library/SynchronousClientHelper.php +++ b/test/Library/SynchronousClientHelper.php @@ -21,51 +21,6 @@ public function createClient(array $options = null): Client return new Client($options); } - /** - * Disconnects a synchronous client in a reliable way - * - * Calling just `Client::disconnect` instead of running the code in this - * method does only work as (probably) expected (ie. actually close the - * connection to the broker) if the client does not have any open channels. - * Otherwise, `Client::disconnect` waits for `Channel::close` on open - * channels to complete which is promise-based and needs a running event - * loop (`Client::run`) to be able to fulfill its promise. If there is no - * running event loop after calling `Client::disconnect`, the client will - * stay connected until its destructor is called or the connection is - * closed by the broker (eg. because of missing a heartbeat event). - * - * The code in this method contains the same logic as `Client::__destruct`. - * - * See this discussion for more details: - * - * - https://github.com/jakubkulhan/bunny/issues/93 - * - * @param Client $client - * - * @return void - */ - public function disconnectClientWithEventLoop(Client $client) - { - if (!$client->isConnected()) { - return; - } - - /** @var Promise $disconnectPromise */ - $disconnectPromise = $client->disconnect(); - - $disconnectPromise->done( - function () use ($client) { - $client->stop(); - } - ); - - if (!$client->isConnected()) { - return; - } - - $client->run(); - } - /** * @return array */ diff --git a/test/scripts/bunny-consumer.php b/test/scripts/bunny-consumer.php index 95995e6..1d50fe2 100755 --- a/test/scripts/bunny-consumer.php +++ b/test/scripts/bunny-consumer.php @@ -10,7 +10,7 @@ use Bunny\Channel; use Bunny\Client; use Bunny\Message; - +use React\EventLoop\Loop; use function Bunny\Test\Library\parseAmqpUri; require __DIR__ . '/../../vendor/autoload.php'; @@ -22,9 +22,7 @@ function app(array $args) $client = new Client($connection); pcntl_signal(SIGINT, function () use ($client) { - $client->disconnect()->done(function () use ($client) { - $client->stop(); - }); + $client->disconnect(); }); $client->connect(); @@ -35,7 +33,9 @@ function app(array $args) $channel->consume(function (Message $message, Channel $channel) use ($client) { $channel->ack($message); }); - $client->run($args['maxSeconds'] > 0 ? $args['maxSeconds'] : null); + Loop::addTimer($args['maxSeconds'], static function () use ($client): void { + $client->disconnect(); + }); } $argv_copy = $argv; diff --git a/tutorial/1-hello-world/receive-async.php b/tutorial/1-hello-world/receive-async.php deleted file mode 100644 index 9081422..0000000 --- a/tutorial/1-hello-world/receive-async.php +++ /dev/null @@ -1,31 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) { - return $channel->queueDeclare('hello', false, false, false, false)->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) { - echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; - $channel->consume( - function (Message $message, Channel $channel, Client $client) { - echo " [x] Received ", $message->content, "\n"; - }, - 'hello', - '', - false, - true - ); -}); - -$loop->run(); diff --git a/tutorial/1-hello-world/receive.php b/tutorial/1-hello-world/receive.php index 27841ca..d4afd84 100644 --- a/tutorial/1-hello-world/receive.php +++ b/tutorial/1-hello-world/receive.php @@ -3,22 +3,25 @@ use Bunny\Channel; use Bunny\Client; use Bunny\Message; +use React\EventLoop\Loop; +use function React\Async\async; -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; -$client = (new Client())->connect(); +$client = new Client(); $channel = $client->channel(); - +$channel->qos(0, 100); $channel->queueDeclare('hello', false, false, false, false); echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; -$channel->run( - function (Message $message, Channel $channel, Client $client) { +$channel->consume( + function (Message $message, Channel $channel) { echo " [x] Received ", $message->content, "\n"; + $channel->ack($message); }, 'hello', '', false, - true + false, ); diff --git a/tutorial/1-hello-world/send-async.php b/tutorial/1-hello-world/send-async.php deleted file mode 100644 index 9cc9263..0000000 --- a/tutorial/1-hello-world/send-async.php +++ /dev/null @@ -1,32 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) { - return $channel->queueDeclare('hello', false, false, false, false)->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) { - echo " [x] Sending 'Hello World!'\n"; - return $channel->publish('Hello World!', [], '', 'hello')->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) { - echo " [x] Sent 'Hello World!'\n"; - $client = $channel->getClient(); - return $channel->close()->then(function () use ($client) { - return $client; - }); -})->then(function (Client $client) { - $client->disconnect(); -}); - -$loop->run(); \ No newline at end of file diff --git a/tutorial/1-hello-world/send.php b/tutorial/1-hello-world/send.php index 699c618..020897d 100644 --- a/tutorial/1-hello-world/send.php +++ b/tutorial/1-hello-world/send.php @@ -1,16 +1,41 @@ connect(); +$client = new Client(); $channel = $client->channel(); - $channel->queueDeclare('hello', false, false, false, false); +$channel->close(); -$channel->publish('Hello World!', [], '', 'hello'); -echo " [x] Sent 'Hello World!'\n"; +$keepRunning = true; -$channel->close(); -$client->disconnect(); +Loop::futureTick(async(function () use ($client, &$keepRunning) { + $channel = $client->channel(); + while ($keepRunning) { + $channel->publish('Hello World!', [], '', 'hello'); + } +})); + +//$timer = Loop::addPeriodicTimer(60, static function () use ($client): void { +// file_put_contents('var/client.' . time() . '.stop.txt', var_export($client, true)); +// file_put_contents('var/loop.' . time() . '.stop.txt', var_export(Loop::get(), true)); +//}); + +Loop::addTimer(60 * 60 * 6, static function () use (&$keepRunning, $client): void { + $keepRunning = false; + file_put_contents('var/client.pre.stop.txt', var_export($client, true)); + file_put_contents('var/loop.pre.stop.txt', var_export(Loop::get(), true)); +}); + +Loop::addTimer((60 * 60 * 6) + 3, static function () use ($client/*, $timer*/): void { + file_put_contents('var/client.post.stop.txt', var_export($client, true)); + file_put_contents('var/loop.post.stop.txt', var_export(Loop::get(), true)); + +// Loop::cancelTimer($timer); +}); diff --git a/tutorial/2-work-queues/new_task-async.php b/tutorial/2-work-queues/new_task-async.php deleted file mode 100644 index 4bc918b..0000000 --- a/tutorial/2-work-queues/new_task-async.php +++ /dev/null @@ -1,40 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) { - return $channel->queueDeclare('task_queue', false, true, false, false)->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) use ($data) { - echo " [x] Sending '{$data}'\n"; - return $channel->publish( - $data, - [ - 'delivery-mode' => 2 - ], - '', - 'task_queue' - )->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) use ($data) { - echo " [x] Sent '{$data}'\n"; - $client = $channel->getClient(); - return $channel->close()->then(function () use ($client) { - return $client; - }); -})->then(function (Client $client) { - $client->disconnect(); -}); - -$loop->run(); diff --git a/tutorial/2-work-queues/new_task.php b/tutorial/2-work-queues/new_task.php index 81ecbc3..16a70c6 100644 --- a/tutorial/2-work-queues/new_task.php +++ b/tutorial/2-work-queues/new_task.php @@ -2,9 +2,9 @@ use Bunny\Client; -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; -$client = (new Client())->connect(); +$client = new Client(); $channel = $client->channel(); $channel->queueDeclare('task_queue', false, true, false, false); diff --git a/tutorial/2-work-queues/worker-async.php b/tutorial/2-work-queues/worker-async.php deleted file mode 100644 index edbda8e..0000000 --- a/tutorial/2-work-queues/worker-async.php +++ /dev/null @@ -1,35 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) { - return $channel->qos(0, 1)->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) { - return $channel->queueDeclare('task_queue', false, true, false, false)->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) { - echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; - $channel->consume( - function (Message $message, Channel $channel, Client $client) { - echo " [x] Received ", $message->content, "\n"; - sleep(substr_count($message->content, '.')); - echo " [x] Done", $message->content, "\n"; - $channel->ack($message); - }, - 'task_queue' - ); -}); - -$loop->run(); diff --git a/tutorial/2-work-queues/worker.php b/tutorial/2-work-queues/worker.php index 7db87d4..d90a49f 100644 --- a/tutorial/2-work-queues/worker.php +++ b/tutorial/2-work-queues/worker.php @@ -4,9 +4,9 @@ use Bunny\Client; use Bunny\Message; -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; -$client = (new Client())->connect(); +$client = new Client(); $channel = $client->channel(); $channel->queueDeclare('task_queue', false, true, false, false); @@ -14,7 +14,7 @@ echo ' [*] Waiting for messages. To exit press CTRL+C', "\n"; $channel->qos(0, 1); -$channel->run( +$channel->consume( function (Message $message, Channel $channel, Client $client) { echo " [x] Received ", $message->content, "\n"; sleep(substr_count($message->content, '.')); diff --git a/tutorial/3-publish-subscribe/emit_log-async.php b/tutorial/3-publish-subscribe/emit_log-async.php deleted file mode 100644 index af476e1..0000000 --- a/tutorial/3-publish-subscribe/emit_log-async.php +++ /dev/null @@ -1,37 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) { - return $channel->exchangeDeclare('logs', 'fanout')->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) use ($data) { - echo " [x] Sending '{$data}'\n"; - return $channel->publish($data, [], 'logs')->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) use ($data) { - echo " [x] Sent '{$data}'\n"; - $client = $channel->getClient(); - return $channel->close()->then(function () use ($client) { - return $client; - }); -})->then(function (Client $client) { - $client->disconnect(); -}); - -$loop->run(); diff --git a/tutorial/3-publish-subscribe/emit_log.php b/tutorial/3-publish-subscribe/emit_log.php index 64ed8c3..48b4199 100644 --- a/tutorial/3-publish-subscribe/emit_log.php +++ b/tutorial/3-publish-subscribe/emit_log.php @@ -2,9 +2,9 @@ use Bunny\Client; -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; -$client = (new Client())->connect(); +$client = new Client(); $channel = $client->channel(); $channel->exchangeDeclare('logs', 'fanout'); diff --git a/tutorial/3-publish-subscribe/receive_logs-async.php b/tutorial/3-publish-subscribe/receive_logs-async.php deleted file mode 100644 index 81d45b2..0000000 --- a/tutorial/3-publish-subscribe/receive_logs-async.php +++ /dev/null @@ -1,36 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) { - return $channel->exchangeDeclare('logs', 'fanout')->then(function () use ($channel) { - return $channel->queueDeclare('', false, false, true, false); - })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel) { - return $channel->queueBind($frame->queue, 'logs')->then(function () use ($frame) { - return $frame; - }); - })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel) { - echo ' [*] Waiting for logs. To exit press CTRL+C', "\n"; - $channel->consume( - function (Message $message, Channel $channel, Client $client) { - echo ' [x] ', $message->content, "\n"; - }, - $frame->queue, - '', - false, - true - ); - }); -}); - -$loop->run(); diff --git a/tutorial/3-publish-subscribe/receive_logs.php b/tutorial/3-publish-subscribe/receive_logs.php index d696ac3..e297017 100644 --- a/tutorial/3-publish-subscribe/receive_logs.php +++ b/tutorial/3-publish-subscribe/receive_logs.php @@ -4,9 +4,9 @@ use Bunny\Client; use Bunny\Message; -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; -$client = (new Client())->connect(); +$client = new Client(); $channel = $client->channel(); $channel->exchangeDeclare('logs', 'fanout'); @@ -15,7 +15,7 @@ echo ' [*] Waiting for logs. To exit press CTRL+C', "\n"; -$channel->run( +$channel->consume( function (Message $message, Channel $channel, Client $client) { echo ' [x] ', $message->content, "\n"; }, diff --git a/tutorial/4-routing/emit_log-async.php b/tutorial/4-routing/emit_log-async.php deleted file mode 100644 index 46767aa..0000000 --- a/tutorial/4-routing/emit_log-async.php +++ /dev/null @@ -1,38 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) { - return $channel->exchangeDeclare('direct_logs', 'direct')->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) use ($data, $severity) { - echo " [x] Sending ",$severity,':',$data," \n"; - return $channel->publish($data, [], 'direct_logs', $severity)->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) use ($data, $severity) { - echo " [x] Sent ",$severity,':',$data," \n"; - $client = $channel->getClient(); - return $channel->close()->then(function () use ($client) { - return $client; - }); -})->then(function (Client $client) { - $client->disconnect(); -}); - -$loop->run(); diff --git a/tutorial/4-routing/emit_log.php b/tutorial/4-routing/emit_log.php index 7fd9df9..f36f18a 100644 --- a/tutorial/4-routing/emit_log.php +++ b/tutorial/4-routing/emit_log.php @@ -2,9 +2,9 @@ use Bunny\Client; -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; -$client = (new Client())->connect(); +$client = new Client(); $channel = $client->channel(); $channel->exchangeDeclare('direct_logs', 'direct'); diff --git a/tutorial/4-routing/receive_logs-async.php b/tutorial/4-routing/receive_logs-async.php deleted file mode 100644 index 2f0bdf7..0000000 --- a/tutorial/4-routing/receive_logs-async.php +++ /dev/null @@ -1,48 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) use ($severities) { - return $channel->exchangeDeclare('direct_logs', 'direct')->then(function () use ($channel, $severities) { - return $channel->queueDeclare('', false, false, true, false); - })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel, $severities) { - $promises = []; - - foreach($severities as $severity) { - $promises[] = $channel->queueBind($frame->queue, 'direct_logs', $severity); - } - - return \React\Promise\all($promises)->then(function () use ($frame) { - return $frame; - }); - })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel) { - echo ' [*] Waiting for logs. To exit press CTRL+C', "\n"; - $channel->consume( - function (Message $message, Channel $channel, Client $client) { - echo ' [x] ', $message->content, "\n"; - }, - $frame->queue, - '', - false, - true - ); - }); -}); - -$loop->run(); diff --git a/tutorial/4-routing/receive_logs.php b/tutorial/4-routing/receive_logs.php index 184df66..7bc6dd8 100644 --- a/tutorial/4-routing/receive_logs.php +++ b/tutorial/4-routing/receive_logs.php @@ -4,9 +4,9 @@ use Bunny\Client; use Bunny\Message; -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; -$client = (new Client())->connect(); +$client = new Client(); $channel = $client->channel(); $channel->exchangeDeclare('direct_logs', 'direct'); @@ -15,6 +15,7 @@ $severities = array_slice($argv, 1); if(empty($severities )) { file_put_contents('php://stderr', "Usage: $argv[0] [info] [warning] [error]\n"); + $client->disconnect(); exit(1); } @@ -24,7 +25,7 @@ echo ' [*] Waiting for logs. To exit press CTRL+C', "\n"; -$channel->run( +$channel->consume( function (Message $message, Channel $channel, Client $client) { echo ' [x] ', $message->routingKey, ':', $message->content, "\n"; }, diff --git a/tutorial/5-topics/emit_log_topic-async.php b/tutorial/5-topics/emit_log_topic-async.php deleted file mode 100644 index 149b809..0000000 --- a/tutorial/5-topics/emit_log_topic-async.php +++ /dev/null @@ -1,38 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) { - return $channel->exchangeDeclare('topic_logs', 'topic')->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) use ($data, $routing_key) { - echo " [x] Sending ", $routing_key, ':', $data, " \n"; - return $channel->publish($data, [], 'topic_logs', $routing_key)->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) use ($data, $routing_key) { - echo " [x] Sent ", $routing_key, ':', $data, " \n"; - $client = $channel->getClient(); - return $channel->close()->then(function () use ($client) { - return $client; - }); -})->then(function (Client $client) { - $client->disconnect(); -}); - -$loop->run(); diff --git a/tutorial/5-topics/emit_log_topic.php b/tutorial/5-topics/emit_log_topic.php index 09c3e26..3c08ac1 100644 --- a/tutorial/5-topics/emit_log_topic.php +++ b/tutorial/5-topics/emit_log_topic.php @@ -2,9 +2,9 @@ use Bunny\Client; -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; -$client = (new Client())->connect(); +$client = new Client(); $channel = $client->channel(); $channel->exchangeDeclare('topic_logs', 'topic'); diff --git a/tutorial/5-topics/receive_logs_topic-async.php b/tutorial/5-topics/receive_logs_topic-async.php deleted file mode 100644 index 1e483b9..0000000 --- a/tutorial/5-topics/receive_logs_topic-async.php +++ /dev/null @@ -1,48 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) use ($binding_keys) { - return $channel->exchangeDeclare('topic_logs', 'topic')->then(function () use ($channel, $binding_keys) { - return $channel->queueDeclare('', false, false, true, false); - })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel, $binding_keys) { - $promises = []; - - foreach($binding_keys as $binding_key) { - $promises[] = $channel->queueBind($frame->queue, 'topic_logs', $binding_key); - } - - return \React\Promise\all($promises)->then(function () use ($frame) { - return $frame; - }); - })->then(function (MethodQueueDeclareOkFrame $frame) use ($channel) { - echo ' [*] Waiting for logs. To exit press CTRL+C', "\n"; - $channel->consume( - function (Message $message, Channel $channel, Client $client) { - echo ' [x] ', $message->content, "\n"; - }, - $frame->queue, - '', - false, - true - ); - }); -}); - -$loop->run(); diff --git a/tutorial/5-topics/receive_logs_topic.php b/tutorial/5-topics/receive_logs_topic.php index 958f546..3da20b3 100644 --- a/tutorial/5-topics/receive_logs_topic.php +++ b/tutorial/5-topics/receive_logs_topic.php @@ -4,9 +4,9 @@ use Bunny\Client; use Bunny\Message; -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; -$client = (new Client())->connect(); +$client = new Client(); $channel = $client->channel(); $channel->exchangeDeclare('topic_logs', 'topic'); @@ -15,6 +15,7 @@ $binding_keys = array_slice($argv, 1); if(empty($binding_keys )) { file_put_contents('php://stderr', "Usage: $argv[0] [binding_key]\n"); + $client->disconnect(); exit(1); } @@ -24,7 +25,7 @@ echo ' [*] Waiting for logs. To exit press CTRL+C', "\n"; -$channel->run( +$channel->consume( function (Message $message, Channel $channel, Client $client) { echo ' [x] ', $message->routingKey, ':', $message->content, "\n"; }, diff --git a/tutorial/6-rpc/rpc_client-async.php b/tutorial/6-rpc/rpc_client-async.php deleted file mode 100644 index feb9173..0000000 --- a/tutorial/6-rpc/rpc_client-async.php +++ /dev/null @@ -1,65 +0,0 @@ -channel = (new Client($loop))->connect()->then(function (Client $client) { - return $client->channel(); - }); - } - - public function call($n) - { - return $this->channel->then(function (Channel $channel) { - return \React\Promise\all([ - $channel->queueDeclare('', false, false, true), - \React\Promise\resolve($channel), - ]); - })->then(function ($values) use ($n) { - list ($responseQueue, $channel) = $values; - $corr_id = uniqid(); - $deferred = new Deferred(); - $channel->consume( - function (Message $message, Channel $channel, Client $client) use ($deferred, $corr_id) { - if ($message->getHeader('correlation_id') != $corr_id) { - return; - } - $deferred->resolve((int)$message->content); - $client->disconnect(); - }, - $responseQueue->queue - ); - $channel->publish( - $n, - [ - 'correlation_id' => $corr_id, - 'reply_to' => $responseQueue->queue, - ], - '', - 'rpc_queue' - ); - return $deferred->promise(); - }); - } -} - -$fibonacci_rpc = new FibonacciRpcClient($loop); -$response = $fibonacci_rpc->call(30)->then(function ($n) { - echo " [.] Got ", $n, "\n"; -}); - -$loop->run(); diff --git a/tutorial/6-rpc/rpc_client.php b/tutorial/6-rpc/rpc_client.php index debb418..5b2b433 100644 --- a/tutorial/6-rpc/rpc_client.php +++ b/tutorial/6-rpc/rpc_client.php @@ -3,8 +3,12 @@ use Bunny\Channel; use Bunny\Client; use Bunny\Message; +use React\EventLoop\Loop; +use React\Promise\Deferred; +use function React\Async\async; +use function React\Async\await; -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; class FibonacciRpcClient { @@ -13,21 +17,27 @@ class FibonacciRpcClient public function __construct() { - $this->client = (new Client())->connect(); + $this->client = new Client(); $this->channel = $this->client->channel(); } + public function close() + { + $this->client->disconnect(); + } + public function call($n) { $corr_id = uniqid(); - $response = null; + $response = new Deferred(); $responseQueue = $this->channel->queueDeclare('', false, false, true); - $this->channel->consume( - function (Message $message, Channel $channel, Client $client) use (&$response, $corr_id) { + $subscription = $this->channel->consume( + function (Message $message, Channel $channel, Client $client) use (&$response, $corr_id, &$subscription) { if ($message->getHeader('correlation_id') != $corr_id) { return; } - $response = $message->content; + $response->resolve($message->content); + $channel->cancel($subscription->consumerTag); }, $responseQueue->queue ); @@ -40,13 +50,12 @@ function (Message $message, Channel $channel, Client $client) use (&$response, $ '', 'rpc_queue' ); - while ($response === null) { - $this->client->run(0.01); - } - return (int) $response; + + return (int) await($response->promise()); } } $fibonacci_rpc = new FibonacciRpcClient(); $response = $fibonacci_rpc->call(30); echo " [.] Got ", $response, "\n"; +$fibonacci_rpc->close(); diff --git a/tutorial/6-rpc/rpc_server-async.php b/tutorial/6-rpc/rpc_server-async.php deleted file mode 100644 index 4d8de0a..0000000 --- a/tutorial/6-rpc/rpc_server-async.php +++ /dev/null @@ -1,48 +0,0 @@ -connect()->then(function (Client $client) { - return $client->channel(); -})->then(function (Channel $channel) { - return $channel->queueDeclare('rpc_queue')->then(function () use ($channel) { - return $channel; - }); -})->then(function (Channel $channel) { - echo " [x] Awaiting RPC requests\n"; - $channel->consume( - function (Message $message, Channel $channel, Client $client) { - $n = intval($message->content); - echo " [.] fib(", $n, ")\n"; - $channel->publish( - (string) fib($n), - [ - 'correlation_id' => $message->getHeader('correlation_id'), - ], - '', - $message->getHeader('reply_to') - )->then(function () use ($channel, $message) { - $channel->ack($message); - }); - }, - 'rpc_queue' - ); -}); - -$loop->run(); diff --git a/tutorial/6-rpc/rpc_server.php b/tutorial/6-rpc/rpc_server.php index 3fb83ce..0828e31 100644 --- a/tutorial/6-rpc/rpc_server.php +++ b/tutorial/6-rpc/rpc_server.php @@ -3,6 +3,8 @@ use Bunny\Channel; use Bunny\Client; use Bunny\Message; +use React\EventLoop\Loop; +use function React\Async\async; function fib($n) { if ($n == 0) @@ -12,21 +14,21 @@ function fib($n) { return fib($n-1) + fib($n-2); } -require '../../vendor/autoload.php'; +require dirname(__DIR__, 2) . '/vendor/autoload.php'; -$client = (new Client())->connect(); +$client = new Client(); $channel = $client->channel(); $channel->queueDeclare('rpc_queue'); echo " [x] Awaiting RPC requests\n"; -$channel->run( +$channel->consume( function (Message $message, Channel $channel, Client $client) { $n = intval($message->content); echo " [.] fib(", $n, ")\n"; $channel->publish( - (string) fib($n), + (string)fib($n), [ 'correlation_id' => $message->getHeader('correlation_id'), ],