From 9b3b018df83d5de5538bb0161870bf4dbe212a8a Mon Sep 17 00:00:00 2001 From: Anton Shabouta Date: Fri, 4 Jan 2019 03:11:14 +0300 Subject: [PATCH] Use amphp sockets && refactoring --- README.md | 28 +- benchmark/consumer.php | 40 + benchmark/producer.php | 51 + composer.json | 3 +- composer.lock | 668 +++++++++- src/Buffer.php | 299 +++-- src/Channel.php | 1443 +++++++++++++++------ src/ChannelAwaiter.php | 291 ----- src/Client.php | 1190 +---------------- src/Config.php | 38 +- src/Connection.php | 337 +++++ src/ConnectionAwaiter.php | 148 --- src/Constants.php | 388 +++--- src/Exception/BufferUnderflow.php | 2 +- src/Exception/ChannelException.php | 9 +- src/Exception/ClassInvalid.php | 22 +- src/Exception/ClientException.php | 33 +- src/Exception/MethodInvalid.php | 36 +- src/Exception/ProtocolException.php | 68 +- src/Exception/RidgeException.php | 17 + src/Heartbeat.php | 97 ++ src/Message.php | 128 +- src/Protocol/AbstractFrame.php | 18 - src/Protocol/AccessRequestFrame.php | 8 + src/Protocol/AccessRequestOkFrame.php | 15 +- src/Protocol/BasicAckFrame.php | 15 +- src/Protocol/BasicCancelFrame.php | 15 +- src/Protocol/BasicCancelOkFrame.php | 15 +- src/Protocol/BasicConsumeFrame.php | 15 +- src/Protocol/BasicConsumeOkFrame.php | 21 +- src/Protocol/BasicDeliverFrame.php | 37 +- src/Protocol/BasicGetEmptyFrame.php | 21 +- src/Protocol/BasicGetFrame.php | 29 +- src/Protocol/BasicGetOkFrame.php | 15 +- src/Protocol/BasicNackFrame.php | 29 +- src/Protocol/BasicPublishFrame.php | 37 +- src/Protocol/BasicQosFrame.php | 29 +- src/Protocol/BasicQosOkFrame.php | 17 +- src/Protocol/BasicRecoverAsyncFrame.php | 21 +- src/Protocol/BasicRecoverFrame.php | 21 +- src/Protocol/BasicRecoverOkFrame.php | 21 +- src/Protocol/BasicRejectFrame.php | 15 +- src/Protocol/BasicReturnFrame.php | 33 +- src/Protocol/ChannelCloseFrame.php | 33 +- src/Protocol/ChannelCloseOkFrame.php | 17 +- src/Protocol/ChannelFlowFrame.php | 21 +- src/Protocol/ChannelFlowOkFrame.php | 21 +- src/Protocol/ChannelOpenFrame.php | 21 +- src/Protocol/ChannelOpenOkFrame.php | 15 +- src/Protocol/ConfirmSelectFrame.php | 21 +- src/Protocol/ConfirmSelectOkFrame.php | 17 +- src/Protocol/ConnectionBlockedFrame.php | 21 +- src/Protocol/ConnectionCloseFrame.php | 34 +- src/Protocol/ConnectionCloseOkFrame.php | 18 +- src/Protocol/ConnectionOpenFrame.php | 30 +- src/Protocol/ConnectionOpenOkFrame.php | 22 +- src/Protocol/ConnectionSecureFrame.php | 22 +- src/Protocol/ConnectionSecureOkFrame.php | 22 +- src/Protocol/ConnectionStartFrame.php | 38 +- src/Protocol/ConnectionStartOkFrame.php | 34 +- src/Protocol/ConnectionTuneFrame.php | 30 +- src/Protocol/ConnectionTuneOkFrame.php | 30 +- src/Protocol/ConnectionUnblockedFrame.php | 18 +- src/Protocol/ContentBodyFrame.php | 20 +- src/Protocol/ContentHeaderFrame.php | 202 +-- src/Protocol/ExchangeBindFrame.php | 41 +- src/Protocol/ExchangeBindOkFrame.php | 17 +- src/Protocol/ExchangeDeclareFrame.php | 53 +- src/Protocol/ExchangeDeclareOkFrame.php | 17 +- src/Protocol/ExchangeDeleteFrame.php | 33 +- src/Protocol/ExchangeDeleteOkFrame.php | 17 +- src/Protocol/ExchangeUnbindFrame.php | 41 +- src/Protocol/ExchangeUnbindOkFrame.php | 17 +- src/Protocol/HeartbeatFrame.php | 4 +- src/Protocol/MethodFrame.php | 23 +- src/Protocol/QueueBindFrame.php | 41 +- src/Protocol/QueueBindOkFrame.php | 21 +- src/Protocol/QueueDeclareFrame.php | 49 +- src/Protocol/QueueDeclareOkFrame.php | 29 +- src/Protocol/QueueDeleteFrame.php | 37 +- src/Protocol/QueueDeleteOkFrame.php | 21 +- src/Protocol/QueuePurgeFrame.php | 29 +- src/Protocol/QueuePurgeOkFrame.php | 21 +- src/Protocol/QueueUnbindFrame.php | 37 +- src/Protocol/QueueUnbindOkFrame.php | 17 +- src/Protocol/TxCommitFrame.php | 17 +- src/Protocol/TxCommitOkFrame.php | 17 +- src/Protocol/TxRollbackFrame.php | 17 +- src/Protocol/TxRollbackOkFrame.php | 17 +- src/Protocol/TxSelectFrame.php | 17 +- src/Protocol/TxSelectOkFrame.php | 17 +- src/ProtocolReader.php | 410 +++--- src/ProtocolWriter.php | 283 ++-- test.php | 19 +- tests/BufferTest.php | 391 ++++++ 95 files changed, 4615 insertions(+), 3645 deletions(-) create mode 100644 benchmark/consumer.php create mode 100644 benchmark/producer.php delete mode 100644 src/ChannelAwaiter.php create mode 100644 src/Connection.php delete mode 100644 src/ConnectionAwaiter.php create mode 100644 src/Exception/RidgeException.php create mode 100644 src/Heartbeat.php create mode 100644 tests/BufferTest.php diff --git a/README.md b/README.md index 1b07e2b..8d71052 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,36 @@ $ composer require phpinnacle/ridge ```php connect(); + + /** @var Channel $channel */ + $channel = yield $client->channel(); + + yield $channel->queueDeclare('queue_name'); + + for ($i = 0; $i < 10; $i++) { + yield $channel->publish("test_$i", '', 'queue_name'); + } + + yield $channel->consume(function (Message $message, Channel $channel) { + echo $message->content() . \PHP_EOL; + + yield $channel->ack($message); + }, 'queue_name'); }); + ``` More examples can be found in [`examples`](examples) directory. diff --git a/benchmark/consumer.php b/benchmark/consumer.php new file mode 100644 index 0000000..f7181e0 --- /dev/null +++ b/benchmark/consumer.php @@ -0,0 +1,40 @@ +connect(); + + /** @var Channel $channel */ + $channel = yield $client->channel(); + + yield $channel->queueDeclare('bench_queue'); + yield $channel->exchangeDeclare('bench_exchange'); + yield $channel->queueBind('bench_queue', 'bench_exchange'); + + $t = null; + $count = 0; + + yield $channel->consume(function (Message $message) use (&$t, &$count, $client) { + if ($t === null) { + $t = \microtime(true); + } + + if ($message->content() === 'quit') { + \printf('Pid: %s, Count: %s, Time: %.4f' . \PHP_EOL, \getmypid(), $count, \microtime(true) - $t); + + yield $client->disconnect(); + } else { + ++$count; + } + + }, 'bench_queue', '', false, true); +}); diff --git a/benchmark/producer.php b/benchmark/producer.php new file mode 100644 index 0000000..1dc0997 --- /dev/null +++ b/benchmark/producer.php @@ -0,0 +1,51 @@ +connect(); + + /** @var Channel $channel */ + $channel = yield $client->channel(); + + yield $channel->queueDeclare('bench_queue'); + yield $channel->exchangeDeclare('bench_exchange'); + yield $channel->queueBind('bench_queue', 'bench_exchange'); + + $body = <<publish($body, 'bench_exchange'); + } + + $promises[] = $channel->publish('quit', 'bench_exchange'); + + yield $promises; + + yield $client->disconnect(); + + echo \microtime(true) - $time, \PHP_EOL; +}); diff --git a/composer.json b/composer.json index ed589ce..1d34e02 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ ], "require": { "php": "^7.2", - "amphp/amp": "^2.0" + "amphp/amp": "^2.0", + "amphp/socket": "^0.10.11" }, "require-dev": { "phpunit/phpunit": "^6.0" diff --git a/composer.lock b/composer.lock index 95f0f5a..88fd34c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b27dfbd103f0bf73c94791a3df29823f", + "content-hash": "d0f01d3ead419c312e28b12a6008405d", "packages": [ { "name": "amphp/amp", @@ -81,6 +81,672 @@ "promise" ], "time": "2018-12-11T10:31:37+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.5.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "6bbfcb6f47e92577e739586ba0c87e867be70a23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/6bbfcb6f47e92577e739586ba0c87e867be70a23", + "reference": "6bbfcb6f47e92577e739586ba0c87e867be70a23", + "shasum": "" + }, + "require": { + "amphp/amp": "^2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "friendsofphp/php-cs-fixer": "^2.3", + "infection/infection": "^0.9.3", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\ByteStream\\": "lib" + }, + "files": [ + "lib/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "time": "2018-12-27T18:08:06+00:00" + }, + { + "name": "amphp/cache", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/cache.git", + "reference": "ab2339e465d9d383dc748f288d530fd7cd7aadea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/cache/zipball/ab2339e465d9d383dc748f288d530fd7cd7aadea", + "reference": "ab2339e465d9d383dc748f288d530fd7cd7aadea", + "shasum": "" + }, + "require": { + "amphp/amp": "^2" + }, + "require-dev": { + "amphp/phpunit-util": "^1", + "friendsofphp/php-cs-fixer": "^2.3", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Cache\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A promise-aware caching API for Amp.", + "homepage": "https://github.com/amphp/cache", + "time": "2017-10-04T19:22:12+00:00" + }, + { + "name": "amphp/dns", + "version": "v0.9.13", + "source": { + "type": "git", + "url": "https://github.com/amphp/dns.git", + "reference": "4647e5f58263ffdeff7da5c269f517cb48cff84f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/dns/zipball/4647e5f58263ffdeff7da5c269f517cb48cff84f", + "reference": "4647e5f58263ffdeff7da5c269f517cb48cff84f", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.1", + "amphp/cache": "^1.2", + "amphp/file": "^0.2 || ^0.3", + "amphp/parser": "^1", + "amphp/uri": "^0.1", + "amphp/windows-registry": "^0.3", + "daverandom/libdns": "^2.0.1", + "ext-filter": "*", + "php": ">=7.0" + }, + "require-dev": { + "amphp/phpunit-util": "^1", + "friendsofphp/php-cs-fixer": "^2.3", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Dns\\": "lib" + }, + "files": [ + "lib/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Chris Wright", + "email": "addr@daverandom.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" + ], + "time": "2018-05-01T18:08:54+00:00" + }, + { + "name": "amphp/file", + "version": "v0.3.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/file.git", + "reference": "25d8ef6e67b95d5249e0af7a80adce77657d16bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/file/zipball/25d8ef6e67b95d5249e0af7a80adce77657d16bb", + "reference": "25d8ef6e67b95d5249e0af7a80adce77657d16bb", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1", + "amphp/parallel": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\File\\": "lib" + }, + "files": [ + "lib/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Allows non-blocking access to the filesystem for Amp.", + "homepage": "https://github.com/amphp/file", + "keywords": [ + "amp", + "amphp", + "async", + "disk", + "file", + "filesystem", + "io", + "non-blocking", + "static" + ], + "time": "2018-10-28T14:38:35+00:00" + }, + { + "name": "amphp/parallel", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel.git", + "reference": "ccf285bdcc38f355a1a113175291e1bfe87205ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel/zipball/ccf285bdcc38f355a1a113175291e1bfe87205ad", + "reference": "ccf285bdcc38f355a1a113175291e1bfe87205ad", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.5", + "amphp/parser": "^1", + "amphp/process": "^1", + "amphp/sync": "^1.0.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "phpunit/phpunit": "^6" + }, + "suggest": { + "ext-pthreads": "Required for thread contexts" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parallel\\": "lib" + }, + "files": [ + "lib/Worker/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", + "keywords": [ + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" + ], + "time": "2018-12-30T19:32:25+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "f83e68f03d5b8e8e0365b8792985a7f341c57ae1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/f83e68f03d5b8e8e0365b8792985a7f341c57ae1", + "reference": "f83e68f03d5b8e8e0365b8792985a7f341c57ae1", + "shasum": "" + }, + "require": { + "php": ">=7" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.3", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "time": "2017-06-06T05:29:10+00:00" + }, + { + "name": "amphp/process", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "90f3f31cbd0eee7fa5ae03ce588b1fc8f122997c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/90f3f31cbd0eee7fa5ae03ce588b1fc8f122997c", + "reference": "90f3f31cbd0eee7fa5ae03ce588b1fc8f122997c", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.4", + "php": ">=7" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Process\\": "lib" + }, + "files": [ + "lib/constants.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Asynchronous process manager.", + "homepage": "https://github.com/amphp/process", + "time": "2018-11-09T00:02:00+00:00" + }, + { + "name": "amphp/socket", + "version": "v0.10.11", + "source": { + "type": "git", + "url": "https://github.com/amphp/socket.git", + "reference": "2cb9d0ef823cfd1de4cfbd0e680c58841367f727" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/socket/zipball/2cb9d0ef823cfd1de4cfbd0e680c58841367f727", + "reference": "2cb9d0ef823cfd1de4cfbd0e680c58841367f727", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.1", + "amphp/dns": "^0.9", + "amphp/uri": "^0.1", + "php": ">=7.0" + }, + "require-dev": { + "amphp/phpunit-util": "^1", + "friendsofphp/php-cs-fixer": "^2.3", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Socket\\": "src" + }, + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Async socket connection / server tools for Amp.", + "homepage": "https://github.com/amphp/socket", + "keywords": [ + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" + ], + "time": "2018-10-17T16:53:02+00:00" + }, + { + "name": "amphp/sync", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "a1d8f244eb19e3e2a96abc4686cebc80995bbc90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/a1d8f244eb19e3e2a96abc4686cebc80995bbc90", + "reference": "a1d8f244eb19e3e2a96abc4686cebc80995bbc90", + "shasum": "" + }, + "require": { + "amphp/amp": "^2" + }, + "require-dev": { + "amphp/phpunit-util": "^1", + "friendsofphp/php-cs-fixer": "^2.3", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Sync\\": "lib" + }, + "files": [ + "lib/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + } + ], + "description": "Mutex, Semaphore, and other synchronization tools for Amp.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "time": "2017-11-29T21:48:53+00:00" + }, + { + "name": "amphp/uri", + "version": "v0.1.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/uri.git", + "reference": "b857ba4df3cf0852302ba1637fccce4ce1205241" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/uri/zipball/b857ba4df3cf0852302ba1637fccce4ce1205241", + "reference": "b857ba4df3cf0852302ba1637fccce4ce1205241", + "shasum": "" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.3", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Uri\\": "src" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey" + } + ], + "description": "Uri Parser and Resolver.", + "homepage": "https://github.com/amphp/uri", + "time": "2017-10-23T12:40:35+00:00" + }, + { + "name": "amphp/windows-registry", + "version": "v0.3.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/windows-registry.git", + "reference": "834af7a30ad7c006b0326ccd2686ddc6e6943366" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/windows-registry/zipball/834af7a30ad7c006b0326ccd2686ddc6e6943366", + "reference": "834af7a30ad7c006b0326ccd2686ddc6e6943366", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.4", + "amphp/process": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\WindowsRegistry\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Windows Registry Reader.", + "time": "2018-10-24T03:34:54+00:00" + }, + { + "name": "daverandom/libdns", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "1ecd825b6fa9bb3fddc07751997e29dc78749b95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/1ecd825b6fa9bb3fddc07751997e29dc78749b95", + "reference": "1ecd825b6fa9bb3fddc07751997e29dc78749b95", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": ">=7.0" + }, + "suggest": { + "ext-intl": "Required for IDN support" + }, + "type": "library", + "autoload": { + "psr-4": { + "LibDNS\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "DNS protocol implementation written in pure PHP", + "keywords": [ + "dns" + ], + "time": "2018-01-10T15:56:17+00:00" } ], "packages-dev": [ diff --git a/src/Buffer.php b/src/Buffer.php index 5265e0e..417983d 100644 --- a/src/Buffer.php +++ b/src/Buffer.php @@ -23,7 +23,7 @@ * * All integers are read from and written to buffer in big-endian order. */ -class Buffer +final class Buffer { /** * @var bool @@ -38,91 +38,62 @@ class Buffer /** * @var string */ - private $buffer; + private $data; /** * @var int */ - private $length; + private $size; /** * @param string $buffer */ - public function __construct(string $buffer = "") + public function __construct(string $buffer = '') { - $this->buffer = $buffer; - $this->length = \strlen($this->buffer); + $this->data = $buffer; + $this->size = \strlen($this->data); - self::$isLittleEndian = \unpack("S", "\x01\x00")[1] === 1; - self::$native64BitPack = PHP_INT_SIZE === 8; - } - - /** - * Swaps 16-bit integer endianness. - * - * @param string $s - * - * @return string - */ - public static function swapEndian16(string $s): string - { - return $s[1] . $s[0]; - } - - /** - * Swaps 32-bit integer endianness. - * - * @param string $s - * - * @return string - */ - public static function swapEndian32(string $s): string - { - return $s[3] . $s[2] . $s[1] . $s[0]; + if (self::$native64BitPack === null) { + self::$native64BitPack = PHP_INT_SIZE === 8; + self::$isLittleEndian = \unpack("S", "\x01\x00")[1] === 1; + } } /** - * Swaps 64-bit integer endianness. - * - * @param string $s + * Returns number of bytes in buffer. * - * @return string + * @return int */ - public static function swapEndian64(string $s): string + public function size(): int { - return $s[7] . $s[6] . $s[5] . $s[4] . $s[3] . $s[2] . $s[1] . $s[0]; + return $this->size; } /** - * Swaps 64-bit integer endianness so integer can be read/written as two 32-bit integers. - * - * @param string $s + * Returns true if buffer is empty. * - * @return string + * @return boolean */ - public static function swapHalvedEndian64(string $s): string + public function empty(): bool { - return $s[3] . $s[2] . $s[1] . $s[0] . $s[7] . $s[6] . $s[5] . $s[4]; + return $this->size === 0; } /** - * Returns number of bytes in buffer. - * - * @return int + * @return bool */ - public function getLength(): int + public function ready(): bool { - return $this->length; + return \ord($this->data[-1]) === Constants::FRAME_END; } /** - * Returns true if buffer is empty. - * - * @return boolean + * @return void */ - public function isEmpty(): bool + public function clear(): void { - return $this->length === 0; + $this->data = ""; + $this->size = 0; } /** @@ -135,12 +106,12 @@ public function isEmpty(): bool */ public function read(int $n, int $offset = 0): string { - if ($this->length < $offset + $n) { + if ($this->size < $offset + $n) { throw new Exception\BufferUnderflow; - } elseif ($offset === 0 && $this->length === $offset + $n) { - return $this->buffer; + } elseif ($offset === 0 && $this->size === $offset + $n) { + return $this->data; } else { - return \substr($this->buffer, $offset, $n); + return \substr($this->data, $offset, $n); } } @@ -153,20 +124,20 @@ public function read(int $n, int $offset = 0): string */ public function consume(int $n): string { - if ($this->length < $n) { + if ($this->size < $n) { throw new Exception\BufferUnderflow; - } elseif ($this->length === $n) { - $buffer = $this->buffer; + } elseif ($this->size === $n) { + $buffer = $this->data; - $this->buffer = ""; - $this->length = 0; + $this->data = ""; + $this->size = 0; return $buffer; } else { - $buffer = \substr($this->buffer, 0, $n); + $buffer = \substr($this->data, 0, $n); - $this->buffer = \substr($this->buffer, $n); - $this->length -= $n; + $this->data = \substr($this->data, $n); + $this->size -= $n; return $buffer; } @@ -181,16 +152,16 @@ public function consume(int $n): string */ public function discard(int $n): self { - if ($this->length < $n) { + if ($this->size < $n) { throw new Exception\BufferUnderflow; - } elseif ($this->length === $n) { - $this->buffer = ""; - $this->length = 0; + } elseif ($this->size === $n) { + $this->data = ""; + $this->size = 0; return $this; } else { - $this->buffer = \substr($this->buffer, $n); - $this->length -= $n; + $this->data = \substr($this->data, $n); + $this->size -= $n; return $this; } @@ -205,12 +176,12 @@ public function discard(int $n): self */ public function slice(int $n): self { - if ($this->length < $n) { + if ($this->size < $n) { throw new Exception\BufferUnderflow; - } elseif ($this->length === $n) { - return new Buffer($this->buffer); + } elseif ($this->size === $n) { + return new self($this->data); } else { - return new Buffer(\substr($this->buffer, 0, $n)); + return new self(\substr($this->data, 0, $n)); } } @@ -223,23 +194,23 @@ public function slice(int $n): self */ public function consumeSlice(int $n): self { - if ($this->length < $n) { + if ($this->size < $n) { throw new Exception\BufferUnderflow; - } elseif ($this->length === $n) { - $buffer = $this->buffer; + } elseif ($this->size === $n) { + $buffer = $this->data; - $this->buffer = ""; - $this->length = 0; + $this->data = ""; + $this->size = 0; - return new Buffer($buffer); + return new self($buffer); } else { - $buffer = \substr($this->buffer, 0, $n); + $buffer = \substr($this->data, 0, $n); - $this->buffer = \substr($this->buffer, $n); - $this->length -= $n; + $this->data = \substr($this->data, $n); + $this->size -= $n; - return new Buffer($buffer); + return new self($buffer); } } @@ -253,11 +224,11 @@ public function consumeSlice(int $n): self public function append($s): self { if ($s instanceof Buffer) { - $s = $s->buffer; + $s = $s->data; } - $this->buffer .= $s; - $this->length = \strlen($this->buffer); + $this->data .= $s; + $this->size = \strlen($this->data); return $this; } @@ -297,7 +268,7 @@ public function readInt8(int $offset = 0): int */ public function consumeUint8(): int { - [, $ret] = \unpack("C", $this->buffer); + [, $ret] = \unpack("C", $this->data); $this->discard(1); @@ -377,7 +348,7 @@ public function readInt16(int $offset = 0): int */ public function consumeUint16(): int { - [, $ret] = \unpack("n", $this->buffer); + [, $ret] = \unpack("n", $this->data); $this->discard(2); @@ -463,7 +434,7 @@ public function readInt32(int $offset = 0): int */ public function consumeUint32(): int { - [, $ret] = unpack("N", $this->buffer); + [, $ret] = unpack("N", $this->data); $this->discard(4); @@ -644,6 +615,56 @@ public function appendInt64(int $value): self return $this->append($s); } + /** + * Reads and discards string from buffer. + * + * @return string + */ + public function consumeString(): string + { + return $this->consume($this->consumeUint8()); + } + + /** + * Appends string to buffer. + * + * @param string $value + * + * @return self + */ + public function appendString(string $value): self + { + return $this + ->appendUint8(\strlen($value)) + ->append($value) + ; + } + + /** + * Reads and discards text from buffer. + * + * @return string + */ + public function consumeText(): string + { + return $this->consume($this->consumeUint32()); + } + + /** + * Appends text to buffer. + * + * @param string $value + * + * @return self + */ + public function appendText(string $value): self + { + return $this + ->appendUint32(\strlen($value)) + ->append($value) + ; + } + /** * Reads float from buffer. * @@ -771,23 +792,26 @@ public function appendBits(array $bits): self } /** + * @noinspection PhpDocMissingThrowsInspection + * * Consumes AMQP timestamp from buffer. * * @return \DateTimeInterface */ public function consumeTimestamp(): \DateTimeInterface { + /** @noinspection PhpUnhandledExceptionInspection */ $d = (new \DateTimeImmutable)->setTimestamp($this->consumeUint64()); return $d; } /** - * @param \DateTime $value + * @param \DateTimeInterface $value * * @return self */ - public function appendTimestamp(\DateTime $value): self + public function appendTimestamp(\DateTimeInterface $value): self { return $this->appendUint64($value->getTimestamp()); } @@ -802,7 +826,7 @@ public function consumeTable(): array $buffer = $this->consumeSlice($this->consumeUint32()); $data = []; - while (!$buffer->isEmpty()) { + while (!$buffer->empty()) { $data[$buffer->consume($buffer->consumeUint8())] = $buffer->consumeValue(); } @@ -818,7 +842,7 @@ public function consumeTable(): array */ public function appendTable(array $table): self { - $buffer = new Buffer; + $buffer = new self; foreach ($table as $k => $v) { $buffer->appendUint8(\strlen($k)); @@ -826,7 +850,7 @@ public function appendTable(array $table): self $buffer->appendValue($v); } - $this->appendUint32($buffer->getLength()); + $this->appendUint32($buffer->size()); return $this->append($buffer); } @@ -841,7 +865,7 @@ public function consumeArray(): array $buffer = $this->consumeSlice($this->consumeUint32()); $data = []; - while (!$buffer->isEmpty()) { + while (!$buffer->empty()) { $data[] = $buffer->consumeValue(); } @@ -857,13 +881,13 @@ public function consumeArray(): array */ public function appendArray(array $value): self { - $buffer = new Buffer; + $buffer = new self; foreach ($value as $v) { $buffer->appendValue($v); } - $this->appendUint32($buffer->getLength()); + $this->appendUint32($buffer->size()); return $this->append($buffer); } @@ -881,11 +905,17 @@ public function consumeDecimal(): int return $value * \pow(10, $scale); } + /** + * @return string + */ + public function __toString(): string + { + return $this->data; + } + /** * Consumes AMQP table/array field value. * - * @param Buffer $buffer - * * @return mixed * @throws Exception\ProtocolException */ @@ -931,11 +961,7 @@ private function consumeValue() case Constants::FIELD_NULL: return null; default: - throw new Exception\ProtocolException( - sprintf("Unhandled field type 0x%02x", $fieldType) . - (ctype_print(chr($fieldType)) ? " ('" . chr($fieldType) . "')" : "") . - "." - ); + throw Exception\ProtocolException::unknownFieldType($fieldType); } } @@ -948,8 +974,7 @@ private function appendValue($value) { if (\is_string($value)) { $this->appendUint8(Constants::FIELD_LONG_STRING); - $this->appendUint32(\strlen($value)); - $this->append($value); + $this->appendText($value); } elseif (\is_int($value)) { $this->appendUint8(Constants::FIELD_LONG_INT); $this->appendInt32($value); @@ -960,7 +985,7 @@ private function appendValue($value) $this->appendUint8(Constants::FIELD_DOUBLE); $this->appendDouble($value); } elseif (\is_array($value)) { - if (\array_keys($value) === \range(0, \count($value) - 1)) { // sequential array + if (\array_keys($value) === \range(0, \count($value) - 1)) { $this->appendUint8(Constants::FIELD_ARRAY); $this->appendArray($value); } else { @@ -973,11 +998,55 @@ private function appendValue($value) $this->appendUint8(Constants::FIELD_TIMESTAMP); $this->appendTimestamp($value); } else { - throw new Exception\ProtocolException( - "Unhandled value type '" . \gettype($value) . "' " . - (\is_object($value) ? "(class " . \get_class($value) . ")" : "") . - "." - ); + throw Exception\ProtocolException::unknownValueType($value); } } + + /** + * Swaps 16-bit integer endianness. + * + * @param string $s + * + * @return string + */ + private static function swapEndian16(string $s): string + { + return $s[1] . $s[0]; + } + + /** + * Swaps 32-bit integer endianness. + * + * @param string $s + * + * @return string + */ + private static function swapEndian32(string $s): string + { + return $s[3] . $s[2] . $s[1] . $s[0]; + } + + /** + * Swaps 64-bit integer endianness. + * + * @param string $s + * + * @return string + */ + private static function swapEndian64(string $s): string + { + return $s[7] . $s[6] . $s[5] . $s[4] . $s[3] . $s[2] . $s[1] . $s[0]; + } + + /** + * Swaps 64-bit integer endianness so integer can be read/written as two 32-bit integers. + * + * @param string $s + * + * @return string + */ + private static function swapHalvedEndian64(string $s): string + { + return $s[3] . $s[2] . $s[1] . $s[0] . $s[7] . $s[6] . $s[5] . $s[4]; + } } \ No newline at end of file diff --git a/src/Channel.php b/src/Channel.php index a7cb89c..c469957 100644 --- a/src/Channel.php +++ b/src/Channel.php @@ -12,19 +12,18 @@ namespace PHPinnacle\Ridge; +use function Amp\asyncCall; use function Amp\call; -use Amp\Deferred; use Amp\Promise; final class Channel { const STATE_READY = 1, - STATE_WAIT_HEAD = 2, - STATE_WAIT_BODY = 3, - STATE_ERROR = 4, - STATE_CLOSING = 5, - STATE_CLOSED = 6 + STATE_OPEN = 2, + STATE_ERROR = 3, + STATE_CLOSING = 4, + STATE_CLOSED = 5 ; const @@ -34,84 +33,34 @@ final class Channel ; /** - * @var Client + * @var Connection */ - private $client; + private $connection; /** * @var int */ - private $id; + public $id; /** * @var int */ - private $state; - - /** - * @var - */ - private $mode; + private $state = self::STATE_READY; /** * @var int */ - private $bodySizeRemaining; - - /** - * @var Buffer - */ - private $bodyBuffer; - - /** - * @var Protocol\BasicReturnFrame - */ - private $returnFrame; - - /** - * @var Protocol\BasicDeliverFrame - */ - private $deliverFrame; - - /** - * @var Protocol\BasicGetOkFrame - */ - private $getOkFrame; + private $mode = self::MODE_REGULAR; /** - * @var Protocol\ContentHeaderFrame + * @var bool */ - private $headerFrame; + private $consume = false; /** * @var callable[] */ - private $returnCallbacks = []; - - /** - * @var callable[] - */ - private $ackCallbacks = []; - - /** - * @var callable[] - */ - private $deliverCallbacks; - - /** - * @var Deferred - */ - private $getDeferred; - - /** - * @var Deferred - */ - private $closeDeferred; - - /** - * @var - */ - private $closePromise; + private $callbacks = []; /** * @var int @@ -119,85 +68,46 @@ final class Channel private $deliveryTag = 0; /** - * @param Client $client - * @param int $id + * @var int */ - public function __construct(Client $client, int $id) - { - $this->client = $client; - $this->id = $id; - } + private $frameMax = 8192; // TODO tune on connection /** - * Listener is called whenever 'basic.return' frame is received with arguments (Message $returnedMessage, MethodBasicReturnFrame $frame) - * - * @param callable $callback - * - * @return self + * @param int $id + * @param Connection $connection */ - public function addReturnListener(callable $callback): self + public function __construct(int $id, Connection $connection) { - $this->removeReturnListener($callback); // remove if previously added to prevent calling multiple times - $this->returnCallbacks[] = $callback; - - return $this; + $this->id = $id; + $this->connection = $connection; } /** - * Removes registered return listener. If the callback is not registered, this is noop. - * - * @param callable $callback + * @param string $outOfBand * - * @return self + * @return Promise */ - public function removeReturnListener(callable $callback): self + public function open(string $outOfBand = ''): Promise { - foreach ($this->returnCallbacks as $k => $v) { - if ($v === $callback) { - unset($this->returnCallbacks[$k]); + return call(function () use ($outOfBand) { + if ($this->state !== self::STATE_READY) { + throw new Exception\ChannelException("Trying to open not ready channel #{$this->id}."); } - } - - return $this; - } - - /** - * Listener is called whenever 'basic.ack' or 'basic.nack' is received. - * - * @param callable $callback - * @return self - */ - public function addAckListener(callable $callback) - { - if ($this->mode !== self::MODE_CONFIRM) { - throw new Exception\ChannelException("Ack/nack listener can be added when channel in confirm mode."); - } - - $this->removeAckListener($callback); - $this->ackCallbacks[] = $callback; - return $this; - } - - /** - * Removes registered ack/nack listener. If the callback is not registered, this is noop. - * - * @param callable $callback - * @return self - */ - public function removeAckListener(callable $callback) - { - if ($this->mode !== self::MODE_CONFIRM) { - throw new Exception\ChannelException("Ack/nack listener can be removed when channel in confirm mode."); - } + yield $this->connection->write((new Buffer) + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(5 + \strlen($outOfBand)) + ->appendUint16(20) + ->appendUint16(10) + ->appendString($outOfBand) + ->appendUint8(206) + ); - foreach ($this->ackCallbacks as $k => $v) { - if ($v === $callback) { - unset($this->ackCallbacks[$k]); - } - } + yield $this->await(Protocol\ChannelOpenOkFrame::class); - return $this; + $this->state = self::STATE_OPEN; + }); } /** @@ -208,7 +118,7 @@ public function removeAckListener(callable $callback) * @param int $replyCode * @param string $replyText * - * @return Promise + * @return Promise */ public function close(int $replyCode = 0, string $replyText = ''): Promise { @@ -218,14 +128,100 @@ public function close(int $replyCode = 0, string $replyText = ''): Promise } if ($this->state === self::STATE_CLOSING) { - return $this->closePromise; + return; } $this->state = self::STATE_CLOSING; - yield $this->client->channelClose($this->id, $replyCode, $replyText, 0, 0); + yield $this->connection->write((new Buffer) + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(11 + \strlen($replyText)) + ->appendUint16(20) + ->appendUint16(40) + ->appendInt16($replyCode) + ->appendString($replyText) + ->appendInt16(0) + ->appendInt16(0) + ->appendUint8(206) + ); + + yield $this->await(Protocol\ChannelCloseOkFrame::class); + + yield $this->connection->write((new Buffer) + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(4) + ->appendUint16(20) + ->appendUint16(41) + ->appendUint8(206) + ); + + $this->state = self::STATE_CLOSED; + }); + } + + /** + * @param string $realm + * @param bool $exclusive + * @param bool $passive + * @param bool $active + * @param bool $write + * @param bool $read + * + * @return Promise + */ + public function accessRequest + ( + string $realm = '/data', + bool $exclusive = false, + bool $passive = true, + bool $active = true, + bool $write = true, + bool $read = true + ): Promise + { + $flags = [$exclusive, $passive, $active, $write, $read]; + + return call (function () use ($realm, $flags) { + yield $this->connection->write((new Buffer) + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(6 + \strlen($realm)) + ->appendUint16(30) + ->appendUint16(10) + ->appendString($realm) + ->appendBits($flags) + ->appendUint8(206) + ); + + return $this->await(Protocol\AccessRequestOkFrame::class); + }); + } + + /** + * @param int $prefetchSize + * @param int $prefetchCount + * @param bool $global + * + * @return Promise + */ + public function qos(int $prefetchSize = 0, int $prefetchCount = 0, bool $global = false): Promise + { + return call(function () use ($prefetchSize, $prefetchCount, $global) { + yield $this->connection->write((new Buffer) + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(11) + ->appendUint16(60) + ->appendUint16(10) + ->appendInt32($prefetchSize) + ->appendInt16($prefetchCount) + ->appendBits([$global]) + ->appendUint8(206) + ); - return $this->client->removeChannel($this->id); + return $this->await(Protocol\BasicQosOkFrame::class); }); } @@ -243,22 +239,77 @@ public function close(int $replyCode = 0, string $replyText = ''): Promise * * @return Promise */ - public function consume(callable $callback, $queue = '', $consumerTag = '', $noLocal = false, $noAck = false, $exclusive = false, $nowait = false, $arguments = []) + public function consume + ( + callable $callback, + string $queue = '', + string $consumerTag = '', + bool $noLocal = false, + bool $noAck = false, + bool $exclusive = false, + bool $nowait = false, + array $arguments = [] + ) : Promise { $flags = [$noLocal, $noAck, $exclusive, $nowait]; return call(function () use ($callback, $queue, $consumerTag, $flags, $arguments) { - $frame = yield $this->client->consume($this->id, $queue, $consumerTag, ...$flags); + $buffer = new Buffer; + $buffer + ->appendUint16(60) + ->appendUint16(20) + ->appendInt16(0) + ->appendString($queue) + ->appendString($consumerTag) + ->appendBits($flags) + ->appendTable($arguments) + ; - if ($frame instanceof Protocol\BasicConsumeOkFrame) { - $this->deliverCallbacks[$frame->consumerTag] = $callback; - } else { - throw new Exception\ChannelException( - "basic.consume unexpected response of type " . gettype($frame) . - (is_object($frame) ? " (" . get_class($frame) . ")" : "") . - "." - ); + yield $this->connection->send($this->methodFrame(60, 20, $buffer)); + + /** @var Protocol\BasicConsumeOkFrame $frame */ + $frame = yield $this->await(Protocol\BasicConsumeOkFrame::class); + + $this->callbacks[$frame->consumerTag] = $callback; + + $this->startConsuming(); + + return $frame; + }); + } + + /** + * Cancels given consumer subscription. + * + * @param string $consumerTag + * @param bool $nowait + * + * @return Promise + */ + public function cancel(string $consumerTag, bool $nowait = false): Promise + { + return call(function () use ($consumerTag, $nowait) { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(6 + \strlen($consumerTag)) + ->appendUint16(60) + ->appendUint16(30) + ->appendString($consumerTag) + ->appendBits([$nowait]) + ->appendUint8(206) + ; + + $result = yield $this->connection->write($buffer); + + if ($nowait === false) { + $result = yield $this->await(Protocol\BasicCancelOkFrame::class); } + + unset($this->callbacks[$consumerTag]); + + return $result; }); } @@ -272,7 +323,19 @@ public function consume(callable $callback, $queue = '', $consumerTag = '', $noL */ public function ack(Message $message, bool $multiple = false): Promise { - return $this->client->ack($this->id, $message->deliveryTag, $multiple); + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(13) + ->appendUint16(60) + ->appendUint16(80) + ->appendInt64($message->deliveryTag()) + ->appendBits([$multiple]) + ->appendUint8(206) + ; + + return $this->connection->write($buffer); } /** @@ -284,9 +347,21 @@ public function ack(Message $message, bool $multiple = false): Promise * * @return Promise */ - public function nack(Message $message, $multiple = false, $requeue = true): Promise + public function nack(Message $message, bool $multiple = false, bool $requeue = true): Promise { - return $this->client->nack($this->id, $message->deliveryTag, $multiple, $requeue); + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(13) + ->appendUint16(60) + ->appendUint16(120) + ->appendInt64($message->deliveryTag()) + ->appendBits([$multiple, $requeue]) + ->appendUint8(206) + ; + + return $this->connection->write($buffer); } /** @@ -297,9 +372,63 @@ public function nack(Message $message, $multiple = false, $requeue = true): Prom * * @return Promise */ - public function reject(Message $message, $requeue = true) + public function reject(Message $message, bool $requeue = true): Promise { - return $this->client->reject($this->id, $message->deliveryTag, $requeue); + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(13) + ->appendUint16(60) + ->appendUint16(90) + ->appendInt64($message->deliveryTag()) + ->appendBits([$requeue]) + ->appendUint8(206) + ; + + return $this->connection->write($buffer); + } + + /** + * @param bool $requeue + * + * @return Promise + */ + public function recover(bool $requeue = false): Promise + { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(5) + ->appendUint16(60) + ->appendUint16(110) + ->appendBits([$requeue]) + ->appendUint8(206) + ; + + return $this->connection->write($buffer); + } + + /** + * @param bool $requeue + * + * @return Promise + */ + public function recoverAsync(bool $requeue = false): Promise + { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(5) + ->appendUint16(60) + ->appendUint16(100) + ->appendBits([$requeue]) + ->appendUint8(206) + ; + + return $this->connection->write($buffer); } /** @@ -308,139 +437,142 @@ public function reject(Message $message, $requeue = true) * @param string $queue * @param bool $noAck * - * @return Promise + * @return Promise */ - public function get($queue = '', $noAck = false) + public function get(string $queue = '', bool $noAck = false): Promise { - if ($this->getDeferred !== null) { - throw new Exception\ChannelException("Another 'basic.get' already in progress. You should use 'basic.consume' instead of multiple 'basic.get'."); - } + static $getting = false; + + return call(function () use ($queue, $noAck, &$getting) { + if ($getting) { + throw Exception\ChannelException::getInProgress(); + } - $response = $this->getImpl($queue, $noAck); + $getting = true; - if ($response instanceof PromiseInterface) { - $this->getDeferred = new Deferred(); + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(8 + \strlen($queue)) + ->appendUint16(60) + ->appendUint16(70) + ->appendInt16(0) + ->appendString($queue) + ->appendBits([$noAck]) + ->appendUint8(206) + ; - $response->done(function ($frame) { - if ($frame instanceof Protocol\BasicGetEmptyFrame) { - // deferred has to be first nullified and then resolved, otherwise results in race condition - $deferred = $this->getDeferred; - $this->getDeferred = null; - $deferred->resolve(null); + yield $this->connection->write($buffer); - } elseif ($frame instanceof Protocol\BasicGetOkFrame) { - $this->getOkFrame = $frame; - $this->state = self::STATE_WAIT_HEAD; + $promises = [ + $this->await(Protocol\BasicGetOkFrame::class), + $this->await(Protocol\BasicGetEmptyFrame::class) + ]; - } else { - throw new \LogicException("This statement should never be reached."); - } - }); + $frame = yield Promise\first($promises); - return $this->getDeferred->promise(); - } elseif ($response instanceof Protocol\BasicGetEmptyFrame) { - return null; - } elseif ($response instanceof Protocol\BasicGetOkFrame) { - $this->state = self::STATE_WAIT_HEAD; + if ($frame instanceof Protocol\BasicGetEmptyFrame) { + return null; + } - $headerFrame = $this->client->awaitContentHeader($this->id); - $this->headerFrame = $headerFrame; - $this->bodySizeRemaining = $headerFrame->bodySize; - $this->state = self::STATE_WAIT_BODY; + /** @var Protocol\ContentHeaderFrame $header */ + $header = yield $this->await(Protocol\ContentHeaderFrame::class); - while ($this->bodySizeRemaining > 0) { - $bodyFrame = $this->client->awaitContentBody($this->id); + $buffer = new Buffer; + $remaining = $header->bodySize; - $this->bodyBuffer->append($bodyFrame->payload); - $this->bodySizeRemaining -= $bodyFrame->payloadSize; + while ($remaining > 0) { + /** @var Protocol\ContentBodyFrame $body */ + $body = yield $this->await(Protocol\ContentBodyFrame::class); - if ($this->bodySizeRemaining < 0) { + $buffer->append($body->payload); + $remaining -= $body->size; + + if ($remaining < 0) { $this->state = self::STATE_ERROR; - $this->client->disconnect(Constants::STATUS_SYNTAX_ERROR, $errorMessage = "Body overflow, received " . (-$this->bodySizeRemaining) . " more bytes."); - throw new Exception\ChannelException($errorMessage); + + $error = "Body overflow, received " . (-$remaining) . " more bytes."; + + yield $this->connection->disconnect(Constants::STATUS_SYNTAX_ERROR, $error); + + throw new Exception\ChannelException($error); } } - $this->state = self::STATE_READY; + $getting = false; - $message = new Message( + return new Message( + (string) $buffer, + $frame->exchange, + $frame->routingKey, null, - $response->deliveryTag, - $response->redelivered, - $response->exchange, - $response->routingKey, - $this->headerFrame->toArray(), - $this->bodyBuffer->consume($this->bodyBuffer->getLength()) + $frame->deliveryTag, + $frame->redelivered, + $header->toArray() ); - - $this->headerFrame = null; - - return $message; - - } else { - throw new \LogicException("This statement should never be reached."); - } + }); } /** * Published message to given exchange. * * @param string $body - * @param array $headers * @param string $exchange * @param string $routingKey + * @param array $headers * @param bool $mandatory * @param bool $immediate * * @return Promise */ - public function publish($body, array $headers = [], $exchange = '', $routingKey = '', $mandatory = false, $immediate = false) + public function publish + ( + string $body, + string $exchange = '', + string $routingKey = '', + array $headers = [], + bool $mandatory = false, + bool $immediate = false + ): Promise { - return call(function () use ($body, $headers, $exchange, $routingKey, $mandatory, $immediate) { - yield $this->client->publish($this->id, $body, $headers, $exchange, $routingKey, $mandatory, $immediate); + return call(function () use ($body, $exchange, $routingKey, $headers, $mandatory, $immediate) { + yield $this->doPublish($body, $exchange, $routingKey, $headers, $mandatory, $immediate); return ++$this->deliveryTag; }); } - /** - * Cancels given consumer subscription. - * - * @param string $consumerTag - * @param bool $nowait - * - * @return Promise - */ - public function cancel($consumerTag, $nowait = false) - { - $response = $this->cancelImpl($consumerTag, $nowait); - unset($this->deliverCallbacks[$consumerTag]); - return $response; - } - /** * Changes channel to transactional mode. All messages are published to queues only after {@link txCommit()} is called. * * @return Promise */ - public function txSelect() + public function txSelect(): Promise { if ($this->mode !== self::MODE_REGULAR) { throw new Exception\ChannelException("Channel not in regular mode, cannot change to transactional mode."); } - $response = $this->txSelectImpl(); + return call(function () { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(4) + ->appendUint16(90) + ->appendUint16(10) + ->appendUint8(206) + ; + + yield $this->connection->write($buffer); - if ($response instanceof PromiseInterface) { - return $response->then(function ($response) { - $this->mode = self::MODE_TRANSACTIONAL; - return $response; - }); + $frame = yield $this->await(Protocol\TxSelectOkFrame::class); - } else { $this->mode = self::MODE_TRANSACTIONAL; - return $response; - } + + return $frame; + }); } /** @@ -448,13 +580,26 @@ public function txSelect() * * @return Promise */ - public function txCommit() + public function txCommit(): Promise { if ($this->mode !== self::MODE_TRANSACTIONAL) { throw new Exception\ChannelException("Channel not in transactional mode, cannot call 'tx.commit'."); } - return $this->txCommitImpl(); + return call(function () { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(4) + ->appendUint16(90) + ->appendUint16(20) + ->appendUint8(206); + + yield $this->connection->write($buffer); + + return $this->await(Protocol\TxCommitOkFrame::class); + }); } /** @@ -462,260 +607,732 @@ public function txCommit() * * @return Promise */ - public function txRollback() + public function txRollback(): Promise { if ($this->mode !== self::MODE_TRANSACTIONAL) { throw new Exception\ChannelException("Channel not in transactional mode, cannot call 'tx.rollback'."); } - return $this->txRollbackImpl(); + return call(function () { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(4) + ->appendUint16(90) + ->appendUint16(30) + ->appendUint8(206); + + yield $this->connection->write($buffer); + + return $this->await(Protocol\TxRollbackOkFrame::class); + }); } /** * Changes channel to confirm mode. Broker then asynchronously sends 'basic.ack's for published messages. * - * @param bool $nowait + * @param bool $nowait + * * @return Promise */ - public function confirmSelect(callable $callback = null, $nowait = false) + public function confirmSelect(bool $nowait = false) { if ($this->mode !== self::MODE_REGULAR) { throw new Exception\ChannelException("Channel not in regular mode, cannot change to transactional mode."); } - $response = $this->confirmSelectImpl($nowait); + return call(function () use ($nowait) { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(5) + ->appendUint16(85) + ->appendUint16(10) + ->appendBits([$nowait]) + ->appendUint8(206) + ; + + $result = yield $this->connection->write($buffer); + + if ($nowait === false) { + $result = yield $this->await(Protocol\ConfirmSelectOkFrame::class); + } - if ($response instanceof PromiseInterface) { - return $response->then(function ($response) use ($callback) { - $this->enterConfirmMode($callback); - return $response; - }); + $this->mode = self::MODE_CONFIRM; + $this->deliveryTag = 0; - } else { - $this->enterConfirmMode($callback); - return $response; - } + return $result; + }); } - private function enterConfirmMode(callable $callback = null) + /** + * @param string $queue + * @param bool $passive + * @param bool $durable + * @param bool $exclusive + * @param bool $autoDelete + * @param bool $nowait + * @param array $arguments + * + * @return Promise + */ + public function queueDeclare + ( + string $queue = '', + bool $passive = false, + bool $durable = false, + bool $exclusive = false, + bool $autoDelete = false, + bool $nowait = false, + array $arguments = [] + ): Promise { - $this->mode = self::MODE_CONFIRM; - $this->deliveryTag = 0; + $flags = [$passive, $durable, $exclusive, $autoDelete, $nowait]; + + return call(function () use ($queue, $flags, $nowait, $arguments) { + $buffer = new Buffer; + $buffer + ->appendUint16(50) + ->appendUint16(10) + ->appendInt16(0) + ->appendString($queue) + ->appendBits($flags) + ->appendTable($arguments) + ; + + $frame = $this->methodFrame(50, 10, $buffer); + + if ($nowait) { + return $this->connection->send($frame); + } else { + yield $this->connection->send($frame); - if ($callback) { - $this->addAckListener($callback); - } + return $this->await(Protocol\QueueDeclareOkFrame::class); + } + }); } /** - * Callback after channel-level frame has been received. + * @param string $queue + * @param string $exchange + * @param string $routingKey + * @param bool $nowait + * @param array $arguments * - * @param Protocol\AbstractFrame $frame + * @return Promise */ - public function onFrameReceived(Protocol\AbstractFrame $frame) + public function queueBind + ( + string $queue = '', + string $exchange = '', + string $routingKey = '', + bool $nowait = false, + array $arguments = [] + ): Promise { - if ($this->state === self::STATE_ERROR) { - throw new Exception\ChannelException("Channel in error state."); - } + return call(function () use ($queue, $exchange, $routingKey, $nowait, $arguments) { + $buffer = new Buffer; + $buffer + ->appendUint16(50) + ->appendUint16(20) + ->appendInt16(0) + ->appendString($queue) + ->appendString($exchange) + ->appendString($routingKey) + ->appendBits([$nowait]) + ->appendTable($arguments) + ; + + $frame = $this->methodFrame(50, 20, $buffer); + + if ($nowait) { + return $this->connection->send($frame); + } else { + yield $this->connection->send($frame); - if ($this->state === self::STATE_CLOSED) { - throw new Exception\ChannelException("Received frame #{$frame->type} on closed channel #{$this->id}."); - } + return $this->await(Protocol\QueueBindOkFrame::class); + } + }); + } - if ($frame instanceof Protocol\MethodFrame) { - if ($this->state === self::STATE_CLOSING && !($frame instanceof Protocol\ChannelCloseOkFrame)) { - return; - } elseif ($this->state !== self::STATE_READY && !($frame instanceof Protocol\ChannelCloseOkFrame)) { - $currentState = $this->state; + /** + * @param string $queue + * @param string $exchange + * @param string $routingKey + * @param array $arguments + * + * @return Promise + */ + public function queueUnbind + ( + string $queue = '', + string $exchange = '', + string $routingKey = '', + array $arguments = [] + ): Promise + { + return call(function () use ($queue, $exchange, $routingKey, $arguments) { + $buffer = new Buffer; + $buffer + ->appendUint16(50) + ->appendUint16(50) + ->appendInt16(0) + ->appendString($queue) + ->appendString($exchange) + ->appendString($routingKey) + ->appendTable($arguments) + ; + + yield $this->connection->send($this->methodFrame(50, 50, $buffer)); + + return $this->await(Protocol\QueueUnbindOkFrame::class); + }); + } - $this->state = self::STATE_ERROR; + /** + * @param string $queue + * @param bool $nowait + * + * @return Promise + */ + public function queuePurge(string $queue = '', bool $nowait = false): Promise + { + return call(function () use ($queue, $nowait) { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(8 + \strlen($queue)) + ->appendUint16(50) + ->appendUint16(30) + ->appendInt16(0) + ->appendString($queue) + ->appendBits([$nowait]) + ->appendUint8(206) + ; + + if ($nowait) { + return $this->connection->write($buffer); + } else { + yield $this->connection->write($buffer); - if ($currentState === self::STATE_WAIT_HEAD) { - $msg = "Got method frame, expected header frame."; - } elseif ($currentState === self::STATE_WAIT_BODY) { - $msg = "Got method frame, expected body frame."; - } else { - throw new \LogicException("Unhandled channel state."); - } + return $this->await(Protocol\QueuePurgeOkFrame::class); + } + }); + } - $this->client->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); + /** + * @param string $queue + * @param bool $ifUnused + * @param bool $ifEmpty + * @param bool $nowait + * + * @return Promise + */ + public function queueDelete + ( + string $queue = '', + bool $ifUnused = false, + bool $ifEmpty = false, + bool $nowait = false + ): Promise + { + $flags = [$ifUnused, $ifEmpty, $nowait]; + + return call(function () use ($queue, $flags, $nowait) { + $buffer = new Buffer; + $buffer->appendUint8(1); + $buffer->appendUint16($this->id); + $buffer->appendUint32(8 + strlen($queue)); + $buffer->appendUint16(50); + $buffer->appendUint16(40); + $buffer->appendInt16(0); + $buffer->appendString($queue); + $buffer->appendBits($flags); + $buffer->appendUint8(206); + + if ($nowait) { + return $this->connection->write($buffer); + } else { + yield $this->connection->write($buffer); - throw new Exception\ChannelException("Unexpected frame: " . $msg); + return $this->await(Protocol\QueueDeleteOkFrame::class); } + }); + } - if ($frame instanceof Protocol\ChannelCloseOkFrame) { - $this->state = self::STATE_CLOSED; + /** + * @param string $exchange + * @param string $exchangeType + * @param bool $passive + * @param bool $durable + * @param bool $autoDelete + * @param bool $internal + * @param bool $nowait + * @param array $arguments + * + * @return Promise + */ + public function exchangeDeclare + ( + string $exchange, + string $exchangeType = 'direct', + bool $passive = false, + bool $durable = false, + bool $autoDelete = false, + bool $internal = false, + bool $nowait = false, + array $arguments = [] + ): Promise + { + $flags = [$passive, $durable, $autoDelete, $internal, $nowait]; + + return call(function () use ($exchange, $exchangeType, $flags, $arguments) { + $buffer = new Buffer; + $buffer + ->appendUint16(40) + ->appendUint16(10) + ->appendInt16(0) + ->appendString($exchange) + ->appendString($exchangeType) + ->appendBits($flags) + ->appendTable($arguments) + ; + + yield $this->connection->send($this->methodFrame(40, 10, $buffer)); + + return $this->await(Protocol\ExchangeDeclareOkFrame::class); + }); + } - if ($this->closeDeferred !== null) { - $this->closeDeferred->resolve($this->id); - } + /** + * @param string $destination + * @param string $source + * @param string $routingKey + * @param bool $nowait + * @param array $arguments + * + * @return Promise + */ + public function exchangeBind + ( + string $destination, + string $source, + string $routingKey = '', + bool $nowait = false, + array $arguments = [] + ): Promise + { + return call(function () use ($destination, $source, $routingKey, $nowait, $arguments) { + $buffer = new Buffer; + $buffer + ->appendUint16(40) + ->appendUint16(30) + ->appendInt16(0) + ->appendString($destination) + ->appendString($source) + ->appendString($routingKey) + ->appendBits([$nowait]) + ->appendTable($arguments) + ; + + $frame = $this->methodFrame(40, 30, $buffer); + + if ($nowait) { + return $this->connection->send($frame); + } else { + yield $this->connection->send($frame); - // break reference cycle, must be called after resolving promise - $this->client = null; - // break consumers' reference cycle - $this->deliverCallbacks = []; + return $this->await(Protocol\ExchangeBindOkFrame::class); + } + }); + } - } elseif ($frame instanceof Protocol\BasicReturnFrame) { - $this->returnFrame = $frame; - $this->state = self::STATE_WAIT_HEAD; + /** + * @param string $destination + * @param string $source + * @param string $routingKey + * @param bool $nowait + * @param array $arguments + * + * @return Promise + */ + public function exchangeUnbind + ( + string $destination, + string $source, + string $routingKey = '', + bool $nowait = false, + array $arguments = [] + ): Promise + { + return call(function () use ($destination, $source, $routingKey, $nowait, $arguments) { + $buffer = new Buffer; + $buffer + ->appendUint16(40) + ->appendUint16(40) + ->appendInt16(0) + ->appendString($destination) + ->appendString($source) + ->appendString($routingKey) + ->appendBits([$nowait]) + ->appendTable($arguments) + ; + + $frame = $this->methodFrame(40, 40, $buffer); + + if ($nowait) { + return $this->connection->send($frame); + } else { + yield $this->connection->send($frame); - } elseif ($frame instanceof Protocol\BasicDeliverFrame) { - $this->deliverFrame = $frame; - $this->state = self::STATE_WAIT_HEAD; + return $this->await(Protocol\ExchangeUnbindOkFrame::class); + } + }); + } - } elseif ($frame instanceof Protocol\BasicAckFrame) { - foreach ($this->ackCallbacks as $callback) { - $callback($frame); - } + /** + * @param string $exchange + * @param bool $unused + * @param bool $nowait + * + * @return Promise + */ + public function exchangeDelete(string $exchange, bool $unused = false, bool $nowait = false): Promise + { + return call(function () use ($exchange, $unused, $nowait) { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(8 + \strlen($exchange)) + ->appendUint16(40) + ->appendUint16(20) + ->appendInt16(0) + ->appendString($exchange) + ->appendBits([$unused, $nowait]) + ->appendUint8(206) + ; + + yield $this->connection->write($buffer); + + return $this->await(Protocol\ExchangeDeleteOkFrame::class); + }); + } - } elseif ($frame instanceof Protocol\BasicNackFrame) { - foreach ($this->ackCallbacks as $callback) { - $callback($frame); - } + /** + * @param string $body + * @param string $exchange + * @param string $routingKey + * @param array $headers + * @param bool $mandatory + * @param bool $immediate + * + * @return Promise + */ + public function doPublish + ( + string $body, + string $exchange = '', + string $routingKey = '', + array $headers = [], + bool $mandatory = false, + bool $immediate = false + ): Promise + { + $buffer = new Buffer; + + $flags = 0; + $contentType = ''; + $contentEncoding = ''; + $type = ''; + $replyTo = ''; + $expiration = ''; + $messageId = ''; + $correlationId = ''; + $userId = ''; + $appId = ''; + $clusterId = ''; + + $deliveryMode = null; + $priority = null; + $timestamp = null; + + $headersBuffer = null; + + $buffer + ->appendUint8(1) + ->appendUint16($this->id) + ->appendUint32(9 + \strlen($exchange) + \strlen($routingKey)) + ->appendUint16(60) + ->appendUint16(40) + ->appendInt16(0) + ->appendString($exchange) + ->appendString($routingKey) + ->appendBits([$mandatory, $immediate]) + ->appendUint8(206) + ; + + $s = 14; + + if (isset($headers['content-type'])) { + $flags |= 32768; + $contentType = $headers['content-type']; + $s += 1 + \strlen($contentType); + unset($headers['content-type']); + } - } else { - throw new Exception\ChannelException("Unhandled method frame " . get_class($frame) . "."); - } + if (isset($headers['content-encoding'])) { + $flags |= 16384; + $contentEncoding = $headers['content-encoding']; + $s += 1 + \strlen($contentEncoding); + unset($headers['content-encoding']); + } - } elseif ($frame instanceof Protocol\ContentHeaderFrame) { - if ($this->state === self::STATE_CLOSING) { - // drop frames in closing state - return; + if (isset($headers['delivery-mode'])) { + $flags |= 4096; + $deliveryMode = (int) $headers['delivery-mode']; + $s += 1; + unset($headers['delivery-mode']); + } - } elseif ($this->state !== self::STATE_WAIT_HEAD) { - $currentState = $this->state; - $this->state = self::STATE_ERROR; + if (isset($headers['priority'])) { + $flags |= 2048; + $priority = (int) $headers['priority']; + $s += 1; + unset($headers['priority']); + } - if ($currentState === self::STATE_READY) { - $msg = "Got header frame, expected method frame."; - } elseif ($currentState === self::STATE_WAIT_BODY) { - $msg = "Got header frame, expected content frame."; - } else { - throw new \LogicException("Unhandled channel state."); - } + if (isset($headers['correlation-id'])) { + $flags |= 1024; + $correlationId = $headers['correlation-id']; + $s += 1 + \strlen($correlationId); + unset($headers['correlation-id']); + } - $this->client->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); + if (isset($headers['reply-to'])) { + $flags |= 512; + $replyTo = $headers['reply-to']; + $s += 1 + \strlen($replyTo); + unset($headers['reply-to']); + } - throw new Exception\ChannelException("Unexpected frame: " . $msg); - } + if (isset($headers['expiration'])) { + $flags |= 256; + $expiration = $headers['expiration']; + $s += 1 + \strlen($expiration); + unset($headers['expiration']); + } - $this->headerFrame = $frame; - $this->bodySizeRemaining = $frame->bodySize; + if (isset($headers['message-id'])) { + $flags |= 128; + $messageId = $headers['message-id']; + $s += 1 + \strlen($messageId); + unset($headers['message-id']); + } - if ($this->bodySizeRemaining > 0) { - $this->state = self::STATE_WAIT_BODY; - } else { - $this->state = self::STATE_READY; - $this->onBodyComplete(); - } + if (isset($headers['timestamp'])) { + $flags |= 64; + $timestamp = $headers['timestamp']; + $s += 8; + unset($headers['timestamp']); + } - } elseif ($frame instanceof Protocol\ContentBodyFrame) { - if ($this->state === self::STATE_CLOSING) { - // drop frames in closing state - return; + if (isset($headers['type'])) { + $flags |= 32; + $type = $headers['type']; + $s += 1 + \strlen($type); + unset($headers['type']); + } - } elseif ($this->state !== self::STATE_WAIT_BODY) { - $currentState = $this->state; - $this->state = self::STATE_ERROR; + if (isset($headers['user-id'])) { + $flags |= 16; + $userId = $headers['user-id']; + $s += 1 + \strlen($userId); + unset($headers['user-id']); + } - if ($currentState === self::STATE_READY) { - $msg = "Got body frame, expected method frame."; - } elseif ($currentState === self::STATE_WAIT_HEAD) { - $msg = "Got body frame, expected header frame."; - } else { - throw new \LogicException("Unhandled channel state."); - } + if (isset($headers['app-id'])) { + $flags |= 8; + $appId = $headers['app-id']; + $s += 1 + \strlen($appId); + unset($headers['app-id']); + } - $this->client->disconnect(Constants::STATUS_UNEXPECTED_FRAME, $msg); + if (isset($headers['cluster-id'])) { + $flags |= 4; + $clusterId = $headers['cluster-id']; + $s += 1 + \strlen($clusterId); + unset($headers['cluster-id']); + } - throw new Exception\ChannelException("Unexpected frame: " . $msg); - } + if (!empty($headers)) { + $flags |= 8192; + $headersBuffer = new Buffer; + $headersBuffer->appendTable($headers); + $s += $headersBuffer->size(); + } + + $buffer + ->appendUint8(2) + ->appendUint16($this->id) + ->appendUint32($s) + ->appendUint16(60) + ->appendUint16(0) + ; - $this->bodyBuffer->append($frame->payload); - $this->bodySizeRemaining -= $frame->size; + $buffer->appendUint64(\strlen($body)); - if ($this->bodySizeRemaining < 0) { - $this->state = self::STATE_ERROR; - $this->client->disconnect(Constants::STATUS_SYNTAX_ERROR, "Body overflow, received " . (-$this->bodySizeRemaining) . " more bytes."); + $buffer->appendUint16($flags); - } elseif ($this->bodySizeRemaining === 0) { - $this->state = self::STATE_READY; - $this->onBodyComplete(); + if ($flags & 32768) { + $buffer->appendString($contentType); + } + if ($flags & 16384) { + $buffer->appendString($contentEncoding); + } + if ($flags & 8192) { + $buffer->append($headersBuffer); + } + if ($flags & 4096) { + $buffer->appendUint8($deliveryMode); + } + if ($flags & 2048) { + $buffer->appendUint8($priority); + } + if ($flags & 1024) { + $buffer->appendString($correlationId); + } + if ($flags & 512) { + $buffer->appendString($replyTo); + } + if ($flags & 256) { + $buffer->appendString($expiration); + } + if ($flags & 128) { + $buffer->appendString($messageId); + } + if ($flags & 64) { + $buffer->appendTimestamp($timestamp); + } + if ($flags & 32) { + $buffer->appendString($type); + } + if ($flags & 16) { + $buffer->appendString($userId); + } + if ($flags & 8) { + $buffer->appendString($appId); + } + if ($flags & 4) { + $buffer->appendString($clusterId); + } + + $buffer->appendUint8(206); + + for ($payloadMax = $this->frameMax - 8, $i = 0, $l = \strlen($body); $i < $l; $i += $payloadMax) { + $payloadSize = $l - $i; + + if ($payloadSize > $payloadMax) { + $payloadSize = $payloadMax; } - } elseif ($frame instanceof Protocol\HeartbeatFrame) { - $this->client->disconnect(Constants::STATUS_UNEXPECTED_FRAME, "Got heartbeat on non-zero channel."); - throw new Exception\ChannelException("Unexpected heartbeat frame."); - } else { - throw new Exception\ChannelException("Unhandled frame " . get_class($frame) . "."); + + $buffer + ->appendUint8(3) + ->appendUint16($this->id) + ->appendUint32($payloadSize) + ->append(\substr($body, $i, $payloadSize)) + ->appendUint8(206) + ; } + + return $this->connection->write($buffer); } /** - * Callback after content body has been completely received. + * @return void */ - protected function onBodyComplete() + private function startConsuming(): void { - if ($this->returnFrame) { - $content = $this->bodyBuffer->consume($this->bodyBuffer->getLength()); - $message = new Message( - null, - null, - false, - $this->returnFrame->exchange, - $this->returnFrame->routingKey, - $this->headerFrame->toArray(), - $content - ); + if ($this->consume) { + return; + } - foreach ($this->returnCallbacks as $callback) { - $callback($message, $this->returnFrame); - } + asyncCall(function () { + while ($this->state === self::STATE_OPEN) { + /** @var Protocol\BasicDeliverFrame $deliver */ + $deliver = yield $this->await(Protocol\BasicDeliverFrame::class); - $this->returnFrame = null; - $this->headerFrame = null; - - } elseif ($this->deliverFrame) { - $content = $this->bodyBuffer->consume($this->bodyBuffer->getLength()); - if (isset($this->deliverCallbacks[$this->deliverFrame->consumerTag])) { - $message = new Message( - $this->deliverFrame->consumerTag, - $this->deliverFrame->deliveryTag, - $this->deliverFrame->redelivered, - $this->deliverFrame->exchange, - $this->deliverFrame->routingKey, - $this->headerFrame->toArray(), - $content - ); - - $callback = $this->deliverCallbacks[$this->deliverFrame->consumerTag]; - - $callback($message, $this, $this->client); - } + if (!isset($this->callbacks[$deliver->consumerTag])) { + continue; + } - $this->deliverFrame = null; - $this->headerFrame = null; + /** @var Protocol\ContentHeaderFrame $header */ + /** @var Protocol\ContentBodyFrame $body */ + $header = yield $this->await(Protocol\ContentHeaderFrame::class); - } elseif ($this->getOkFrame) { - $content = $this->bodyBuffer->consume($this->bodyBuffer->getLength()); + $buffer = new Buffer; + $remaining = $header->bodySize; - // deferred has to be first nullified and then resolved, otherwise results in race condition - $deferred = $this->getDeferred; - $this->getDeferred = null; - $deferred->resolve(new Message( - null, - $this->getOkFrame->deliveryTag, - $this->getOkFrame->redelivered, - $this->getOkFrame->exchange, - $this->getOkFrame->routingKey, - $this->headerFrame->toArray(), - $content - )); + while ($remaining > 0) { + /** @var Protocol\ContentBodyFrame $body */ + $body = yield $this->await(Protocol\ContentBodyFrame::class); - $this->getOkFrame = null; - $this->headerFrame = null; + $buffer->append($body->payload); + $remaining -= $body->size; - } else { - throw new \LogicException("Either return or deliver frame has to be handled here."); - } + if ($remaining < 0) { + $this->state = self::STATE_ERROR; + + $error = "Body overflow, received " . (-$remaining) . " more bytes."; + + yield $this->connection->disconnect(Constants::STATUS_SYNTAX_ERROR, $error); + + throw new Exception\ChannelException($error); + } + } + + asyncCall($this->callbacks[$deliver->consumerTag], new Message( + (string) $buffer, + $deliver->exchange, + $deliver->routingKey, + $deliver->consumerTag, + $deliver->deliveryTag, + $deliver->redelivered, + $header->toArray() + ), $this); + } + }); + + $this->consume = true; + } + + /** + * @param string $class + * + * @return Promise + */ + private function await(string $class): Promise + { + return $this->connection->await($class, $this->id); + } + + /** + * @param int $classId + * @param int $methodId + * @param Buffer $buffer + * + * @return Protocol\MethodFrame + */ + private function methodFrame(int $classId, int $methodId, Buffer $buffer): Protocol\MethodFrame + { + $frame = new Protocol\MethodFrame($classId, $methodId); + $frame->channel = $this->id; + $frame->size = $buffer->size(); + $frame->payload = $buffer; + + return $frame; } } diff --git a/src/ChannelAwaiter.php b/src/ChannelAwaiter.php deleted file mode 100644 index 1f68005..0000000 --- a/src/ChannelAwaiter.php +++ /dev/null @@ -1,291 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types = 1); - -namespace PHPinnacle\Ridge; - -use function Amp\call; -use Amp\Deferred; -use Amp\Promise; - -final class ChannelAwaiter -{ - /** - * @var Client - */ - private $client; - - /** - * @var callable[] - */ - private $callbacks; - - /** - * @param Client $client - */ - public function __construct(Client $client) - { - $this->client = $client; - } - - /** - * @param Protocol\AbstractFrame $frame - * - * @return Promise - */ - public function receive(Protocol\AbstractFrame $frame): Promise - { - return call(function () use ($frame) { - foreach ($this->callbacks as $k => $callback) { - $result = yield call($callback, $frame); - - if ($result === true) { - unset($this->callbacks[$k]); - - return true; - } - - unset($result); - } - - return false; - }); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitContentHeader(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\ContentHeaderFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitContentBody(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\ContentBodyFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitQueueDeclareOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\QueueDeclareOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitQueueBindOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\QueueBindOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitQueueUnbindOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\QueueUnbindOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitQueueDeleteOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\QueueDeleteOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitConsumeOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\BasicConsumeOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitCancelOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\BasicCancelOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitDeliver(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\BasicDeliverFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitAck(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\BasicAckFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitNack(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\BasicNackFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitExchangeDeclareOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\ExchangeDeclareOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitExchangeBindOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\ExchangeBindOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitExchangeUnbindOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\ExchangeUnbindOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitExchangeDeleteOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\ExchangeDeleteOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitChannelOpenOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\ChannelOpenOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitChannelClose(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\ChannelCloseFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitChannelCloseOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\ChannelCloseOkFrame::class); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function awaitQosOk(int $channel): Promise - { - return $this->awaitFrame($channel, Protocol\BasicQosOkFrame::class); - } - - /** - * @param int $channel - * @param string $class - * - * @return Promise - */ - private function awaitFrame(int $channel, string $class): Promise - { - $deferred = new Deferred(); - - $this->callbacks[] = function (Protocol\AbstractFrame $frame) use ($channel, $class, $deferred) { - if ($frame->channel === $channel && \is_a($frame, $class)) { - $deferred->resolve($frame); - - return true; - } - - if ($frame instanceof Protocol\ChannelCloseFrame && $frame->channel === $channel) { - yield $this->client->channelCloseOk($channel); - - $deferred->fail(new Exception\ClientException($frame->replyText, $frame->replyCode)); - - return true; - } - - if ($frame instanceof Protocol\ConnectionCloseFrame) { - yield $this->client->connectionCloseOk(); - - $deferred->fail(new Exception\ClientException($frame->replyText, $frame->replyCode)); - - return true; - } - - return false; - }; - - return $deferred->promise(); - } -} diff --git a/src/Client.php b/src/Client.php index 5f66ff3..69a027a 100644 --- a/src/Client.php +++ b/src/Client.php @@ -13,8 +13,6 @@ namespace PHPinnacle\Ridge; use function Amp\call; -use Amp\Deferred; -use Amp\Loop; use Amp\Promise; final class Client @@ -36,57 +34,6 @@ final class Client */ private $state = self::STATE_NOT_CONNECTED; - /** - * @var ProtocolReader - */ - private $reader; - - /** - * @var Buffer - */ - private $readBuffer; - - /** - * Read from stream watcher - * - * @var string|null - */ - private $readWatcher; - - /** - * @var ProtocolWriter - */ - private $writer; - - /** - * @var Buffer - */ - private $writeBuffer; - - /** - * Write to stream watcher - * - * @var string|null - */ - private $writeWatcher; - - /** - * Heartbeat watcher - * - * @var string|null - */ - private $heartbeatWatcher; - - /** - * @var Promise - */ - private $flushPromise; - - /** - * @var Promise|null - */ - private $disconnectPromise; - /** * @var Channel[] */ @@ -97,45 +44,15 @@ final class Client */ private $nextChannelId = 1; - /** - * @var float - */ - private $lastWrite = 0.0; - - /** - * @var float - */ - private $lastRead = 0.0; - - /** - * @var int - */ - private $frameMax = 0xFFFF; - /** * @var int */ private $channelMax = 0xFFFF; /** - * @var resource + * @var Connection */ - private $stream; - - /** - * @var ConnectionAwaiter - */ - private $awaitConnection; - - /** - * @var ChannelAwaiter - */ - private $awaitChannel; - - /** - * @var array - */ - private $cache = []; + private $connection; /** * @param Config $config @@ -143,8 +60,6 @@ final class Client public function __construct(Config $config) { $this->config = $config; - - $this->init(); } /** @@ -159,19 +74,11 @@ public function connect(): Promise $this->state = self::STATE_CONNECTING; - $this->writeBuffer->append('AMQP'); - $this->writeBuffer->appendUint8(0); - $this->writeBuffer->appendUint8(0); - $this->writeBuffer->appendUint8(9); - $this->writeBuffer->appendUint8(1); - - yield $this->flushWriteBuffer(); + $this->connection = new Connection($this->config); - $this->readWatcher = Loop::onReadable($this->getStream(), function() { - yield $this->onDataAvailable(); - }); + yield $this->connection->connect(); - return $this->doConnect(); + $this->state = self::STATE_CONNECTED; }); } @@ -184,30 +91,29 @@ public function connect(): Promise public function disconnect($replyCode = 0, $replyText = ''): Promise { return call(function() use ($replyCode, $replyText) { - if ($this->state === self::STATE_DISCONNECTING) { + if (\in_array($this->state, [self::STATE_NOT_CONNECTED, self::STATE_DISCONNECTING])) { return; } if ($this->state !== self::STATE_CONNECTED) { - throw new \RuntimeException('Client is not connected'); + throw new Exception\ClientException('Client is not connected'); } $this->state = self::STATE_DISCONNECTING; if ($replyCode === 0) { + $promises = []; + foreach($this->channels as $channel) { - yield $channel->close($replyCode, $replyText); + $promises[] = $channel->close($replyCode, $replyText); } - } - yield $this->connectionClose($replyCode, $replyText, 0, 0); + yield $promises; + } - $this->cancelReadWatcher(); - $this->cancelWriteWatcher(); - $this->cancelHeartbeatWatcher(); - $this->closeStream(); + yield $this->connection->disconnect($replyCode, $replyText); - $this->init(); + $this->state = self::STATE_NOT_CONNECTED; }); } @@ -217,855 +123,26 @@ public function disconnect($replyCode = 0, $replyText = ''): Promise public function channel(): Promise { return call(function() { - try { - $channelId = $this->findChannelId(); - - $this->channels[$channelId] = new Channel($this, $channelId); - - yield $this->channelOpen($channelId); - - return $this->channels[$channelId]; - } catch(\Throwable $throwable) { - throw new \RuntimeException('channel.open unexpected response', $throwable->getCode(), $throwable); - } - }); - } - - 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); - $buffer->appendBits([$mandatory, $immediate]); - $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 = (int) $headers['delivery-mode']; - $s += 1; - unset($headers['delivery-mode']); - } - - if (isset($headers['priority'])) { - $flags |= 2048; - $priority = (int) $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; - $headersBuffer = new Buffer(); - $headersBuffer->appendTable($headers); - $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) { - $buffer->appendTimestamp($timestamp); - } - 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->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); - } - - return $this->flushWriteBuffer(); - } - - /** - * @param int $channel - * @param string $queue - * @param string $consumerTag - * @param bool $noLocal - * @param bool $noAck - * @param bool $exclusive - * @param bool $nowait - * @param array $arguments - * - * @return Promise - */ - public function consume( - int $channel, string $queue = '', string $consumerTag = '', bool $noLocal = false, bool $noAck = false, - bool $exclusive = false, bool $nowait = false, array $arguments = [] - ) { - $flags = [$noLocal, $noAck, $exclusive, $nowait]; - - return call(function() use ($channel, $queue, $consumerTag, $flags, $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); - $buffer->appendBits($flags); - $buffer->appendTable($arguments); - - $frame = new Protocol\MethodFrame(60, 20); - $frame->channel = $channel; - $frame->size = $buffer->getLength(); - $frame->payload = $buffer; - - $this->writer->appendFrame($frame, $this->writeBuffer); - - yield $this->flushWriteBuffer(); - - unset($buffer, $frame); - - return $this->awaitChannel->awaitConsumeOk($channel); - }); - } - - /** - * @param int $channel - * @param int $deliveryTag - * @param bool $multiple - * - * @return Promise - */ - public function ack(int $channel, int $deliveryTag = 0, bool $multiple = false): Promise - { - $buffer = $this->writeBuffer; - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(13); - $buffer->appendUint16(60); - $buffer->appendUint16(80); - $buffer->appendInt64($deliveryTag); - $buffer->appendBits([$multiple]); - $buffer->appendUint8(206); - - return $this->flushWriteBuffer(); - } - - /** - * @param $channel - * @param int $deliveryTag - * @param bool $multiple - * @param bool $requeue - * - * @return Promise - */ - public function nack(int $channel, int $deliveryTag = 0, bool $multiple = false, bool $requeue = true) - { - $buffer = $this->writeBuffer; - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(13); - $buffer->appendUint16(60); - $buffer->appendUint16(120); - $buffer->appendInt64($deliveryTag); - $buffer->appendBits([$multiple, $requeue]); - $buffer->appendUint8(206); - - return $this->flushWriteBuffer(); - } - - 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); - $buffer->appendBits([$requeue]); - $buffer->appendUint8(206); - - return $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(100); - $buffer->appendBits([$requeue]); - $buffer->appendUint8(206); - - return $this->flushWriteBuffer(); - } - - /** - * @param int $channel - * @param string $consumerTag - * @param bool $nowait - * - * @return Promise - */ - public function cancel(int $channel, string $consumerTag, bool $nowait = false): Promise - { - return call(function () use ($channel, $consumerTag, $nowait) { - $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); - $buffer->appendBits([$nowait]); - $buffer->appendUint8(206); - - if ($nowait) { - return $this->flushWriteBuffer(); + if ($this->state !== self::STATE_CONNECTED) { + throw new Exception\ClientException('Client is not connected'); } - yield $this->flushWriteBuffer(); - - unset($buffer); - - return $this->awaitChannel->awaitCancelOk($channel); - }); - } - - /** - * @param int $channel - * @param int $prefetchSize - * @param int $prefetchCount - * @param bool $global - * - * @return Promise - */ - public function qos(int $channel, int $prefetchSize = 0, int $prefetchCount = 0, bool $global = false): Promise - { - return call(function () use ($channel, $prefetchSize, $prefetchCount, $global) { - $buffer = $this->writeBuffer; - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(11); - $buffer->appendUint16(60); - $buffer->appendUint16(10); - $buffer->appendInt32($prefetchSize); - $buffer->appendInt16($prefetchCount); - $buffer->appendBits([$global]); - $buffer->appendUint8(206); - - yield $this->flushWriteBuffer(); - - unset($buffer); - - return $this->awaitChannel->awaitQosOk($channel); - }); - } - - /** - * @param string $virtualHost - * @param string $capabilities - * @param bool $insist - * - * @return Promise - */ - public function connectionOpen(string $virtualHost = '/', string $capabilities = '', bool $insist = false): Promise - { - return call(function () use ($virtualHost, $capabilities, $insist) { - $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); - $buffer->appendBits([$insist]); - $buffer->appendUint8(206); - - yield $this->flushWriteBuffer(); - - unset($buffer); - - return $this->awaitConnection->awaitConnectionOpenOk(); - }); - } - - /** - * @param int $replyCode - * @param string $replyText - * @param int $closeClassId - * @param int $closeMethodId - * - * @return Promise - */ - public function connectionClose(int $replyCode, string $replyText, int $closeClassId, int $closeMethodId): Promise - { - return call(function () use ($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); - - yield $this->flushWriteBuffer(); - - unset($buffer); - - return $this->awaitConnection->awaitConnectionCloseOk(); - }); - } - - /** - * @param int $channel - * @param string $outOfBand - * - * @return Promise - */ - public function channelOpen(int $channel, string $outOfBand = ''): Promise - { - return call(function () use ($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); - - yield $this->flushWriteBuffer(); - - unset($buffer); - - $frame = yield $this->awaitChannel->awaitChannelOpenOk($channel); - - return $this->qos( - (int) $frame->channel, - $this->config->qosSize(), - $this->config->qosCount(), - $this->config->qosGlobal() - ); - }); - } - - /** - * @param $channel - * @param $replyCode - * @param $replyText - * @param $closeClassId - * @param $closeMethodId - * - * @return Promise - */ - public function channelClose($channel, $replyCode, $replyText, $closeClassId, $closeMethodId): Promise - { - return call(function () use ($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); - - return $this->flushWriteBuffer(); - }); - } - - /** - * @param int $channel - * @param string $exchange - * @param string $exchangeType - * @param bool $passive - * @param bool $durable - * @param bool $autoDelete - * @param bool $internal - * @param bool $nowait - * @param array $arguments - * - * @return Promise - */ - public function exchangeDeclare( - int $channel, - string $exchange, - string $exchangeType = 'direct', - bool $passive = false, - bool $durable = false, - bool $autoDelete = false, - bool $internal = false, - bool $nowait = false, - array $arguments = [] - ): Promise { - $flags = [$passive, $durable, $autoDelete, $internal, $nowait]; - - return call(function () use ($channel, $exchange, $exchangeType, $flags, $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); - $buffer->appendBits($flags); - $buffer->appendTable($arguments); - - $frame = new Protocol\MethodFrame(40, 10); - $frame->channel = $channel; - $frame->size = $buffer->getLength(); - $frame->payload = $buffer; - - $this->writer->appendFrame($frame, $this->writeBuffer); - - yield $this->flushWriteBuffer(); - - unset($buffer, $frame); - - return $this->awaitChannel->awaitExchangeDeclareOk($channel); - }); - } - - /** - * @param int $channel - * @param string $exchange - * @param bool $unused - * @param bool $nowait - * - * @return Promise - */ - public function exchangeDelete(int $channel, string $exchange, bool $unused = false, bool $nowait = false): Promise - { - return call(function () use ($channel, $exchange, $unused, $nowait) { - $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); - $buffer->appendBits([$unused, $nowait]); - $buffer->appendUint8(206); - - yield $this->flushWriteBuffer(); - - unset($buffer); - - return $this->awaitChannel->awaitExchangeDeleteOk($channel); - }); - } - - /** - * @param int $channel - * - * @return Promise - */ - public function channelCloseOk(int $channel): Promise - { - $buffer = $this->writeBuffer; - $buffer->appendUint8(1); - $buffer->appendUint16($channel); - $buffer->appendUint32(4); - $buffer->appendUint16(20); - $buffer->appendUint16(41); - $buffer->appendUint8(206); - - return $this->flushWriteBuffer(); - } - - /** - * @return Promise - */ - public function connectionCloseOk(): Promise - { - $buffer = $this->writeBuffer; - $buffer->appendUint8(1); - $buffer->appendUint16(0); - $buffer->appendUint32(4); - $buffer->appendUint16(10); - $buffer->appendUint16(51); - $buffer->appendUint8(206); - - return $this->flushWriteBuffer(); - } - - /** - * @return Promise - */ - private function flushWriteBuffer(): Promise - { - /** @var Promise|null $flushWriteBufferPromise */ - $flushWriteBufferPromise = $this->flushPromise; - - if ($flushWriteBufferPromise !== null) { - return $flushWriteBufferPromise; - } - - $deferred = new Deferred(); - - $this->writeWatcher = Loop::onWritable($this->getStream(), function() use ($deferred) { try { - $this->write(); - - if ($this->writeBuffer->isEmpty()) { - $this->cancelWriteWatcher(); - $this->flushPromise = null; - - $deferred->resolve(true); - } - } catch(\Exception $e) { - $this->cancelWriteWatcher(); - $this->flushPromise = null; - - $deferred->fail($e); - } - }); - - return $this->flushPromise = $deferred->promise(); - } - - /** - * Execute connect - * - * @return Promise - */ - private function doConnect(): Promise - { - return call(function () { - /** @var Protocol\ConnectionStartFrame $start */ - $start = yield $this->awaitConnection->awaitConnectionStart(); - - yield $this->authResponse($start); + $channelId = $this->findChannelId(); + $channel = new Channel($channelId, $this->connection); - /** @var Protocol\ConnectionTuneFrame $tune */ - $tune = yield $this->awaitConnection->awaitConnectionTune(); + $this->channels[$channelId] = &$channel; - $this->frameMax = $tune->frameMax; + yield $channel->open(); + yield $channel->qos($this->config->qosSize(), $this->config->qosCount(), $this->config->qosGlobal()); - if ($tune->channelMax > 0) { - $this->channelMax = $tune->channelMax; + return $channel; + } catch(\Throwable $throwable) { + throw new Exception\ClientException('channel.open unexpected response', $throwable->getCode(), $throwable); } - - yield $this->connectionTuneOk($tune->channelMax, $tune->frameMax, $this->config->heartbeat()); - yield $this->connectionOpen((string) ($this->options['vhost'] ?? '/')); - - $this->state = self::STATE_CONNECTED; - - $this->addHeartbeatTimer(); - }); - } - - /** - * @param Protocol\ConnectionStartFrame $start - * - * @return Promise - */ - private function authResponse(Protocol\ConnectionStartFrame $start): Promise - { - if (\strpos($start->mechanisms, "AMQPLAIN") === false) { - throw new Exception\ClientException("Server does not support AMQPLAIN mechanism (supported: {$start->mechanisms})."); - } - - $responseBuffer = new Buffer(); - - $responseBuffer->appendTable([ - "LOGIN" => $this->config->user(), - "PASSWORD" => $this->config->password(), - ]); - - $responseBuffer->discard(4); - - return $this->connectionStartOk([], "AMQPLAIN", $responseBuffer->read($responseBuffer->getLength()), "en_US"); - } - - /** - * @param array $properties - * @param string $mechanism - * @param string $response - * @param string $locale - * - * @return Promise - */ - private function connectionStartOk(array $properties, string $mechanism, string $response, string $locale = 'en_US'): Promise - { - $buffer = new Buffer; - $buffer->appendUint16(10); - $buffer->appendUint16(11); - $buffer->appendTable($properties); - $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->size = $buffer->getLength(); - $frame->payload = $buffer; - - $this->writer->appendFrame($frame, $this->writeBuffer); - - return $this->flushWriteBuffer(); - } - - /** - * @param int $channelMax - * @param int $frameMax - * @param int $heartbeat - * - * @return Promise - */ - private function connectionTuneOk($channelMax = 0, $frameMax = 0, $heartbeat = 0): Promise - { - $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((int) ($heartbeat * 1000)); - $buffer->appendUint8(206); - - return $this->flushWriteBuffer(); - } - - /** - * Add timer for heartbeat - * - * @return void - */ - private function addHeartbeatTimer(): void - { - /** @var float $seconds */ - $seconds = $this->config->heartbeat(); - - $this->heartbeatWatcher = Loop::repeat((int) ($seconds * 1000), function() { - yield $this->onHeartbeat(); }); } - /** - * @return void - */ - private function cancelHeartbeatWatcher(): void - { - if ($this->heartbeatWatcher !== null) { - Loop::cancel($this->heartbeatWatcher); - - $this->heartbeatWatcher = null; - } - } - - /** - * @return void - */ - private function cancelReadWatcher(): void - { - if ($this->readWatcher !== null) { - Loop::cancel($this->readWatcher); - - $this->readWatcher = null; - } - } - - /** - * @return void - */ - private function cancelWriteWatcher(): void - { - if ($this->writeWatcher !== null) { - Loop::cancel($this->writeWatcher); - - $this->writeWatcher = null; - } - } - /** * @return int */ @@ -1099,223 +176,4 @@ private function findChannelId(): int throw new Exception\ClientException("No available channels"); } - - /** - * Initializes instance. - */ - private function init() - { - $this->flushPromise = null; - $this->disconnectPromise = null; - - $this->readBuffer = new Buffer; - $this->writeBuffer = new Buffer; - - $this->reader = new ProtocolReader; - $this->writer = new ProtocolWriter; - - $this->awaitConnection = new ConnectionAwaiter($this); - $this->awaitChannel = new ChannelAwaiter($this); - } - - /** - * Creates stream according to options passed in constructor. - * - * @return resource - */ - protected function getStream() - { - if ($this->stream === null) { - // see https://github.com/nrk/predis/blob/v1.0/src/Connection/StreamConnection.php - $uri = "tcp://{$this->config->host()}:{$this->config->port()}"; - $flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT; - -// if (isset($this->options["persistent"]) && !!$this->options["persistent"]) { -// $flags |= STREAM_CLIENT_PERSISTENT; -// -// if (!isset($this->options["path"])) { -// throw new Exception\ClientException("If you need persistent connection, you have to specify 'path' option."); -// } -// -// $uri .= (strpos($this->options["path"], "/") === 0) ? $this->options["path"] : "/" . $this->options["path"]; -// } - - $this->stream = @stream_socket_client($uri, $errno, $errstr, $this->config->timeout(), $flags); - - if (!$this->stream) { - throw new Exception\ClientException( - "Could not connect to {$this->config->host()}:{$this->config->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"]); -// } - - \stream_set_blocking($this->stream, false); - } - - return $this->stream; - } - - /** - * Closes stream. - */ - private function closeStream() - { - @fclose($this->stream); - - $this->stream = null; - } - - /** - * Reads data from stream into {@link readBuffer}. - */ - private 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"]) { - throw new Exception\ClientException("Timeout reached while reading from stream."); - } - } - - if (@\feof($this->stream)) { - throw new Exception\ClientException("Broken pipe or closed connection."); - } - - $this->readBuffer->append($s); - - $this->lastRead = \microtime(true); - } - - /** - * Writes data from {@link writeBuffer} to stream. - */ - private function write() - { - if (($written = @\fwrite($this->getStream(), $this->writeBuffer->read($this->writeBuffer->getLength()))) === false) { - throw new Exception\ClientException("Could not write data to socket."); - } - - if ($written === 0) { - throw new Exception\ClientException("Broken pipe or closed connection."); - } - - \fflush($this->getStream()); // flush internal PHP buffers - - $this->writeBuffer->discard($written); - - $this->lastWrite = \microtime(true); - } - - /** - * @inheritdoc - * - * @return Promise It does not return any result - */ - private function onDataAvailable(): Promise - { - return call(function() { - $this->read(); - - while(($frame = $this->reader->consumeFrame($this->readBuffer)) !== null) { - if (yield $this->awaitConnection->receive($frame)) { - continue; - } - - if (yield $this->awaitChannel->receive($frame)) { - continue; - } - - if ($frame->channel === 0) { - yield $this->onFrameReceived($frame); - } else { - if (!isset($this->channels[$frame->channel])) { - throw new Exception\ClientException("Received frame #{$frame->type} on closed channel #{$frame->channel}."); - } - - if (!$frame instanceof Protocol\ChannelCloseFrame) { - $this->channels[$frame->channel]->onFrameReceived($frame); - } - } - - unset($frame); - } - }); - } - - /** - * Callback after connection-level frame has been received. - * - * @param Protocol\AbstractFrame $frame - * - * @return Promise - */ - private function onFrameReceived(Protocol\AbstractFrame $frame): Promise - { - return call(function () use ($frame) { - if ($frame instanceof Protocol\MethodFrame) { - if ($frame instanceof Protocol\ConnectionCloseFrame) { - throw new Exception\ClientException("Connection closed by server: " . $frame->replyText, $frame->replyCode); - } else { - throw new Exception\ClientException("Unhandled method frame " . \get_class($frame) . "."); - } - } elseif ($frame instanceof Protocol\ContentHeaderFrame) { - yield $this->disconnect(Constants::STATUS_UNEXPECTED_FRAME, "Got header frame on connection channel (#0)."); - } elseif ($frame instanceof Protocol\ContentBodyFrame) { - yield $this->disconnect(Constants::STATUS_UNEXPECTED_FRAME, "Got body frame on connection channel (#0)."); - } elseif ($frame instanceof Protocol\HeartbeatFrame) { - $this->lastRead = \microtime(true); - } else { - throw new Exception\ClientException("Unhandled frame " . \get_class($frame) . "."); - } - }); - } - - /** - * @return Promise It does not return any result - */ - private function onHeartbeat(): Promise - { - return call(function() { - /** @var float $currentTime */ - $currentTime = \microtime(true); - - /** @var float|null $lastWrite */ - $lastWrite = $this->lastWrite; - - if (null === $lastWrite) - { - $lastWrite = $currentTime; - } - - /** @var float $nextHeartbeat */ - $nextHeartbeat = $lastWrite + $this->config->heartbeat(); - - if ($currentTime >= $nextHeartbeat) - { - $this->writer->appendFrame(new Protocol\HeartbeatFrame, $this->writeBuffer); - - yield $this->flushWriteBuffer(); - } - - unset($currentTime, $lastWrite, $nextHeartbeat); - }); - } } diff --git a/src/Config.php b/src/Config.php index edcd4b3..f19a704 100644 --- a/src/Config.php +++ b/src/Config.php @@ -58,27 +58,27 @@ final class Config /** * @var int */ - private $timeout; + private $timeout = 1; /** * @var int */ - private $heartbeat; + private $heartbeat = 60; /** * @var int */ - private $qosSize; + private $qosSize = 0; /** * @var int */ - private $qosCount; + private $qosCount = 0; /** * @var bool */ - private $qosGlobal; + private $qosGlobal = false; /** * @param string $host @@ -87,13 +87,13 @@ final class Config * @param string $user * @param string $pass */ - public function __construct(string $host, int $port, string $vhost, string $user = null, string $pass = null) + public function __construct(string $host, int $port, string $vhost = null, string $user = null, string $pass = null) { $this->host = $host; $this->port = $port; - $this->vhost = $vhost; - $this->user = $user; - $this->pass = $pass; + $this->vhost = $vhost ?: self::DEFAULT_VHOST; + $this->user = $user ?: self::DEFAULT_USER; + $this->pass = $pass ?: self::DEFAULT_PASS; } /** @@ -117,8 +117,8 @@ public static function dsn(string $dsn): self $parts['pass'] ?? self::DEFAULT_PASS ); - $self->timeout = \filter_var($options['timeout'], FILTER_VALIDATE_FLOAT); - $self->heartbeat = \filter_var($options['heartbeat'], FILTER_VALIDATE_FLOAT); + $self->timeout = \filter_var($options['timeout'], FILTER_VALIDATE_INT); + $self->heartbeat = \filter_var($options['heartbeat'], FILTER_VALIDATE_INT); $self->qosSize = \filter_var($options['qos_size'], FILTER_VALIDATE_INT); $self->qosCount = \filter_var($options['qos_count'], FILTER_VALIDATE_INT); $self->qosGlobal = \filter_var($options['qos_global'], FILTER_VALIDATE_BOOLEAN); @@ -126,6 +126,14 @@ public static function dsn(string $dsn): self return $self; } + /** + * @return string + */ + public function uri(): string + { + return \sprintf('tcp://%s:%d', $this->host, $this->port); + } + /** * @return string */ @@ -167,17 +175,17 @@ public function vhost(): string } /** - * @return float + * @return int */ - public function timeout(): float + public function timeout(): int { return $this->timeout; } /** - * @return float + * @return int */ - public function heartbeat(): float + public function heartbeat(): int { return $this->heartbeat; } diff --git a/src/Connection.php b/src/Connection.php new file mode 100644 index 0000000..3336dd2 --- /dev/null +++ b/src/Connection.php @@ -0,0 +1,337 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types = 1); + +namespace PHPinnacle\Ridge; + +use function Amp\asyncCall, Amp\call, Amp\Socket\connect; +use Amp\Deferred; +use Amp\Promise; +use Amp\Socket\ClientConnectContext; +use Amp\Socket\Socket; + +final class Connection +{ + /** + * @var Config + */ + private $config; + + /** + * @var Heartbeat + */ + private $heartbeat; + + /** + * @var Buffer + */ + private $buffer; + + /** + * @var Socket + */ + private $socket; + + /** + * @var Deferred[][][] + */ + private $await = []; + + /** + * @param Config $config + */ + public function __construct(Config $config) + { + $this->config = $config; + $this->heartbeat = new Heartbeat($this); + $this->buffer = new Buffer; + } + + /** + * @param Protocol\AbstractFrame $frame + * + * @return Promise + */ + public function send(Protocol\AbstractFrame $frame): Promise + { + return $this->write(ProtocolWriter::buffer($frame)); + } + + /** + * @noinspection PhpDocMissingThrowsInspection + * + * @param Buffer $payload + * + * @return Promise + */ + public function write(Buffer $payload): Promise + { + $this->heartbeat->touch(); + + /** @noinspection PhpUnhandledExceptionInspection */ + return $this->socket->write((string) $payload); + } + + /** + * @param string $frame + * @param int $channel + * + * @return Promise + */ + public function await(string $frame, int $channel): Promise + { + $deferred = new Deferred; + + $this->await[$frame][$channel][] = $deferred; + + return $deferred->promise(); + } + + /** + * @return Promise + */ + public function connect(): Promise + { + return call(function () { + $context = (new ClientConnectContext)->withConnectTimeout($this->config->timeout()); + + $this->socket = yield connect($this->config->uri(), $context); + + $buffer = new Buffer; + $buffer + ->append('AMQP') + ->appendUint8(0) + ->appendUint8(0) + ->appendUint8(9) + ->appendUint8(1) + ; + + yield $this->write($buffer); + + asyncCall(function () { + while (null !== $chunk = yield $this->socket->read()) { + $this->buffer->append($chunk); + + $this->consume(); + } + }); + + return $this->doConnect(); + }); + } + + /** + * @param int $replyCode + * @param string $replyText + * + * @return Promise + */ + public function disconnect(int $replyCode = 0, string $replyText = ''): Promise + { + return call(function() use ($replyCode, $replyText) { + $this->heartbeat->disable(); + + yield $this->connectionClose($replyCode, $replyText); + yield $this->connectionCloseOk(); + + // $this->closeSocket(); // TODO + }); + } + + /** + * @return void + */ + private function consume(): void + { + while ($frame = ProtocolReader::frame($this->buffer)) { + $class = \get_class($frame); + $defers = $this->await[$class][$frame->channel] ?? []; + + foreach ($defers as $defer) { + $defer->resolve($frame); + } + + unset($this->await[$class]); + } + } + + /** + * Execute connect + * + * @return Promise + */ + private function doConnect(): Promise + { + return call(function () { + yield $this->connectionStart(); + yield $this->connectionTune(); + + return $this->connectionOpen((string) ($this->options['vhost'] ?? '/')); + }); + } + + /** + * @return Promise + */ + private function connectionStart(): Promise + { + return call(function () { + /** @var Protocol\ConnectionStartFrame $start */ + $start = yield $this->await(Protocol\ConnectionStartFrame::class, 0); + + if (\strpos($start->mechanisms, "AMQPLAIN") === false) { + throw new Exception\ClientException("Server does not support AMQPLAIN mechanism (supported: {$start->mechanisms})."); + } + + $buffer = new Buffer; + $buffer + ->appendTable([ + "LOGIN" => $this->config->user(), + "PASSWORD" => $this->config->password(), + ]) + ->discard(4) + ; + + $frameBuffer = new Buffer; + $frameBuffer + ->appendUint16(10) + ->appendUint16(11) + ->appendTable([]) + ->appendString("AMQPLAIN") + ->appendText((string) $buffer) + ->appendString("en_US") + ; + + $frame = new Protocol\MethodFrame(10, 11); + $frame->channel = 0; + $frame->size = $frameBuffer->size(); + $frame->payload = $frameBuffer; + + return $this->send($frame); + }); + } + + /** + * @return Promise + */ + private function connectionTune(): Promise + { + return call(function () { + /** @var Protocol\ConnectionTuneFrame $tune */ + $tune = yield $this->await(Protocol\ConnectionTuneFrame::class, 0); + + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16(0) + ->appendUint32(12) + ->appendUint16(10) + ->appendUint16(31) + ->appendInt16($tune->channelMax) + ->appendInt32($tune->frameMax) + ->appendInt16($tune->heartbeat) + ->appendUint8(206); + + yield $this->write($buffer); + + if ($tune->heartbeat > 0) { + $this->heartbeat->enable($tune->heartbeat); + } + }); + } + + /** + * @param string $virtualHost + * @param string $capabilities + * @param bool $insist + * + * @return Promise + */ + private function connectionOpen(string $virtualHost = '/', string $capabilities = '', bool $insist = false): Promise + { + return call(function () use ($virtualHost, $capabilities, $insist) { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16(0) + ->appendUint32(7 + \strlen($virtualHost) + \strlen($capabilities)) + ->appendUint16(10) + ->appendUint16(40) + ->appendString($virtualHost) + ->appendString($capabilities) + ->appendBits([$insist]) + ->appendUint8(206) + ; + + yield $this->write($buffer); + + return $this->await(Protocol\ConnectionOpenOkFrame::class, 0); + }); + } + + /** + * @param int $code + * @param string $reason + * + * @return Promise + */ + private function connectionClose(int $code, string $reason): Promise + { + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16(0) + ->appendUint32(11 + \strlen($reason)) + ->appendUint16(10) + ->appendUint16(50) + ->appendInt16($code) + ->appendString($reason) + ->appendInt16(0) + ->appendInt16(0) + ->appendUint8(206) + ; + + return $this->write($buffer); + } + + /** + * @return Promise + */ + private function connectionCloseOk(): Promise + { + return call(function () { + yield $this->await(Protocol\ConnectionCloseOkFrame::class, 0); + + $buffer = new Buffer; + $buffer + ->appendUint8(1) + ->appendUint16(0) + ->appendUint32(4) + ->appendUint16(10) + ->appendUint16(51) + ->appendUint8(206) + ; + + return $this->write($buffer); + }); + } + +// /** +// * @return void +// */ +// private function closeSocket(): void +// { +// if ($this->socket) { +// $this->socket->close(); +// $this->socket = null; +// } +// } +} diff --git a/src/ConnectionAwaiter.php b/src/ConnectionAwaiter.php deleted file mode 100644 index c6ca746..0000000 --- a/src/ConnectionAwaiter.php +++ /dev/null @@ -1,148 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types = 1); - -namespace PHPinnacle\Ridge; - -use function Amp\call; -use Amp\Deferred; -use Amp\Promise; - -final class ConnectionAwaiter -{ - /** - * @var Client - */ - private $client; - - /** - * @var callable[] - */ - private $callbacks = []; - - /** - * @param Client $client - */ - public function __construct(Client $client) - { - $this->client = $client; - } - - /** - * @param Protocol\AbstractFrame $frame - * - * @return Promise - */ - public function receive(Protocol\AbstractFrame $frame): Promise - { - return call(function () use ($frame) { - foreach ($this->callbacks as $k => $callback) { - $result = yield call($callback, $frame); - - if ($result === true) { - unset($this->callbacks[$k]); - - return true; - } - - unset($result); - } - - return false; - }); - } - - /** - * @return Promise - */ - public function awaitConnectionStart(): Promise - { - return $this->awaitFrame(Protocol\ConnectionStartFrame::class); - } - - /** - * @return Promise - */ - public function awaitConnectionTune(): Promise - { - return $this->awaitFrame(Protocol\ConnectionTuneFrame::class); - } - - /** - * @return Promise - */ - public function awaitConnectionOpenOk(): Promise - { - return $this->awaitFrame(Protocol\ConnectionOpenOkFrame::class); - } - - /** - * @return Promise - */ - public function awaitConnectionClose(): Promise - { - return $this->awaitFrame(Protocol\ConnectionCloseFrame::class); - } - - /** - * @return Promise - */ - public function awaitConnectionCloseOk(): Promise - { - return $this->awaitFrame(Protocol\ConnectionCloseOkFrame::class); - } - - /** - * @return Promise - */ - public function awaitConnectionBlocked(): Promise - { - return $this->awaitFrame(Protocol\ConnectionBlockedFrame::class); - } - - /** - * @return Promise - */ - public function awaitConnectionUnblocked(): Promise - { - return $this->awaitFrame(Protocol\ConnectionUnblockedFrame::class); - } - - /** - * @param string $class - * - * @return Promise - */ - private function awaitFrame(string $class): Promise - { - $deferred = new Deferred(); - - $this->callbacks[] = function (Protocol\AbstractFrame $frame) use ($class, $deferred) { - if (\is_a($frame, $class)) { - $deferred->resolve($frame); - - return true; - } - - if ($frame instanceof Protocol\ConnectionCloseFrame) { - yield $this->client->connectionCloseOk(); - - $deferred->fail(new Exception\ClientException($frame->replyText, $frame->replyCode)); - - return true; - } - - return false; - }; - - return $deferred->promise(); - } -} diff --git a/src/Constants.php b/src/Constants.php index b4c5dcf..65de1e3 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -14,245 +14,155 @@ class Constants { - // frame related constants - const FRAME_METHOD = 1; - - const FRAME_HEADER = 2; - - const FRAME_BODY = 3; - - const FRAME_HEARTBEAT = 8; - - const FRAME_MIN_SIZE = 4096; - - const FRAME_END = 0xCE; - - // connection channel const CONNECTION_CHANNEL = 0; - // status codes - const STATUS_REPLY_SUCCESS = 200; - - const STATUS_CONTENT_TOO_LARGE = 311; - - const STATUS_NO_CONSUMERS = 313; - - const STATUS_CONNECTION_FORCED = 320; - - const STATUS_INVALID_PATH = 402; - - const STATUS_ACCESS_REFUSED = 403; - - const STATUS_NOT_FOUND = 404; - - const STATUS_RESOURCE_LOCKED = 405; - - const STATUS_PRECONDITION_FAILED = 406; - - const STATUS_FRAME_ERROR = 501; - - const STATUS_SYNTAX_ERROR = 502; - - const STATUS_COMMAND_INVALID = 503; - - const STATUS_CHANNEL_ERROR = 504; - - const STATUS_UNEXPECTED_FRAME = 505; - - const STATUS_RESOURCE_ERROR = 506; - - const STATUS_NOT_ALLOWED = 530; - - const STATUS_NOT_IMPLEMENTED = 540; - - const STATUS_INTERNAL_ERROR = 541; - - // connection class - const CLASS_CONNECTION = 10; - - const METHOD_CONNECTION_START = 10; - - const METHOD_CONNECTION_START_OK = 11; - - const METHOD_CONNECTION_SECURE = 20; - - const METHOD_CONNECTION_SECURE_OK = 21; - - const METHOD_CONNECTION_TUNE = 30; - - const METHOD_CONNECTION_TUNE_OK = 31; - - const METHOD_CONNECTION_OPEN = 40; - - const METHOD_CONNECTION_OPEN_OK = 41; - - const METHOD_CONNECTION_CLOSE = 50; - - const METHOD_CONNECTION_CLOSE_OK = 51; - - const METHOD_CONNECTION_BLOCKED = 60; // RabbitMQ extension - - const METHOD_CONNECTION_UNBLOCKED = 61; // RabbitMQ extension - - // channel class - const CLASS_CHANNEL = 20; - - const METHOD_CHANNEL_OPEN = 10; - - const METHOD_CHANNEL_OPEN_OK = 11; - - const METHOD_CHANNEL_FLOW = 20; - - const METHOD_CHANNEL_FLOW_OK = 21; - - const METHOD_CHANNEL_CLOSE = 40; - - const METHOD_CHANNEL_CLOSE_OK = 41; - - // access class - const CLASS_ACCESS = 30; - - const METHOD_ACCESS_REQUEST = 10; - - const METHOD_ACCESS_REQUEST_OK = 11; - - // exchange class - const CLASS_EXCHANGE = 40; - - const METHOD_EXCHANGE_DECLARE = 10; - - const METHOD_EXCHANGE_DECLARE_OK = 11; - - const METHOD_EXCHANGE_DELETE = 20; - - const METHOD_EXCHANGE_DELETE_OK = 21; - - const METHOD_EXCHANGE_BIND = 30; // RabbitMQ extension - - const METHOD_EXCHANGE_BIND_OK = 31; // RabbitMQ extension - - const METHOD_EXCHANGE_UNBIND = 40; // RabbitMQ extension - - const METHOD_EXCHANGE_UNBIND_OK = 51; // RabbitMQ extension - - // queue class - const CLASS_QUEUE = 50; - - const METHOD_QUEUE_DECLARE = 10; - - const METHOD_QUEUE_DECLARE_OK = 11; - - const METHOD_QUEUE_BIND = 20; - - const METHOD_QUEUE_BIND_OK = 21; - - const METHOD_QUEUE_PURGE = 30; - - const METHOD_QUEUE_PURGE_OK = 31; - - const METHOD_QUEUE_DELETE = 40; - - const METHOD_QUEUE_DELETE_OK = 41; - - const METHOD_QUEUE_UNBIND = 50; - - const METHOD_QUEUE_UNBIND_OK = 51; - - // basic class - const CLASS_BASIC = 60; - - const METHOD_BASIC_QOS = 10; - - const METHOD_BASIC_QOS_OK = 11; - - const METHOD_BASIC_CONSUME = 20; - - const METHOD_BASIC_CONSUME_OK = 21; - - const METHOD_BASIC_CANCEL = 30; - - const METHOD_BASIC_CANCEL_OK = 31; - - const METHOD_BASIC_PUBLISH = 40; - - const METHOD_BASIC_RETURN = 50; - - const METHOD_BASIC_DELIVER = 60; - - const METHOD_BASIC_GET = 70; - - const METHOD_BASIC_GET_OK = 71; - - const METHOD_BASIC_GET_EMPTY = 72; - - const METHOD_BASIC_ACK = 80; - - const METHOD_BASIC_REJECT = 90; - - const METHOD_BASIC_RECOVER_ASYNC = 100; - - const METHOD_BASIC_RECOVER = 110; - - const METHOD_BASIC_RECOVER_OK = 111; - - const METHOD_BASIC_NACK = 120; // RabbitMQ extension - - // tx class - const CLASS_TX = 90; - - const METHOD_TX_SELECT = 10; - - const METHOD_TX_SELECT_OK = 11; - - const METHOD_TX_COMMIT = 20; - - const METHOD_TX_COMMIT_OK = 21; - - const METHOD_TX_ROLLBACK = 30; - - const METHOD_TX_ROLLBACK_OK = 31; - - // confirm class - const CLASS_CONFIRM = 85; // RabbitMQ extension - - const METHOD_CONFIRM_SELECT = 10; // RabbitMQ extension - - const METHOD_CONFIRM_SELECT_OK = 11; // RabbitMQ extension - - // table/array field value types - const FIELD_BOOLEAN = 0x74; // 't' - - const FIELD_SHORT_SHORT_INT = 0x62; // 'b' - - const FIELD_SHORT_SHORT_UINT = 0x42; // 'B' - - const FIELD_SHORT_INT = 0x55; // 'U' - - const FIELD_SHORT_UINT = 0x75; // 'u' - - const FIELD_LONG_INT = 0x49; // 'I' - - const FIELD_LONG_UINT = 0x69; // 'i' - - const FIELD_LONG_LONG_INT = 0x4C; // 'L' - - const FIELD_LONG_LONG_UINT = 0x6C; // 'l' - - const FIELD_FLOAT = 0x66; // 'f' - - const FIELD_DOUBLE = 0x64; // 'd' - - const FIELD_DECIMAL_VALUE = 0x44; // 'D' - - const FIELD_SHORT_STRING = 0x73; // 's' - - const FIELD_LONG_STRING = 0x53; // 'S' - - const FIELD_ARRAY = 0x41; // 'A' - - const FIELD_TIMESTAMP = 0x54; // 'T' - - const FIELD_TABLE = 0x46; // 'F' - - const FIELD_NULL = 0x56; // 'V' + const + FRAME_METHOD = 1, + FRAME_HEADER = 2, + FRAME_BODY = 3, + FRAME_HEARTBEAT = 8, + FRAME_MIN_SIZE = 4096, + FRAME_END = 0xCE + ; + + const + STATUS_REPLY_SUCCESS = 200, + STATUS_CONTENT_TOO_LARGE = 311, + STATUS_NO_CONSUMERS = 313, + STATUS_CONNECTION_FORCED = 320, + STATUS_INVALID_PATH = 402, + STATUS_ACCESS_REFUSED = 403, + STATUS_NOT_FOUND = 404, + STATUS_RESOURCE_LOCKED = 405, + STATUS_PRECONDITION_FAILED = 406, + STATUS_FRAME_ERROR = 501, + STATUS_SYNTAX_ERROR = 502, + STATUS_COMMAND_INVALID = 503, + STATUS_CHANNEL_ERROR = 504, + STATUS_UNEXPECTED_FRAME = 505, + STATUS_RESOURCE_ERROR = 506, + STATUS_NOT_ALLOWED = 530, + STATUS_NOT_IMPLEMENTED = 540, + STATUS_INTERNAL_ERROR = 541 + ; + + const + CLASS_CONNECTION = 10, + CLASS_CHANNEL = 20, + CLASS_ACCESS = 30, + CLASS_EXCHANGE = 40, + CLASS_QUEUE = 50, + CLASS_BASIC = 60, + CLASS_TX = 90, + CLASS_CONFIRM = 85 // RabbitMQ extension + ; + + const + METHOD_CONNECTION_START = 10, + METHOD_CONNECTION_START_OK = 11, + METHOD_CONNECTION_SECURE = 20, + METHOD_CONNECTION_SECURE_OK = 21, + METHOD_CONNECTION_TUNE = 30, + METHOD_CONNECTION_TUNE_OK = 31, + METHOD_CONNECTION_OPEN = 40, + METHOD_CONNECTION_OPEN_OK = 41, + METHOD_CONNECTION_CLOSE = 50, + METHOD_CONNECTION_CLOSE_OK = 51, + METHOD_CONNECTION_BLOCKED = 60, // RabbitMQ extension + METHOD_CONNECTION_UNBLOCKED = 61 // RabbitMQ extension + ; + + const + METHOD_CHANNEL_OPEN = 10, + METHOD_CHANNEL_OPEN_OK = 11, + METHOD_CHANNEL_FLOW = 20, + METHOD_CHANNEL_FLOW_OK = 21, + METHOD_CHANNEL_CLOSE = 40, + METHOD_CHANNEL_CLOSE_OK = 41 + ; + + const + METHOD_ACCESS_REQUEST = 10, + METHOD_ACCESS_REQUEST_OK = 11 + ; + + const + METHOD_EXCHANGE_DECLARE = 10, + METHOD_EXCHANGE_DECLARE_OK = 11, + METHOD_EXCHANGE_DELETE = 20, + METHOD_EXCHANGE_DELETE_OK = 21, + METHOD_EXCHANGE_BIND = 30, // RabbitMQ extension + METHOD_EXCHANGE_BIND_OK = 31, // RabbitMQ extension + METHOD_EXCHANGE_UNBIND = 40, // RabbitMQ extension + METHOD_EXCHANGE_UNBIND_OK = 51 // RabbitMQ extension + ; + + const + METHOD_QUEUE_DECLARE = 10, + METHOD_QUEUE_DECLARE_OK = 11, + METHOD_QUEUE_BIND = 20, + METHOD_QUEUE_BIND_OK = 21, + METHOD_QUEUE_PURGE = 30, + METHOD_QUEUE_PURGE_OK = 31, + METHOD_QUEUE_DELETE = 40, + METHOD_QUEUE_DELETE_OK = 41, + METHOD_QUEUE_UNBIND = 50, + METHOD_QUEUE_UNBIND_OK = 51 + ; + + const + METHOD_BASIC_QOS = 10, + METHOD_BASIC_QOS_OK = 11, + METHOD_BASIC_CONSUME = 20, + METHOD_BASIC_CONSUME_OK = 21, + METHOD_BASIC_CANCEL = 30, + METHOD_BASIC_CANCEL_OK = 31, + METHOD_BASIC_PUBLISH = 40, + METHOD_BASIC_RETURN = 50, + METHOD_BASIC_DELIVER = 60, + METHOD_BASIC_GET = 70, + METHOD_BASIC_GET_OK = 71, + METHOD_BASIC_GET_EMPTY = 72, + METHOD_BASIC_ACK = 80, + METHOD_BASIC_REJECT = 90, + METHOD_BASIC_RECOVER_ASYNC = 100, + METHOD_BASIC_RECOVER = 110, + METHOD_BASIC_RECOVER_OK = 111, + METHOD_BASIC_NACK = 120 // RabbitMQ extension + ; + + const + METHOD_TX_SELECT = 10, + METHOD_TX_SELECT_OK = 11, + METHOD_TX_COMMIT = 20, + METHOD_TX_COMMIT_OK = 21, + METHOD_TX_ROLLBACK = 30, + METHOD_TX_ROLLBACK_OK = 31 + ; + + const + METHOD_CONFIRM_SELECT = 10, // RabbitMQ extension + METHOD_CONFIRM_SELECT_OK = 11 // RabbitMQ extension + ; + + const + FIELD_BOOLEAN = 0x74, // 't' + FIELD_SHORT_SHORT_INT = 0x62, // 'b' + FIELD_SHORT_SHORT_UINT = 0x42, // 'B' + FIELD_SHORT_INT = 0x55, // 'U' + FIELD_SHORT_UINT = 0x75, // 'u' + FIELD_LONG_INT = 0x49, // 'I' + FIELD_LONG_UINT = 0x69, // 'i' + FIELD_LONG_LONG_INT = 0x4C, // 'L' + FIELD_LONG_LONG_UINT = 0x6C, // 'l' + FIELD_FLOAT = 0x66, // 'f' + FIELD_DOUBLE = 0x64, // 'd' + FIELD_DECIMAL_VALUE = 0x44, // 'D' + FIELD_SHORT_STRING = 0x73, // 's' + FIELD_LONG_STRING = 0x53, // 'S' + FIELD_ARRAY = 0x41, // 'A' + FIELD_TIMESTAMP = 0x54, // 'T' + FIELD_TABLE = 0x46, // 'F' + FIELD_NULL = 0x56 // 'V' + ; } diff --git a/src/Exception/BufferUnderflow.php b/src/Exception/BufferUnderflow.php index b9b36dc..fb2e497 100644 --- a/src/Exception/BufferUnderflow.php +++ b/src/Exception/BufferUnderflow.php @@ -12,6 +12,6 @@ namespace PHPinnacle\Ridge\Exception; -class BufferUnderflow extends \RuntimeException +final class BufferUnderflow extends RidgeException { } diff --git a/src/Exception/ChannelException.php b/src/Exception/ChannelException.php index ea93b08..adcd08d 100644 --- a/src/Exception/ChannelException.php +++ b/src/Exception/ChannelException.php @@ -12,6 +12,13 @@ namespace PHPinnacle\Ridge\Exception; -class ChannelException extends \RuntimeException +final class ChannelException extends RidgeException { + /** + * @return self + */ + public static function getInProgress(): self + { + return new self("Another 'basic.get' already in progress. You should use 'basic.consume' instead of multiple 'basic.get'."); + } } diff --git a/src/Exception/ClassInvalid.php b/src/Exception/ClassInvalid.php index 4ad9830..6a2d14b 100644 --- a/src/Exception/ClassInvalid.php +++ b/src/Exception/ClassInvalid.php @@ -12,33 +12,13 @@ namespace PHPinnacle\Ridge\Exception; -/** - * Peer sent frame with invalid method class id. - * - * @author Jakub Kulhan - */ -class ClassInvalid extends ProtocolException +final class ClassInvalid extends ProtocolException { - /** - * @var int - */ - private $classId; - /** * @param int $classId */ public function __construct(int $classId) { parent::__construct("Unhandled method frame class '{$classId}'."); - - $this->classId = $classId; - } - - /** - * @return int - */ - public function getClassId(): int - { - return $this->classId; } } diff --git a/src/Exception/ClientException.php b/src/Exception/ClientException.php index b34a94f..e0e1e3b 100644 --- a/src/Exception/ClientException.php +++ b/src/Exception/ClientException.php @@ -12,6 +12,37 @@ namespace PHPinnacle\Ridge\Exception; -class ClientException extends \RuntimeException +use PHPinnacle\Ridge\Protocol; + +final class ClientException extends RidgeException { + /** + * @param Protocol\AbstractFrame $frame + * + * @return self + */ + public static function unknownFrameClass(Protocol\AbstractFrame $frame): self + { + return new self("Unhandled frame '" . \get_class($frame) . "'."); + } + + /** + * @param Protocol\AbstractFrame $frame + * + * @return self + */ + public static function unknownMethodFrame(Protocol\AbstractFrame $frame): self + { + return new self("Unhandled method frame '" . \get_class($frame) . "'."); + } + + /** + * @param Protocol\ConnectionCloseFrame $frame + * + * @return self + */ + public static function connectionClosed(Protocol\ConnectionCloseFrame $frame): self + { + return new self("Connection closed by server: " . $frame->replyText, $frame->replyCode); + } } diff --git a/src/Exception/MethodInvalid.php b/src/Exception/MethodInvalid.php index 8fd611b..b20703a 100644 --- a/src/Exception/MethodInvalid.php +++ b/src/Exception/MethodInvalid.php @@ -12,23 +12,8 @@ namespace PHPinnacle\Ridge\Exception; -/** - * Peer sent frame with invalid method id. - * - * @author Jakub Kulhan - */ -class MethodInvalid extends ProtocolException +final class MethodInvalid extends ProtocolException { - /** - * @var int - */ - private $classId; - - /** - * @var int - */ - private $methodId; - /** * @param int $classId * @param int $methodId @@ -36,24 +21,5 @@ class MethodInvalid extends ProtocolException public function __construct(int $classId, int $methodId) { parent::__construct("Unhandled method frame method '{$methodId}' in class '{$classId}'."); - - $this->classId = $classId; - $this->methodId = $methodId; - } - - /** - * @return int - */ - public function getClassId(): int - { - return $this->classId; - } - - /** - * @return int - */ - public function getMethodId(): int - { - return $this->methodId; } } diff --git a/src/Exception/ProtocolException.php b/src/Exception/ProtocolException.php index 71d4a4b..19c7f80 100644 --- a/src/Exception/ProtocolException.php +++ b/src/Exception/ProtocolException.php @@ -12,6 +12,72 @@ namespace PHPinnacle\Ridge\Exception; -class ProtocolException extends \RuntimeException +use PHPinnacle\Ridge\Constants; +use PHPinnacle\Ridge\Protocol\AbstractFrame; + +class ProtocolException extends RidgeException { + /** + * @param int $frameEnd + * + * @return self + */ + public static function invalidFrameEnd(int $frameEnd): self + { + $message = \sprintf("Frame end byte invalid - expected 0x%02x, got 0x%02x.", Constants::FRAME_END, $frameEnd); + + return new self($message); + } + + /** + * @param int $type + * + * @return self + */ + public static function unknownFrameType(int $type): self + { + return new self("Unhandled frame type '{$type}'."); + } + + /** + * @param AbstractFrame $frame + * + * @return self + */ + public static function unknownFrameClass(AbstractFrame $frame): self + { + return new self("Unhandled frame '" . get_class($frame) . "'."); + } + + /** + * @return self + */ + public static function notEmptyHeartbeat(): self + { + return new self("Heartbeat frame must be empty."); + } + + /** + * @param int $fieldType + * + * @return self + */ + public static function unknownFieldType(int $fieldType): self + { + $cType = \ctype_print(\chr($fieldType)) ? " ('" . \chr($fieldType) . "')" : ""; + + return new self(\sprintf("Unhandled field type 0x%02x%s.", $fieldType, $cType)); + } + + /** + * @param int $fieldType + * + * @return self + */ + public static function unknownValueType($value): self + { + $class = (\is_object($value) ? " (class " . \get_class($value) . ")" : ""); + + return new self(\sprintf("Unhandled value type '%s'%s.", \gettype($value), $class)); + } } diff --git a/src/Exception/RidgeException.php b/src/Exception/RidgeException.php new file mode 100644 index 0000000..c7a2c1e --- /dev/null +++ b/src/Exception/RidgeException.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types = 1); + +namespace PHPinnacle\Ridge\Exception; + +abstract class RidgeException extends \RuntimeException +{ +} diff --git a/src/Heartbeat.php b/src/Heartbeat.php new file mode 100644 index 0000000..d271dcf --- /dev/null +++ b/src/Heartbeat.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types = 1); + +namespace PHPinnacle\Ridge; + +use Amp\Loop; + +final class Heartbeat +{ + /** + * @var Connection + */ + private $connection; + + /** + * @var string + */ + private $watcher; + + /** + * @var int + */ + private $lastWrite; + + /** + * @param Connection $connection + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * @param int $interval + * + * @return void + */ + public function enable(int $interval): void + { + $milliseconds = $interval * 1000; + + $this->watcher = Loop::repeat($milliseconds, function() use ($milliseconds) { + $currentTime = $this->current(); + $lastWrite = $this->lastWrite; + + if ($lastWrite === null) { + $lastWrite = $currentTime; + } + + /** @var int $nextHeartbeat */ + $nextHeartbeat = $lastWrite + $milliseconds; + + if ($currentTime >= $nextHeartbeat) { + yield $this->connection->send(new Protocol\HeartbeatFrame); + } + + unset($currentTime, $lastWrite, $nextHeartbeat); + }); + } + + /** + * @return void + */ + public function disable(): void + { + if ($this->watcher !== null) { + Loop::cancel($this->watcher); + + $this->watcher = null; + } + } + + /** + * @return void + */ + public function touch(): void + { + $this->lastWrite = $this->current(); + } + + /** + * @return int + */ + private function current(): int + { + return (int) \round(\microtime(true) * 1000); + } +} diff --git a/src/Message.php b/src/Message.php index 29890d0..11eba1a 100644 --- a/src/Message.php +++ b/src/Message.php @@ -12,94 +12,136 @@ namespace PHPinnacle\Ridge; -/** - * Convenience crate for transferring messages through app. - * - * @author Jakub Kulhan - */ -class Message +final class Message { /** * @var string */ - public $consumerTag; + private $content; /** - * @var int + * @var string */ - public $deliveryTag; + private $exchange; /** - * @var boolean + * @var string */ - public $redelivered; + private $routingKey; /** * @var string */ - public $exchange; + private $consumerTag; /** - * @var string + * @var int */ - public $routingKey; + private $deliveryTag; /** - * @var array + * @var boolean */ - public $headers; + private $redelivered; /** - * @var string + * @var array */ - public $content; + private $headers; /** - * @param string $consumerTag - * @param string $deliveryTag - * @param boolean $redelivered * @param string $exchange + * @param string $content * @param string $routingKey + * @param string $consumerTag + * @param int $deliveryTag + * @param boolean $redelivered * @param array $headers - * @param string $content */ - public function __construct($consumerTag, $deliveryTag, $redelivered, $exchange, $routingKey, array $headers, $content) - { + public function __construct( + string $content, + string $exchange, + string $routingKey = null, + string $consumerTag = null, + int $deliveryTag = null, + bool $redelivered = false, + array $headers = [] + ) { + $this->content = $content; + $this->exchange = $exchange; + $this->routingKey = $routingKey; $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 + * @return string */ - public function getHeader($name, $default = null) + public function content(): string { - if (isset($this->headers[$name])) { - return $this->headers[$name]; - } else { - return $default; - } + return $this->content; } /** - * Returns TRUE if message has given header. + * @return string + */ + public function exchange(): string + { + return $this->exchange; + } + + /** + * @return string + */ + public function routingKey(): string + { + return $this->routingKey; + } + + /** + * @return string + */ + public function consumerTag(): string + { + return $this->consumerTag; + } + + /** + * @return int + */ + public function deliveryTag(): int + { + return $this->deliveryTag; + } + + /** + * @return bool + */ + public function redelivered(): bool + { + return $this->redelivered; + } + + /** + * @return array + */ + public function headers(): array + { + return $this->headers; + } + + /** + * Returns header or default value. * * @param string $name + * @param mixed $default * - * @return bool + * @return mixed */ - public function hasHeader($name): bool + public function header(string $name, $default = null) { - return isset($this->headers[$name]); + return $this->headers[$name] ?? $default; } } diff --git a/src/Protocol/AbstractFrame.php b/src/Protocol/AbstractFrame.php index 5822809..850c389 100644 --- a/src/Protocol/AbstractFrame.php +++ b/src/Protocol/AbstractFrame.php @@ -12,24 +12,6 @@ namespace PHPinnacle\Ridge\Protocol; -/** - * Base class for all AMQP protocol frames. - * - * Frame classes' sole purpose is to be crate for transferring data. All fields are public because of calls to getters - * and setters are ridiculously slow. - * - * You should not mangle with frame's fields, everything should be handled by classes in {@namespace \Bunny\Protocol}. - * - * Frame's wire format: - * - * 0 1 3 7 size+7 size+8 - * +------+---------+--------------+-----------------+-----------+ - * | type | channel | size | ... payload ... | frame-end | - * +------+---------+--------------+-----------------+-----------+ - * uint8 uint16 uint32 size octets uint8 - * - * @author Jakub Kulhan - */ abstract class AbstractFrame { /** diff --git a/src/Protocol/AccessRequestFrame.php b/src/Protocol/AccessRequestFrame.php index 4b53cb0..5163b82 100644 --- a/src/Protocol/AccessRequestFrame.php +++ b/src/Protocol/AccessRequestFrame.php @@ -1,4 +1,12 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; diff --git a/src/Protocol/AccessRequestOkFrame.php b/src/Protocol/AccessRequestOkFrame.php index b1938e5..02a429a 100644 --- a/src/Protocol/AccessRequestOkFrame.php +++ b/src/Protocol/AccessRequestOkFrame.php @@ -1,16 +1,17 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'access.request-ok' (class #30, method #11) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class AccessRequestOkFrame extends MethodFrame { /** diff --git a/src/Protocol/BasicAckFrame.php b/src/Protocol/BasicAckFrame.php index d525a72..b2a2706 100644 --- a/src/Protocol/BasicAckFrame.php +++ b/src/Protocol/BasicAckFrame.php @@ -1,16 +1,17 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.ack' (class #60, method #80) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicAckFrame extends MethodFrame { /** diff --git a/src/Protocol/BasicCancelFrame.php b/src/Protocol/BasicCancelFrame.php index 4489e7e..c2d1318 100644 --- a/src/Protocol/BasicCancelFrame.php +++ b/src/Protocol/BasicCancelFrame.php @@ -1,16 +1,17 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.cancel' (class #60, method #30) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicCancelFrame extends MethodFrame { /** diff --git a/src/Protocol/BasicCancelOkFrame.php b/src/Protocol/BasicCancelOkFrame.php index a54a850..e091e22 100644 --- a/src/Protocol/BasicCancelOkFrame.php +++ b/src/Protocol/BasicCancelOkFrame.php @@ -1,16 +1,17 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.cancel-ok' (class #60, method #31) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicCancelOkFrame extends MethodFrame { /** diff --git a/src/Protocol/BasicConsumeFrame.php b/src/Protocol/BasicConsumeFrame.php index 7731cdf..fd52dc0 100644 --- a/src/Protocol/BasicConsumeFrame.php +++ b/src/Protocol/BasicConsumeFrame.php @@ -1,16 +1,17 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.consume' (class #60, method #20) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicConsumeFrame extends MethodFrame { /** diff --git a/src/Protocol/BasicConsumeOkFrame.php b/src/Protocol/BasicConsumeOkFrame.php index db22e3d..b230463 100644 --- a/src/Protocol/BasicConsumeOkFrame.php +++ b/src/Protocol/BasicConsumeOkFrame.php @@ -1,25 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.consume-ok' (class #60, method #21) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicConsumeOkFrame extends MethodFrame { - - /** @var string */ + /** + * @var string + */ public $consumerTag; public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_CONSUME_OK); } - } diff --git a/src/Protocol/BasicDeliverFrame.php b/src/Protocol/BasicDeliverFrame.php index 397a038..3dd2ed9 100644 --- a/src/Protocol/BasicDeliverFrame.php +++ b/src/Protocol/BasicDeliverFrame.php @@ -1,37 +1,46 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.deliver' (class #60, method #60) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicDeliverFrame extends MethodFrame { - - /** @var string */ + /** + * @var string + */ public $consumerTag; - /** @var int */ + /** + * @var int + */ public $deliveryTag; - /** @var boolean */ + /** + * @var boolean + */ public $redelivered = false; - /** @var string */ + /** + * @var string + */ public $exchange; - /** @var string */ + /** + * @var string + */ public $routingKey; public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_DELIVER); } - } diff --git a/src/Protocol/BasicGetEmptyFrame.php b/src/Protocol/BasicGetEmptyFrame.php index 0f54fe1..6b1944c 100644 --- a/src/Protocol/BasicGetEmptyFrame.php +++ b/src/Protocol/BasicGetEmptyFrame.php @@ -1,25 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.get-empty' (class #60, method #72) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicGetEmptyFrame extends MethodFrame { - - /** @var string */ + /** + * @var string + */ public $clusterId = ''; public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_GET_EMPTY); } - } diff --git a/src/Protocol/BasicGetFrame.php b/src/Protocol/BasicGetFrame.php index 212cf2c..1715a3e 100644 --- a/src/Protocol/BasicGetFrame.php +++ b/src/Protocol/BasicGetFrame.php @@ -1,31 +1,36 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.get' (class #60, method #70) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicGetFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $queue = ''; - /** @var boolean */ + /** + * @var boolean + */ public $noAck = false; public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_GET); } - } diff --git a/src/Protocol/BasicGetOkFrame.php b/src/Protocol/BasicGetOkFrame.php index 35106e0..66e5abf 100644 --- a/src/Protocol/BasicGetOkFrame.php +++ b/src/Protocol/BasicGetOkFrame.php @@ -1,16 +1,17 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.get-ok' (class #60, method #71) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicGetOkFrame extends MethodFrame { /** diff --git a/src/Protocol/BasicNackFrame.php b/src/Protocol/BasicNackFrame.php index 459e169..16ab3de 100644 --- a/src/Protocol/BasicNackFrame.php +++ b/src/Protocol/BasicNackFrame.php @@ -1,31 +1,36 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.nack' (class #60, method #120) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicNackFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $deliveryTag = 0; - /** @var boolean */ + /** + * @var boolean + */ public $multiple = false; - /** @var boolean */ + /** + * @var boolean + */ public $requeue = true; public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_NACK); } - } diff --git a/src/Protocol/BasicPublishFrame.php b/src/Protocol/BasicPublishFrame.php index 5a68d27..16d4398 100644 --- a/src/Protocol/BasicPublishFrame.php +++ b/src/Protocol/BasicPublishFrame.php @@ -1,37 +1,46 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.publish' (class #60, method #40) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicPublishFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $exchange = ''; - /** @var string */ + /** + * @var string + */ public $routingKey = ''; - /** @var boolean */ + /** + * @var boolean + */ public $mandatory = false; - /** @var boolean */ + /** + * @var boolean + */ public $immediate = false; public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_PUBLISH); } - } diff --git a/src/Protocol/BasicQosFrame.php b/src/Protocol/BasicQosFrame.php index b4a1ea5..f1f245f 100644 --- a/src/Protocol/BasicQosFrame.php +++ b/src/Protocol/BasicQosFrame.php @@ -1,31 +1,36 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.qos' (class #60, method #10) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicQosFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $prefetchSize = 0; - /** @var int */ + /** + * @var int + */ public $prefetchCount = 0; - /** @var boolean */ + /** + * @var boolean + */ public $global = false; public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_QOS); } - } diff --git a/src/Protocol/BasicQosOkFrame.php b/src/Protocol/BasicQosOkFrame.php index 5034d2f..b6fe79f 100644 --- a/src/Protocol/BasicQosOkFrame.php +++ b/src/Protocol/BasicQosOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.qos-ok' (class #60, method #11) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicQosOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_QOS_OK); } - } diff --git a/src/Protocol/BasicRecoverAsyncFrame.php b/src/Protocol/BasicRecoverAsyncFrame.php index e1adfc4..2493319 100644 --- a/src/Protocol/BasicRecoverAsyncFrame.php +++ b/src/Protocol/BasicRecoverAsyncFrame.php @@ -1,25 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.recover-async' (class #60, method #100) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicRecoverAsyncFrame extends MethodFrame { - - /** @var boolean */ + /** + * @var boolean + */ public $requeue = false; public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_RECOVER_ASYNC); } - } diff --git a/src/Protocol/BasicRecoverFrame.php b/src/Protocol/BasicRecoverFrame.php index 5868014..b7fb6d9 100644 --- a/src/Protocol/BasicRecoverFrame.php +++ b/src/Protocol/BasicRecoverFrame.php @@ -1,25 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.recover' (class #60, method #110) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicRecoverFrame extends MethodFrame { - - /** @var boolean */ + /** + * @var boolean + */ public $requeue = false; public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_RECOVER); } - } diff --git a/src/Protocol/BasicRecoverOkFrame.php b/src/Protocol/BasicRecoverOkFrame.php index a1fbf18..1e2a85d 100644 --- a/src/Protocol/BasicRecoverOkFrame.php +++ b/src/Protocol/BasicRecoverOkFrame.php @@ -1,24 +1,21 @@ - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.recover-ok' (class #60, method #111) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicRecoverOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_RECOVER_OK); } - } diff --git a/src/Protocol/BasicRejectFrame.php b/src/Protocol/BasicRejectFrame.php index 6a967cc..7338abe 100644 --- a/src/Protocol/BasicRejectFrame.php +++ b/src/Protocol/BasicRejectFrame.php @@ -1,16 +1,17 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.reject' (class #60, method #90) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicRejectFrame extends MethodFrame { /** diff --git a/src/Protocol/BasicReturnFrame.php b/src/Protocol/BasicReturnFrame.php index 384b798..0357752 100644 --- a/src/Protocol/BasicReturnFrame.php +++ b/src/Protocol/BasicReturnFrame.php @@ -1,34 +1,41 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'basic.return' (class #60, method #50) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class BasicReturnFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $replyCode; - /** @var string */ + /** + * @var string + */ public $replyText = ''; - /** @var string */ + /** + * @var string + */ public $exchange; - /** @var string */ + /** + * @var string + */ public $routingKey; public function __construct() { parent::__construct(Constants::CLASS_BASIC, Constants::METHOD_BASIC_RETURN); } - } diff --git a/src/Protocol/ChannelCloseFrame.php b/src/Protocol/ChannelCloseFrame.php index 7a52edf..6ea4740 100644 --- a/src/Protocol/ChannelCloseFrame.php +++ b/src/Protocol/ChannelCloseFrame.php @@ -1,34 +1,41 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'channel.close' (class #20, method #40) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ChannelCloseFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $replyCode; - /** @var string */ + /** + * @var string + */ public $replyText = ''; - /** @var int */ + /** + * @var int + */ public $closeClassId; - /** @var int */ + /** + * @var int + */ public $closeMethodId; public function __construct() { parent::__construct(Constants::CLASS_CHANNEL, Constants::METHOD_CHANNEL_CLOSE); } - } diff --git a/src/Protocol/ChannelCloseOkFrame.php b/src/Protocol/ChannelCloseOkFrame.php index 03a5b6b..435e986 100644 --- a/src/Protocol/ChannelCloseOkFrame.php +++ b/src/Protocol/ChannelCloseOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'channel.close-ok' (class #20, method #41) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ChannelCloseOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_CHANNEL, Constants::METHOD_CHANNEL_CLOSE_OK); } - } diff --git a/src/Protocol/ChannelFlowFrame.php b/src/Protocol/ChannelFlowFrame.php index 4bc4b5b..f076017 100644 --- a/src/Protocol/ChannelFlowFrame.php +++ b/src/Protocol/ChannelFlowFrame.php @@ -1,25 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'channel.flow' (class #20, method #20) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ChannelFlowFrame extends MethodFrame { - - /** @var boolean */ + /** + * @var boolean + */ public $active; public function __construct() { parent::__construct(Constants::CLASS_CHANNEL, Constants::METHOD_CHANNEL_FLOW); } - } diff --git a/src/Protocol/ChannelFlowOkFrame.php b/src/Protocol/ChannelFlowOkFrame.php index 53dc9e1..6e364a3 100644 --- a/src/Protocol/ChannelFlowOkFrame.php +++ b/src/Protocol/ChannelFlowOkFrame.php @@ -1,25 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'channel.flow-ok' (class #20, method #21) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ChannelFlowOkFrame extends MethodFrame { - - /** @var boolean */ + /** + * @var boolean + */ public $active; public function __construct() { parent::__construct(Constants::CLASS_CHANNEL, Constants::METHOD_CHANNEL_FLOW_OK); } - } diff --git a/src/Protocol/ChannelOpenFrame.php b/src/Protocol/ChannelOpenFrame.php index 601250f..987a0fe 100644 --- a/src/Protocol/ChannelOpenFrame.php +++ b/src/Protocol/ChannelOpenFrame.php @@ -1,25 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'channel.open' (class #20, method #10) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ChannelOpenFrame extends MethodFrame { - - /** @var string */ + /** + * @var string + */ public $outOfBand = ''; public function __construct() { parent::__construct(Constants::CLASS_CHANNEL, Constants::METHOD_CHANNEL_OPEN); } - } diff --git a/src/Protocol/ChannelOpenOkFrame.php b/src/Protocol/ChannelOpenOkFrame.php index fa19c7a..fc35e06 100644 --- a/src/Protocol/ChannelOpenOkFrame.php +++ b/src/Protocol/ChannelOpenOkFrame.php @@ -1,16 +1,17 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'channel.open-ok' (class #20, method #11) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ChannelOpenOkFrame extends MethodFrame { /** diff --git a/src/Protocol/ConfirmSelectFrame.php b/src/Protocol/ConfirmSelectFrame.php index 0c36410..966c9ab 100644 --- a/src/Protocol/ConfirmSelectFrame.php +++ b/src/Protocol/ConfirmSelectFrame.php @@ -1,25 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'confirm.select' (class #85, method #10) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConfirmSelectFrame extends MethodFrame { - - /** @var boolean */ + /** + * @var boolean + */ public $nowait = false; public function __construct() { parent::__construct(Constants::CLASS_CONFIRM, Constants::METHOD_CONFIRM_SELECT); } - } diff --git a/src/Protocol/ConfirmSelectOkFrame.php b/src/Protocol/ConfirmSelectOkFrame.php index d18b517..097a467 100644 --- a/src/Protocol/ConfirmSelectOkFrame.php +++ b/src/Protocol/ConfirmSelectOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'confirm.select-ok' (class #85, method #11) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConfirmSelectOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_CONFIRM, Constants::METHOD_CONFIRM_SELECT_OK); } - } diff --git a/src/Protocol/ConnectionBlockedFrame.php b/src/Protocol/ConnectionBlockedFrame.php index 4ec4f36..84a94da 100644 --- a/src/Protocol/ConnectionBlockedFrame.php +++ b/src/Protocol/ConnectionBlockedFrame.php @@ -1,20 +1,22 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.blocked' (class #10, method #60) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionBlockedFrame extends MethodFrame { - - /** @var string */ + /** + * @var string + */ public $reason = ''; public function __construct() @@ -22,5 +24,4 @@ public function __construct() parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_BLOCKED); $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionCloseFrame.php b/src/Protocol/ConnectionCloseFrame.php index 672ca37..0885a5d 100644 --- a/src/Protocol/ConnectionCloseFrame.php +++ b/src/Protocol/ConnectionCloseFrame.php @@ -1,35 +1,43 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.close' (class #10, method #50) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionCloseFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $replyCode; - /** @var string */ + /** + * @var string + */ public $replyText = ''; - /** @var int */ + /** + * @var int + */ public $closeClassId; - /** @var int */ + /** + * @var int + */ public $closeMethodId; public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_CLOSE); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionCloseOkFrame.php b/src/Protocol/ConnectionCloseOkFrame.php index 818ee58..7bcda03 100644 --- a/src/Protocol/ConnectionCloseOkFrame.php +++ b/src/Protocol/ConnectionCloseOkFrame.php @@ -1,23 +1,23 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.close-ok' (class #10, method #51) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionCloseOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_CLOSE_OK); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionOpenFrame.php b/src/Protocol/ConnectionOpenFrame.php index ebbfa01..9ea31fc 100644 --- a/src/Protocol/ConnectionOpenFrame.php +++ b/src/Protocol/ConnectionOpenFrame.php @@ -1,32 +1,38 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.open' (class #10, method #40) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionOpenFrame extends MethodFrame { - - /** @var string */ + /** + * @var string + */ public $virtualHost = '/'; - /** @var string */ + /** + * @var string + */ public $capabilities = ''; - /** @var boolean */ + /** + * @var boolean + */ public $insist = false; public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_OPEN); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionOpenOkFrame.php b/src/Protocol/ConnectionOpenOkFrame.php index f4d3902..51869b5 100644 --- a/src/Protocol/ConnectionOpenOkFrame.php +++ b/src/Protocol/ConnectionOpenOkFrame.php @@ -1,26 +1,28 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.open-ok' (class #10, method #41) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionOpenOkFrame extends MethodFrame { - - /** @var string */ + /** + * @var string + */ public $knownHosts = ''; public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_OPEN_OK); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionSecureFrame.php b/src/Protocol/ConnectionSecureFrame.php index d775be6..832076a 100644 --- a/src/Protocol/ConnectionSecureFrame.php +++ b/src/Protocol/ConnectionSecureFrame.php @@ -1,26 +1,28 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.secure' (class #10, method #20) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionSecureFrame extends MethodFrame { - - /** @var string */ + /** + * @var string + */ public $challenge; public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_SECURE); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionSecureOkFrame.php b/src/Protocol/ConnectionSecureOkFrame.php index 6d36516..3623996 100644 --- a/src/Protocol/ConnectionSecureOkFrame.php +++ b/src/Protocol/ConnectionSecureOkFrame.php @@ -1,26 +1,28 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.secure-ok' (class #10, method #21) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionSecureOkFrame extends MethodFrame { - - /** @var string */ + /** + * @var string + */ public $response; public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_SECURE_OK); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionStartFrame.php b/src/Protocol/ConnectionStartFrame.php index a12635d..df611ab 100644 --- a/src/Protocol/ConnectionStartFrame.php +++ b/src/Protocol/ConnectionStartFrame.php @@ -1,38 +1,48 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.start' (class #10, method #10) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionStartFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $versionMajor = 0; - /** @var int */ + /** + * @var int + */ public $versionMinor = 9; - /** @var array */ + /** + * @var array + */ public $serverProperties = []; - /** @var string */ + /** + * @var string + */ public $mechanisms = 'PLAIN'; - /** @var string */ + /** + * @var string + */ public $locales = 'en_US'; public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_START); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionStartOkFrame.php b/src/Protocol/ConnectionStartOkFrame.php index 30527c2..a232681 100644 --- a/src/Protocol/ConnectionStartOkFrame.php +++ b/src/Protocol/ConnectionStartOkFrame.php @@ -1,35 +1,43 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.start-ok' (class #10, method #11) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionStartOkFrame extends MethodFrame { - - /** @var array */ + /** + * @var array + */ public $clientProperties = []; - /** @var string */ + /** + * @var string + */ public $mechanism = 'PLAIN'; - /** @var string */ + /** + * @var string + */ public $response; - /** @var string */ + /** + * @var string + */ public $locale = 'en_US'; public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_START_OK); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionTuneFrame.php b/src/Protocol/ConnectionTuneFrame.php index 19ea0d3..92cf6e0 100644 --- a/src/Protocol/ConnectionTuneFrame.php +++ b/src/Protocol/ConnectionTuneFrame.php @@ -1,32 +1,38 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.tune' (class #10, method #30) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionTuneFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $channelMax = 0; - /** @var int */ + /** + * @var int + */ public $frameMax = 0; - /** @var int */ + /** + * @var int + */ public $heartbeat = 0; public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_TUNE); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionTuneOkFrame.php b/src/Protocol/ConnectionTuneOkFrame.php index b2b719b..fb1994a 100644 --- a/src/Protocol/ConnectionTuneOkFrame.php +++ b/src/Protocol/ConnectionTuneOkFrame.php @@ -1,32 +1,38 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.tune-ok' (class #10, method #31) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionTuneOkFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $channelMax = 0; - /** @var int */ + /** + * @var int + */ public $frameMax = 0; - /** @var int */ + /** + * @var int + */ public $heartbeat = 0; public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_TUNE_OK); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ConnectionUnblockedFrame.php b/src/Protocol/ConnectionUnblockedFrame.php index 9fe3513..5430e40 100644 --- a/src/Protocol/ConnectionUnblockedFrame.php +++ b/src/Protocol/ConnectionUnblockedFrame.php @@ -1,23 +1,23 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'connection.unblocked' (class #10, method #61) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ConnectionUnblockedFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_CONNECTION, Constants::METHOD_CONNECTION_UNBLOCKED); + $this->channel = Constants::CONNECTION_CHANNEL; } - } diff --git a/src/Protocol/ContentBodyFrame.php b/src/Protocol/ContentBodyFrame.php index a4cf12b..f0e188c 100644 --- a/src/Protocol/ContentBodyFrame.php +++ b/src/Protocol/ContentBodyFrame.php @@ -1,25 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * Content body AMQP frame. - * - * Payload is opaque content being transferred. Size and number of body frames depends on preceding header frame - * and it's body-size field. - * - * - * - * @author Jakub Kulhan - */ class ContentBodyFrame extends AbstractFrame { - public function __construct($channel = null, $payloadSize = null, $payload = null) { parent::__construct(Constants::FRAME_BODY, $channel, $payloadSize, $payload); } - } diff --git a/src/Protocol/ContentHeaderFrame.php b/src/Protocol/ContentHeaderFrame.php index aeb882d..fe61a7c 100644 --- a/src/Protocol/ContentHeaderFrame.php +++ b/src/Protocol/ContentHeaderFrame.php @@ -1,208 +1,233 @@ * - * 0 2 4 12 14 - * ----+----------+--------+-----------+-------+----------------- - * ... | class-id | weight | body-size | flags | property-list... - * ----+----------+--------+-----------+-------+----------------- - * uint16 uint16 uint64 uint16 - * - * - * @author Jakub Kulhan + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -class ContentHeaderFrame extends AbstractFrame -{ - - const FLAG_CONTENT_TYPE = 0x8000; - - const FLAG_CONTENT_ENCODING = 0x4000; - - const FLAG_HEADERS = 0x2000; - - const FLAG_DELIVERY_MODE = 0x1000; - - const FLAG_PRIORITY = 0x0800; - - const FLAG_CORRELATION_ID = 0x0400; - - const FLAG_REPLY_TO = 0x0200; - - const FLAG_EXPIRATION = 0x0100; - - const FLAG_MESSAGE_ID = 0x0080; - - const FLAG_TIMESTAMP = 0x0040; - - const FLAG_TYPE = 0x0020; - const FLAG_USER_ID = 0x0010; +namespace PHPinnacle\Ridge\Protocol; - const FLAG_APP_ID = 0x0008; +use PHPinnacle\Ridge\Constants; - const FLAG_CLUSTER_ID = 0x0004; +class ContentHeaderFrame extends AbstractFrame +{ + const + FLAG_CONTENT_TYPE = 0x8000, + FLAG_CONTENT_ENCODING = 0x4000, + FLAG_HEADERS = 0x2000, + FLAG_DELIVERY_MODE = 0x1000, + FLAG_PRIORITY = 0x0800, + FLAG_CORRELATION_ID = 0x0400, + FLAG_REPLY_TO = 0x0200, + FLAG_EXPIRATION = 0x0100, + FLAG_MESSAGE_ID = 0x0080, + FLAG_TIMESTAMP = 0x0040, + FLAG_TYPE = 0x0020, + FLAG_USER_ID = 0x0010, + FLAG_APP_ID = 0x0008, + FLAG_CLUSTER_ID = 0x0004 + ; - /** @var int */ + /** + * @var int + */ public $classId = Constants::CLASS_BASIC; - /** @var int */ + /** + * @var int + */ public $weight = 0; - /** @var int */ + /** + * @var int + */ public $bodySize; - /** @var int */ + /** + * @var int + */ public $flags = 0; - /** @var string */ + /** + * @var string + */ public $contentType; - /** @var string */ + /** + * @var string + */ public $contentEncoding; - /** @var array */ + /** + * @var array + */ public $headers; - /** @var int */ + /** + * @var int + */ public $deliveryMode; - /** @var int */ + /** + * @var int + */ public $priority; - /** @var string */ + /** + * @var string + */ public $correlationId; - /** @var string */ + /** + * @var string + */ public $replyTo; - /** @var string */ + /** + * @var string + */ public $expiration; - /** @var string */ + /** + * @var string + */ public $messageId; - /** @var \DateTime */ + /** + * @var \DateTimeInterface + */ public $timestamp; - /** @var string */ + /** + * @var string + */ public $typeHeader; - /** @var string */ + /** + * @var string + */ public $userId; - /** @var string */ + /** + * @var string + */ public $appId; - /** @var string */ - public $clusterId; - /** - * Constructor. + * @var string */ + public $clusterId; + public function __construct() { parent::__construct(Constants::FRAME_HEADER); } /** - * Creates frame from array. - * * @param array $headers * - * @return ContentHeaderFrame + * @return self */ - public static function fromArray(array $headers) + public static function fromArray(array $headers): self { - $instance = new static(); + $instance = new static; if (isset($headers["content-type"])) { - $instance->flags |= ContentHeaderFrame::FLAG_CONTENT_TYPE; + $instance->flags |= self::FLAG_CONTENT_TYPE; $instance->contentType = $headers["content-type"]; + unset($headers["content-type"]); } if (isset($headers["content-encoding"])) { - $instance->flags |= ContentHeaderFrame::FLAG_CONTENT_ENCODING; + $instance->flags |= self::FLAG_CONTENT_ENCODING; $instance->contentEncoding = $headers["content-encoding"]; + unset($headers["content-encoding"]); } if (isset($headers["delivery-mode"])) { - $instance->flags |= ContentHeaderFrame::FLAG_DELIVERY_MODE; + $instance->flags |= self::FLAG_DELIVERY_MODE; $instance->deliveryMode = $headers["delivery-mode"]; + unset($headers["delivery-mode"]); } if (isset($headers["priority"])) { - $instance->flags |= ContentHeaderFrame::FLAG_PRIORITY; + $instance->flags |= self::FLAG_PRIORITY; $instance->priority = $headers["priority"]; + unset($headers["priority"]); } if (isset($headers["correlation-id"])) { - $instance->flags |= ContentHeaderFrame::FLAG_CORRELATION_ID; + $instance->flags |= self::FLAG_CORRELATION_ID; $instance->correlationId = $headers["correlation-id"]; + unset($headers["correlation-id"]); } if (isset($headers["reply-to"])) { - $instance->flags |= ContentHeaderFrame::FLAG_REPLY_TO; + $instance->flags |= self::FLAG_REPLY_TO; $instance->replyTo = $headers["reply-to"]; + unset($headers["reply-to"]); } if (isset($headers["expiration"])) { - $instance->flags |= ContentHeaderFrame::FLAG_EXPIRATION; + $instance->flags |= self::FLAG_EXPIRATION; $instance->expiration = $headers["expiration"]; + unset($headers["expiration"]); } if (isset($headers["message-id"])) { - $instance->flags |= ContentHeaderFrame::FLAG_MESSAGE_ID; + $instance->flags |= self::FLAG_MESSAGE_ID; $instance->messageId = $headers["message-id"]; + unset($headers["message-id"]); } if (isset($headers["timestamp"])) { - $instance->flags |= ContentHeaderFrame::FLAG_TIMESTAMP; + $instance->flags |= self::FLAG_TIMESTAMP; $instance->timestamp = $headers["timestamp"]; + unset($headers["timestamp"]); } if (isset($headers["type"])) { - $instance->flags |= ContentHeaderFrame::FLAG_TYPE; + $instance->flags |= self::FLAG_TYPE; $instance->typeHeader = $headers["type"]; + unset($headers["type"]); } if (isset($headers["user-id"])) { - $instance->flags |= ContentHeaderFrame::FLAG_USER_ID; + $instance->flags |= self::FLAG_USER_ID; $instance->userId = $headers["user-id"]; + unset($headers["user-id"]); } if (isset($headers["app-id"])) { - $instance->flags |= ContentHeaderFrame::FLAG_APP_ID; + $instance->flags |= self::FLAG_APP_ID; $instance->appId = $headers["app-id"]; + unset($headers["app-id"]); } if (isset($headers["cluster-id"])) { - $instance->flags |= ContentHeaderFrame::FLAG_CLUSTER_ID; + $instance->flags |= self::FLAG_CLUSTER_ID; $instance->clusterId = $headers["cluster-id"]; + unset($headers["cluster-id"]); } if (!empty($headers)) { - $instance->flags |= ContentHeaderFrame::FLAG_HEADERS; + $instance->flags |= self::FLAG_HEADERS; $instance->headers = $headers; } @@ -210,17 +235,11 @@ public static function fromArray(array $headers) } /** - * Inverse function of {@link fromArray()} - * * @return array */ - public function toArray() + public function toArray(): array { - $headers = $this->headers; - - if ($headers === null) { - $headers = []; - } + $headers = $this->headers ?: []; if ($this->contentType !== null) { $headers["content-type"] = $this->contentType; @@ -276,5 +295,4 @@ public function toArray() return $headers; } - } diff --git a/src/Protocol/ExchangeBindFrame.php b/src/Protocol/ExchangeBindFrame.php index c3a1500..43be6ed 100644 --- a/src/Protocol/ExchangeBindFrame.php +++ b/src/Protocol/ExchangeBindFrame.php @@ -1,40 +1,51 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'exchange.bind' (class #40, method #30) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ExchangeBindFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $destination; - /** @var string */ + /** + * @var string + */ public $source; - /** @var string */ + /** + * @var string + */ public $routingKey = ''; - /** @var boolean */ + /** + * @var boolean + */ public $nowait = false; - /** @var array */ + /** + * @var array + */ public $arguments = []; public function __construct() { parent::__construct(Constants::CLASS_EXCHANGE, Constants::METHOD_EXCHANGE_BIND); } - } diff --git a/src/Protocol/ExchangeBindOkFrame.php b/src/Protocol/ExchangeBindOkFrame.php index 94fe76b..82054d7 100644 --- a/src/Protocol/ExchangeBindOkFrame.php +++ b/src/Protocol/ExchangeBindOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'exchange.bind-ok' (class #40, method #31) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ExchangeBindOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_EXCHANGE, Constants::METHOD_EXCHANGE_BIND_OK); } - } diff --git a/src/Protocol/ExchangeDeclareFrame.php b/src/Protocol/ExchangeDeclareFrame.php index 08e735c..bed337e 100644 --- a/src/Protocol/ExchangeDeclareFrame.php +++ b/src/Protocol/ExchangeDeclareFrame.php @@ -1,49 +1,66 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'exchange.declare' (class #40, method #10) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ExchangeDeclareFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $exchange; - /** @var string */ + /** + * @var string + */ public $exchangeType = 'direct'; - /** @var boolean */ + /** + * @var boolean + */ public $passive = false; - /** @var boolean */ + /** + * @var boolean + */ public $durable = false; - /** @var boolean */ + /** + * @var boolean + */ public $autoDelete = false; - /** @var boolean */ + /** + * @var boolean + */ public $internal = false; - /** @var boolean */ + /** + * @var boolean + */ public $nowait = false; - /** @var array */ + /** + * @var array + */ public $arguments = []; public function __construct() { parent::__construct(Constants::CLASS_EXCHANGE, Constants::METHOD_EXCHANGE_DECLARE); } - } diff --git a/src/Protocol/ExchangeDeclareOkFrame.php b/src/Protocol/ExchangeDeclareOkFrame.php index 2913fca..10b3efa 100644 --- a/src/Protocol/ExchangeDeclareOkFrame.php +++ b/src/Protocol/ExchangeDeclareOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'exchange.declare-ok' (class #40, method #11) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ExchangeDeclareOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_EXCHANGE, Constants::METHOD_EXCHANGE_DECLARE_OK); } - } diff --git a/src/Protocol/ExchangeDeleteFrame.php b/src/Protocol/ExchangeDeleteFrame.php index 3a267a6..e759f55 100644 --- a/src/Protocol/ExchangeDeleteFrame.php +++ b/src/Protocol/ExchangeDeleteFrame.php @@ -1,34 +1,41 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'exchange.delete' (class #40, method #20) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ExchangeDeleteFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $exchange; - /** @var boolean */ + /** + * @var boolean + */ public $ifUnused = false; - /** @var boolean */ + /** + * @var boolean + */ public $nowait = false; public function __construct() { parent::__construct(Constants::CLASS_EXCHANGE, Constants::METHOD_EXCHANGE_DELETE); } - } diff --git a/src/Protocol/ExchangeDeleteOkFrame.php b/src/Protocol/ExchangeDeleteOkFrame.php index 114ccae..8817920 100644 --- a/src/Protocol/ExchangeDeleteOkFrame.php +++ b/src/Protocol/ExchangeDeleteOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'exchange.delete-ok' (class #40, method #21) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ExchangeDeleteOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_EXCHANGE, Constants::METHOD_EXCHANGE_DELETE_OK); } - } diff --git a/src/Protocol/ExchangeUnbindFrame.php b/src/Protocol/ExchangeUnbindFrame.php index 30ec8e6..4d99999 100644 --- a/src/Protocol/ExchangeUnbindFrame.php +++ b/src/Protocol/ExchangeUnbindFrame.php @@ -1,40 +1,51 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'exchange.unbind' (class #40, method #40) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ExchangeUnbindFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $destination; - /** @var string */ + /** + * @var string + */ public $source; - /** @var string */ + /** + * @var string + */ public $routingKey = ''; - /** @var boolean */ + /** + * @var boolean + */ public $nowait = false; - /** @var array */ + /** + * @var array + */ public $arguments = []; public function __construct() { parent::__construct(Constants::CLASS_EXCHANGE, Constants::METHOD_EXCHANGE_UNBIND); } - } diff --git a/src/Protocol/ExchangeUnbindOkFrame.php b/src/Protocol/ExchangeUnbindOkFrame.php index b7209d9..5a7574f 100644 --- a/src/Protocol/ExchangeUnbindOkFrame.php +++ b/src/Protocol/ExchangeUnbindOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'exchange.unbind-ok' (class #40, method #51) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class ExchangeUnbindOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_EXCHANGE, Constants::METHOD_EXCHANGE_UNBIND_OK); } - } diff --git a/src/Protocol/HeartbeatFrame.php b/src/Protocol/HeartbeatFrame.php index caf7ab6..5f18b15 100644 --- a/src/Protocol/HeartbeatFrame.php +++ b/src/Protocol/HeartbeatFrame.php @@ -12,12 +12,12 @@ namespace PHPinnacle\Ridge\Protocol; -use PHPinnacle\Pinnacle\Transport\AmqpConstants; +use PHPinnacle\Ridge\Constants; class HeartbeatFrame extends AbstractFrame { public function __construct() { - parent::__construct(AmqpConstants::FRAME_HEARTBEAT, AmqpConstants::CONNECTION_CHANNEL, 0, ""); + parent::__construct(Constants::FRAME_HEARTBEAT, Constants::CONNECTION_CHANNEL, 0, ''); } } diff --git a/src/Protocol/MethodFrame.php b/src/Protocol/MethodFrame.php index 93589be..f270a40 100644 --- a/src/Protocol/MethodFrame.php +++ b/src/Protocol/MethodFrame.php @@ -1,24 +1,17 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * Method AMQP frame. - * - * Frame's payload wire format: - * - * - * 0 2 4 - * ----+----------+-----------+-------------------- - * ... | class-id | method-id | method-arguments... - * ----+----------+-----------+-------------------- - * uint16 uint16 - * - * - * @author Jakub Kulhan - */ class MethodFrame extends AbstractFrame { /** diff --git a/src/Protocol/QueueBindFrame.php b/src/Protocol/QueueBindFrame.php index 003559b..5a662cd 100644 --- a/src/Protocol/QueueBindFrame.php +++ b/src/Protocol/QueueBindFrame.php @@ -1,40 +1,51 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'queue.bind' (class #50, method #20) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class QueueBindFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $queue = ''; - /** @var string */ + /** + * @var string + */ public $exchange; - /** @var string */ + /** + * @var string + */ public $routingKey = ''; - /** @var boolean */ + /** + * @var boolean + */ public $nowait = false; - /** @var array */ + /** + * @var array + */ public $arguments = []; public function __construct() { parent::__construct(Constants::CLASS_QUEUE, Constants::METHOD_QUEUE_BIND); } - } diff --git a/src/Protocol/QueueBindOkFrame.php b/src/Protocol/QueueBindOkFrame.php index 2c5517b..c09c7d1 100644 --- a/src/Protocol/QueueBindOkFrame.php +++ b/src/Protocol/QueueBindOkFrame.php @@ -1,24 +1,21 @@ - + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'queue.bind-ok' (class #50, method #21) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class QueueBindOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_QUEUE, Constants::METHOD_QUEUE_BIND_OK); } - } diff --git a/src/Protocol/QueueDeclareFrame.php b/src/Protocol/QueueDeclareFrame.php index 121e639..78d2f52 100644 --- a/src/Protocol/QueueDeclareFrame.php +++ b/src/Protocol/QueueDeclareFrame.php @@ -1,46 +1,61 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'queue.declare' (class #50, method #10) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class QueueDeclareFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $queue = ''; - /** @var boolean */ + /** + * @var boolean + */ public $passive = false; - /** @var boolean */ + /** + * @var boolean + */ public $durable = false; - /** @var boolean */ + /** + * @var boolean + */ public $exclusive = false; - /** @var boolean */ + /** + * @var boolean + */ public $autoDelete = false; - /** @var boolean */ + /** + * @var boolean + */ public $nowait = false; - /** @var array */ + /** + * @var array + */ public $arguments = []; public function __construct() { parent::__construct(Constants::CLASS_QUEUE, Constants::METHOD_QUEUE_DECLARE); } - } diff --git a/src/Protocol/QueueDeclareOkFrame.php b/src/Protocol/QueueDeclareOkFrame.php index 7b37922..a9fb9d7 100644 --- a/src/Protocol/QueueDeclareOkFrame.php +++ b/src/Protocol/QueueDeclareOkFrame.php @@ -1,31 +1,36 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'queue.declare-ok' (class #50, method #11) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class QueueDeclareOkFrame extends MethodFrame { - - /** @var string */ + /** + * @var string + */ public $queue; - /** @var int */ + /** + * @var int + */ public $messageCount; - /** @var int */ + /** + * @var int + */ public $consumerCount; public function __construct() { parent::__construct(Constants::CLASS_QUEUE, Constants::METHOD_QUEUE_DECLARE_OK); } - } diff --git a/src/Protocol/QueueDeleteFrame.php b/src/Protocol/QueueDeleteFrame.php index e62813a..d479b3d 100644 --- a/src/Protocol/QueueDeleteFrame.php +++ b/src/Protocol/QueueDeleteFrame.php @@ -1,37 +1,46 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'queue.delete' (class #50, method #40) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class QueueDeleteFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $queue = ''; - /** @var boolean */ + /** + * @var boolean + */ public $ifUnused = false; - /** @var boolean */ + /** + * @var boolean + */ public $ifEmpty = false; - /** @var boolean */ + /** + * @var boolean + */ public $nowait = false; public function __construct() { parent::__construct(Constants::CLASS_QUEUE, Constants::METHOD_QUEUE_DELETE); } - } diff --git a/src/Protocol/QueueDeleteOkFrame.php b/src/Protocol/QueueDeleteOkFrame.php index 012664d..b0709f6 100644 --- a/src/Protocol/QueueDeleteOkFrame.php +++ b/src/Protocol/QueueDeleteOkFrame.php @@ -1,25 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'queue.delete-ok' (class #50, method #41) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class QueueDeleteOkFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $messageCount; public function __construct() { parent::__construct(Constants::CLASS_QUEUE, Constants::METHOD_QUEUE_DELETE_OK); } - } diff --git a/src/Protocol/QueuePurgeFrame.php b/src/Protocol/QueuePurgeFrame.php index 397197b..4b64449 100644 --- a/src/Protocol/QueuePurgeFrame.php +++ b/src/Protocol/QueuePurgeFrame.php @@ -1,31 +1,36 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'queue.purge' (class #50, method #30) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class QueuePurgeFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $queue = ''; - /** @var boolean */ + /** + * @var boolean + */ public $nowait = false; public function __construct() { parent::__construct(Constants::CLASS_QUEUE, Constants::METHOD_QUEUE_PURGE); } - } diff --git a/src/Protocol/QueuePurgeOkFrame.php b/src/Protocol/QueuePurgeOkFrame.php index 334a138..0360cdf 100644 --- a/src/Protocol/QueuePurgeOkFrame.php +++ b/src/Protocol/QueuePurgeOkFrame.php @@ -1,25 +1,26 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'queue.purge-ok' (class #50, method #31) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class QueuePurgeOkFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $messageCount; public function __construct() { parent::__construct(Constants::CLASS_QUEUE, Constants::METHOD_QUEUE_PURGE_OK); } - } diff --git a/src/Protocol/QueueUnbindFrame.php b/src/Protocol/QueueUnbindFrame.php index 3ac3240..2d1aa2e 100644 --- a/src/Protocol/QueueUnbindFrame.php +++ b/src/Protocol/QueueUnbindFrame.php @@ -1,37 +1,46 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'queue.unbind' (class #50, method #50) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class QueueUnbindFrame extends MethodFrame { - - /** @var int */ + /** + * @var int + */ public $reserved1 = 0; - /** @var string */ + /** + * @var string + */ public $queue = ''; - /** @var string */ + /** + * @var string + */ public $exchange; - /** @var string */ + /** + * @var string + */ public $routingKey = ''; - /** @var array */ + /** + * @var array + */ public $arguments = []; public function __construct() { parent::__construct(Constants::CLASS_QUEUE, Constants::METHOD_QUEUE_UNBIND); } - } diff --git a/src/Protocol/QueueUnbindOkFrame.php b/src/Protocol/QueueUnbindOkFrame.php index 9e77ff0..42bac65 100644 --- a/src/Protocol/QueueUnbindOkFrame.php +++ b/src/Protocol/QueueUnbindOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'queue.unbind-ok' (class #50, method #51) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class QueueUnbindOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_QUEUE, Constants::METHOD_QUEUE_UNBIND_OK); } - } diff --git a/src/Protocol/TxCommitFrame.php b/src/Protocol/TxCommitFrame.php index c57d89c..b67825e 100644 --- a/src/Protocol/TxCommitFrame.php +++ b/src/Protocol/TxCommitFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'tx.commit' (class #90, method #20) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class TxCommitFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_TX, Constants::METHOD_TX_COMMIT); } - } diff --git a/src/Protocol/TxCommitOkFrame.php b/src/Protocol/TxCommitOkFrame.php index 9619f0c..66732c5 100644 --- a/src/Protocol/TxCommitOkFrame.php +++ b/src/Protocol/TxCommitOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'tx.commit-ok' (class #90, method #21) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class TxCommitOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_TX, Constants::METHOD_TX_COMMIT_OK); } - } diff --git a/src/Protocol/TxRollbackFrame.php b/src/Protocol/TxRollbackFrame.php index b2f5e36..a262b48 100644 --- a/src/Protocol/TxRollbackFrame.php +++ b/src/Protocol/TxRollbackFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'tx.rollback' (class #90, method #30) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class TxRollbackFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_TX, Constants::METHOD_TX_ROLLBACK); } - } diff --git a/src/Protocol/TxRollbackOkFrame.php b/src/Protocol/TxRollbackOkFrame.php index ccaee19..d636704 100644 --- a/src/Protocol/TxRollbackOkFrame.php +++ b/src/Protocol/TxRollbackOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'tx.rollback-ok' (class #90, method #31) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class TxRollbackOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_TX, Constants::METHOD_TX_ROLLBACK_OK); } - } diff --git a/src/Protocol/TxSelectFrame.php b/src/Protocol/TxSelectFrame.php index 0b54644..ff84af6 100644 --- a/src/Protocol/TxSelectFrame.php +++ b/src/Protocol/TxSelectFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'tx.select' (class #90, method #10) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class TxSelectFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_TX, Constants::METHOD_TX_SELECT); } - } diff --git a/src/Protocol/TxSelectOkFrame.php b/src/Protocol/TxSelectOkFrame.php index 63b0fef..66f862c 100644 --- a/src/Protocol/TxSelectOkFrame.php +++ b/src/Protocol/TxSelectOkFrame.php @@ -1,22 +1,21 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ namespace PHPinnacle\Ridge\Protocol; use PHPinnacle\Ridge\Constants; -/** - * AMQP 'tx.select-ok' (class #90, method #11) frame. - * - * THIS CLASS IS GENERATED FROM amqp-rabbitmq-0.9.1.json. **DO NOT EDIT!** - * - * @author Jakub Kulhan - */ class TxSelectOkFrame extends MethodFrame { - public function __construct() { parent::__construct(Constants::CLASS_TX, Constants::METHOD_TX_SELECT_OK); } - } diff --git a/src/ProtocolReader.php b/src/ProtocolReader.php index 19b3bd5..5cca372 100644 --- a/src/ProtocolReader.php +++ b/src/ProtocolReader.php @@ -21,10 +21,10 @@ class ProtocolReader * * @return Protocol\AbstractFrame */ - public function consumeFrame(Buffer $buffer): ?Protocol\AbstractFrame + public static function frame(Buffer $buffer): ?Protocol\AbstractFrame { // not enough data - if ($buffer->getLength() < 7) { + if ($buffer->size() < 7) { return null; } @@ -32,10 +32,10 @@ public function consumeFrame(Buffer $buffer): ?Protocol\AbstractFrame $channel = $buffer->readUint16(1); $size = $buffer->readUint32(3); - $payloadOffset = 7; // type:uint8=>1 + channel:uint16=>2 + payloadSize:uint32=>4 ==> 7 + $payloadOffset = 7; // type:uint8=>1 + channel:uint16=>2 + size:uint32=>4 ==> 7 // not enough data - if ($buffer->getLength() < $payloadOffset + $size + 1 /* frame end byte */) { + if ($buffer->size() < $payloadOffset + $size + 1 /* frame end byte */) { return null; } @@ -45,90 +45,38 @@ public function consumeFrame(Buffer $buffer): ?Protocol\AbstractFrame $frameEnd = $buffer->consumeUint8(); if ($frameEnd !== Constants::FRAME_END) { - throw new Exception\ProtocolException(sprintf("Frame end byte invalid - expected 0x%02x, got 0x%02x.", Constants::FRAME_END, $frameEnd)); + throw Exception\ProtocolException::invalidFrameEnd($frameEnd); } $frameBuffer = new Buffer($payload); - if ($type === Constants::FRAME_METHOD) { - $frame = $this->consumeMethodFrame($frameBuffer); - } elseif ($type === Constants::FRAME_HEADER) { - // see https://github.com/pika/pika/blob/master/pika/spec.py class BasicProperties - $frame = new Protocol\ContentHeaderFrame(); - $frame->classId = $frameBuffer->consumeUint16(); - $frame->weight = $frameBuffer->consumeUint16(); - $frame->bodySize = $frameBuffer->consumeUint64(); - $frame->flags = $flags = $frameBuffer->consumeUint16(); - - if ($flags & Protocol\ContentHeaderFrame::FLAG_CONTENT_TYPE) { - $frame->contentType = $frameBuffer->consume($frameBuffer->consumeUint8()); - } - - if ($flags & Protocol\ContentHeaderFrame::FLAG_CONTENT_ENCODING) { - $frame->contentEncoding = $frameBuffer->consume($frameBuffer->consumeUint8()); - } - - if ($flags & Protocol\ContentHeaderFrame::FLAG_HEADERS) { - $frame->headers = $frameBuffer->consumeTable(); - } - - if ($flags & Protocol\ContentHeaderFrame::FLAG_DELIVERY_MODE) { - $frame->deliveryMode = $frameBuffer->consumeUint8(); - } - - if ($flags & Protocol\ContentHeaderFrame::FLAG_PRIORITY) { - $frame->priority = $frameBuffer->consumeUint8(); - } - - if ($flags & Protocol\ContentHeaderFrame::FLAG_CORRELATION_ID) { - $frame->correlationId = $frameBuffer->consume($frameBuffer->consumeUint8()); - } - - if ($flags & Protocol\ContentHeaderFrame::FLAG_REPLY_TO) { - $frame->replyTo = $frameBuffer->consume($frameBuffer->consumeUint8()); - } - - if ($flags & Protocol\ContentHeaderFrame::FLAG_EXPIRATION) { - $frame->expiration = $frameBuffer->consume($frameBuffer->consumeUint8()); - } + switch ($type) { + case Constants::FRAME_METHOD: + $frame = self::consumeMethodFrame($frameBuffer); - if ($flags & Protocol\ContentHeaderFrame::FLAG_MESSAGE_ID) { - $frame->messageId = $frameBuffer->consume($frameBuffer->consumeUint8()); - } - - if ($flags & Protocol\ContentHeaderFrame::FLAG_TIMESTAMP) { - $frame->timestamp = $frameBuffer->consumeTimestamp(); - } + break; + case Constants::FRAME_HEADER: + $frame = self::consumeHeaderFrame($frameBuffer); - if ($flags & Protocol\ContentHeaderFrame::FLAG_TYPE) { - $frame->typeHeader = $frameBuffer->consume($frameBuffer->consumeUint8()); - } + break; + case Constants::FRAME_BODY: + $frame = new Protocol\ContentBodyFrame; + $frame->payload = $frameBuffer->consume($frameBuffer->size()); - if ($flags & Protocol\ContentHeaderFrame::FLAG_USER_ID) { - $frame->userId = $frameBuffer->consume($frameBuffer->consumeUint8()); - } + break; + case Constants::FRAME_HEARTBEAT: + $frame = new Protocol\HeartbeatFrame; - if ($flags & Protocol\ContentHeaderFrame::FLAG_APP_ID) { - $frame->appId = $frameBuffer->consume($frameBuffer->consumeUint8()); - } + if (!$frameBuffer->empty()) { + throw Exception\ProtocolException::notEmptyHeartbeat(); + } - if ($flags & Protocol\ContentHeaderFrame::FLAG_CLUSTER_ID) { - $frame->clusterId = $frameBuffer->consume($frameBuffer->consumeUint8()); - } - } elseif ($type === Constants::FRAME_BODY) { - $frame = new Protocol\ContentBodyFrame(); - $frame->payload = $frameBuffer->consume($frameBuffer->getLength()); - } elseif ($type === Constants::FRAME_HEARTBEAT) { - $frame = new Protocol\HeartbeatFrame(); - - if (!$frameBuffer->isEmpty()) { - throw new Exception\ProtocolException("Heartbeat frame must be empty."); - } - } else { - throw new Exception\ProtocolException("Unhandled frame type '{$type}'."); + break; + default: + throw Exception\ProtocolException::unknownFrameType($type); } - if (!$frameBuffer->isEmpty()) { + if (!$frameBuffer->empty()) { throw new Exception\ProtocolException("Frame buffer not entirely consumed."); } @@ -136,8 +84,6 @@ public function consumeFrame(Buffer $buffer): ?Protocol\AbstractFrame $frame->type = $type; $frame->size = $size; $frame->channel = $channel; - // DO NOT CALL! ContentBodyFrame uses payload for body - // $frame->setPayload($payload); return $frame; } @@ -149,267 +95,267 @@ public function consumeFrame(Buffer $buffer): ?Protocol\AbstractFrame * * @return Protocol\MethodFrame */ - private function consumeMethodFrame(Buffer $buffer): Protocol\MethodFrame + private static function consumeMethodFrame(Buffer $buffer): Protocol\MethodFrame { $classId = $buffer->consumeUint16(); $methodId = $buffer->consumeUint16(); if ($classId === Constants::CLASS_CONNECTION) { if ($methodId === Constants::METHOD_CONNECTION_START) { - $frame = new Protocol\ConnectionStartFrame(); + $frame = new Protocol\ConnectionStartFrame; $frame->versionMajor = $buffer->consumeUint8(); $frame->versionMinor = $buffer->consumeUint8(); $frame->serverProperties = $buffer->consumeTable(); - $frame->mechanisms = $buffer->consume($buffer->consumeUint32()); - $frame->locales = $buffer->consume($buffer->consumeUint32()); + $frame->mechanisms = $buffer->consumeText(); + $frame->locales = $buffer->consumeText(); } elseif ($methodId === Constants::METHOD_CONNECTION_START_OK) { - $frame = new Protocol\ConnectionStartOkFrame(); + $frame = new Protocol\ConnectionStartOkFrame; $frame->clientProperties = $buffer->consumeTable(); - $frame->mechanism = $buffer->consume($buffer->consumeUint8()); - $frame->response = $buffer->consume($buffer->consumeUint32()); - $frame->locale = $buffer->consume($buffer->consumeUint8()); + $frame->mechanism = $buffer->consumeString(); + $frame->response = $buffer->consumeText(); + $frame->locale = $buffer->consumeString(); } elseif ($methodId === Constants::METHOD_CONNECTION_SECURE) { - $frame = new Protocol\ConnectionSecureFrame(); - $frame->challenge = $buffer->consume($buffer->consumeUint32()); + $frame = new Protocol\ConnectionSecureFrame; + $frame->challenge = $buffer->consumeText(); } elseif ($methodId === Constants::METHOD_CONNECTION_SECURE_OK) { - $frame = new Protocol\ConnectionSecureOkFrame(); - $frame->response = $buffer->consume($buffer->consumeUint32()); + $frame = new Protocol\ConnectionSecureOkFrame; + $frame->response = $buffer->consumeText(); } elseif ($methodId === Constants::METHOD_CONNECTION_TUNE) { - $frame = new Protocol\ConnectionTuneFrame(); + $frame = new Protocol\ConnectionTuneFrame; $frame->channelMax = $buffer->consumeInt16(); $frame->frameMax = $buffer->consumeInt32(); $frame->heartbeat = $buffer->consumeInt16(); } elseif ($methodId === Constants::METHOD_CONNECTION_TUNE_OK) { - $frame = new Protocol\ConnectionTuneOkFrame(); + $frame = new Protocol\ConnectionTuneOkFrame; $frame->channelMax = $buffer->consumeInt16(); $frame->frameMax = $buffer->consumeInt32(); $frame->heartbeat = $buffer->consumeInt16(); } elseif ($methodId === Constants::METHOD_CONNECTION_OPEN) { - $frame = new Protocol\ConnectionOpenFrame(); - $frame->virtualHost = $buffer->consume($buffer->consumeUint8()); - $frame->capabilities = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\ConnectionOpenFrame; + $frame->virtualHost = $buffer->consumeString(); + $frame->capabilities = $buffer->consumeString(); list($frame->insist) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_CONNECTION_OPEN_OK) { - $frame = new Protocol\ConnectionOpenOkFrame(); - $frame->knownHosts = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\ConnectionOpenOkFrame; + $frame->knownHosts = $buffer->consumeString(); } elseif ($methodId === Constants::METHOD_CONNECTION_CLOSE) { - $frame = new Protocol\ConnectionCloseFrame(); + $frame = new Protocol\ConnectionCloseFrame; $frame->replyCode = $buffer->consumeInt16(); - $frame->replyText = $buffer->consume($buffer->consumeUint8()); + $frame->replyText = $buffer->consumeString(); $frame->closeClassId = $buffer->consumeInt16(); $frame->closeMethodId = $buffer->consumeInt16(); } elseif ($methodId === Constants::METHOD_CONNECTION_CLOSE_OK) { - $frame = new Protocol\ConnectionCloseOkFrame(); + $frame = new Protocol\ConnectionCloseOkFrame; } elseif ($methodId === Constants::METHOD_CONNECTION_BLOCKED) { - $frame = new Protocol\ConnectionBlockedFrame(); - $frame->reason = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\ConnectionBlockedFrame; + $frame->reason = $buffer->consumeString(); } elseif ($methodId === Constants::METHOD_CONNECTION_UNBLOCKED) { - $frame = new Protocol\ConnectionUnblockedFrame(); + $frame = new Protocol\ConnectionUnblockedFrame; } else { throw new Exception\MethodInvalid($classId, $methodId); } } elseif ($classId === Constants::CLASS_CHANNEL) { if ($methodId === Constants::METHOD_CHANNEL_OPEN) { - $frame = new Protocol\ChannelOpenFrame(); - $frame->outOfBand = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\ChannelOpenFrame; + $frame->outOfBand = $buffer->consumeString(); } elseif ($methodId === Constants::METHOD_CHANNEL_OPEN_OK) { - $frame = new Protocol\ChannelOpenOkFrame(); - $frame->channelId = $buffer->consume($buffer->consumeUint32()); + $frame = new Protocol\ChannelOpenOkFrame; + $frame->channelId = $buffer->consumeText(); } elseif ($methodId === Constants::METHOD_CHANNEL_FLOW) { - $frame = new Protocol\ChannelFlowFrame(); + $frame = new Protocol\ChannelFlowFrame; list($frame->active) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_CHANNEL_FLOW_OK) { - $frame = new Protocol\ChannelFlowOkFrame(); + $frame = new Protocol\ChannelFlowOkFrame; list($frame->active) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_CHANNEL_CLOSE) { - $frame = new Protocol\ChannelCloseFrame(); + $frame = new Protocol\ChannelCloseFrame; $frame->replyCode = $buffer->consumeInt16(); - $frame->replyText = $buffer->consume($buffer->consumeUint8()); + $frame->replyText = $buffer->consumeString(); $frame->closeClassId = $buffer->consumeInt16(); $frame->closeMethodId = $buffer->consumeInt16(); } elseif ($methodId === Constants::METHOD_CHANNEL_CLOSE_OK) { - $frame = new Protocol\ChannelCloseOkFrame(); + $frame = new Protocol\ChannelCloseOkFrame; } else { throw new Exception\MethodInvalid($classId, $methodId); } } elseif ($classId === Constants::CLASS_ACCESS) { if ($methodId === Constants::METHOD_ACCESS_REQUEST) { - $frame = new Protocol\AccessRequestFrame(); - $frame->realm = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\AccessRequestFrame; + $frame->realm = $buffer->consumeString(); list($frame->exclusive, $frame->passive, $frame->active, $frame->write, $frame->read) = $buffer->consumeBits(5); } elseif ($methodId === Constants::METHOD_ACCESS_REQUEST_OK) { - $frame = new Protocol\AccessRequestOkFrame(); + $frame = new Protocol\AccessRequestOkFrame; $frame->reserved1 = $buffer->consumeInt16(); } else { throw new Exception\MethodInvalid($classId, $methodId); } } elseif ($classId === Constants::CLASS_EXCHANGE) { if ($methodId === Constants::METHOD_EXCHANGE_DECLARE) { - $frame = new Protocol\ExchangeDeclareFrame(); + $frame = new Protocol\ExchangeDeclareFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->exchange = $buffer->consume($buffer->consumeUint8()); - $frame->exchangeType = $buffer->consume($buffer->consumeUint8()); + $frame->exchange = $buffer->consumeString(); + $frame->exchangeType = $buffer->consumeString(); list($frame->passive, $frame->durable, $frame->autoDelete, $frame->internal, $frame->nowait) = $buffer->consumeBits(5); $frame->arguments = $buffer->consumeTable(); } elseif ($methodId === Constants::METHOD_EXCHANGE_DECLARE_OK) { - $frame = new Protocol\ExchangeDeclareOkFrame(); + $frame = new Protocol\ExchangeDeclareOkFrame; } elseif ($methodId === Constants::METHOD_EXCHANGE_DELETE) { - $frame = new Protocol\ExchangeDeleteFrame(); + $frame = new Protocol\ExchangeDeleteFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->exchange = $buffer->consume($buffer->consumeUint8()); + $frame->exchange = $buffer->consumeString(); list($frame->ifUnused, $frame->nowait) = $buffer->consumeBits(2); } elseif ($methodId === Constants::METHOD_EXCHANGE_DELETE_OK) { - $frame = new Protocol\ExchangeDeleteOkFrame(); + $frame = new Protocol\ExchangeDeleteOkFrame; } elseif ($methodId === Constants::METHOD_EXCHANGE_BIND) { - $frame = new Protocol\ExchangeBindFrame(); + $frame = new Protocol\ExchangeBindFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->destination = $buffer->consume($buffer->consumeUint8()); - $frame->source = $buffer->consume($buffer->consumeUint8()); - $frame->routingKey = $buffer->consume($buffer->consumeUint8()); + $frame->destination = $buffer->consumeString(); + $frame->source = $buffer->consumeString(); + $frame->routingKey = $buffer->consumeString(); list($frame->nowait) = $buffer->consumeBits(1); $frame->arguments = $buffer->consumeTable(); } elseif ($methodId === Constants::METHOD_EXCHANGE_BIND_OK) { - $frame = new Protocol\ExchangeBindOkFrame(); + $frame = new Protocol\ExchangeBindOkFrame; } elseif ($methodId === Constants::METHOD_EXCHANGE_UNBIND) { - $frame = new Protocol\ExchangeUnbindFrame(); + $frame = new Protocol\ExchangeUnbindFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->destination = $buffer->consume($buffer->consumeUint8()); - $frame->source = $buffer->consume($buffer->consumeUint8()); - $frame->routingKey = $buffer->consume($buffer->consumeUint8()); + $frame->destination = $buffer->consumeString(); + $frame->source = $buffer->consumeString(); + $frame->routingKey = $buffer->consumeString(); list($frame->nowait) = $buffer->consumeBits(1); $frame->arguments = $buffer->consumeTable(); } elseif ($methodId === Constants::METHOD_EXCHANGE_UNBIND_OK) { - $frame = new Protocol\ExchangeUnbindOkFrame(); + $frame = new Protocol\ExchangeUnbindOkFrame; } else { throw new Exception\MethodInvalid($classId, $methodId); } } elseif ($classId === Constants::CLASS_QUEUE) { if ($methodId === Constants::METHOD_QUEUE_DECLARE) { - $frame = new Protocol\QueueDeclareFrame(); + $frame = new Protocol\QueueDeclareFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->queue = $buffer->consume($buffer->consumeUint8()); + $frame->queue = $buffer->consumeString(); list($frame->passive, $frame->durable, $frame->exclusive, $frame->autoDelete, $frame->nowait) = $buffer->consumeBits(5); $frame->arguments = $buffer->consumeTable(); } elseif ($methodId === Constants::METHOD_QUEUE_DECLARE_OK) { - $frame = new Protocol\QueueDeclareOkFrame(); - $frame->queue = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\QueueDeclareOkFrame; + $frame->queue = $buffer->consumeString(); $frame->messageCount = $buffer->consumeInt32(); $frame->consumerCount = $buffer->consumeInt32(); } elseif ($methodId === Constants::METHOD_QUEUE_BIND) { - $frame = new Protocol\QueueBindFrame(); + $frame = new Protocol\QueueBindFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->queue = $buffer->consume($buffer->consumeUint8()); - $frame->exchange = $buffer->consume($buffer->consumeUint8()); - $frame->routingKey = $buffer->consume($buffer->consumeUint8()); + $frame->queue = $buffer->consumeString(); + $frame->exchange = $buffer->consumeString(); + $frame->routingKey = $buffer->consumeString(); list($frame->nowait) = $buffer->consumeBits(1); $frame->arguments = $buffer->consumeTable(); } elseif ($methodId === Constants::METHOD_QUEUE_BIND_OK) { - $frame = new Protocol\QueueBindOkFrame(); + $frame = new Protocol\QueueBindOkFrame; } elseif ($methodId === Constants::METHOD_QUEUE_PURGE) { - $frame = new Protocol\QueuePurgeFrame(); + $frame = new Protocol\QueuePurgeFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->queue = $buffer->consume($buffer->consumeUint8()); + $frame->queue = $buffer->consumeString(); list($frame->nowait) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_QUEUE_PURGE_OK) { - $frame = new Protocol\QueuePurgeOkFrame(); + $frame = new Protocol\QueuePurgeOkFrame; $frame->messageCount = $buffer->consumeInt32(); } elseif ($methodId === Constants::METHOD_QUEUE_DELETE) { - $frame = new Protocol\QueueDeleteFrame(); + $frame = new Protocol\QueueDeleteFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->queue = $buffer->consume($buffer->consumeUint8()); + $frame->queue = $buffer->consumeString(); list($frame->ifUnused, $frame->ifEmpty, $frame->nowait) = $buffer->consumeBits(3); } elseif ($methodId === Constants::METHOD_QUEUE_DELETE_OK) { - $frame = new Protocol\QueueDeleteOkFrame(); + $frame = new Protocol\QueueDeleteOkFrame; $frame->messageCount = $buffer->consumeInt32(); } elseif ($methodId === Constants::METHOD_QUEUE_UNBIND) { - $frame = new Protocol\QueueUnbindFrame(); + $frame = new Protocol\QueueUnbindFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->queue = $buffer->consume($buffer->consumeUint8()); - $frame->exchange = $buffer->consume($buffer->consumeUint8()); - $frame->routingKey = $buffer->consume($buffer->consumeUint8()); + $frame->queue = $buffer->consumeString(); + $frame->exchange = $buffer->consumeString(); + $frame->routingKey = $buffer->consumeString(); $frame->arguments = $buffer->consumeTable(); } elseif ($methodId === Constants::METHOD_QUEUE_UNBIND_OK) { - $frame = new Protocol\QueueUnbindOkFrame(); + $frame = new Protocol\QueueUnbindOkFrame; } else { throw new Exception\MethodInvalid($classId, $methodId); } } elseif ($classId === Constants::CLASS_BASIC) { if ($methodId === Constants::METHOD_BASIC_QOS) { - $frame = new Protocol\BasicQosFrame(); + $frame = new Protocol\BasicQosFrame; $frame->prefetchSize = $buffer->consumeInt32(); $frame->prefetchCount = $buffer->consumeInt16(); list($frame->global) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_BASIC_QOS_OK) { - $frame = new Protocol\BasicQosOkFrame(); + $frame = new Protocol\BasicQosOkFrame; } elseif ($methodId === Constants::METHOD_BASIC_CONSUME) { - $frame = new Protocol\BasicConsumeFrame(); + $frame = new Protocol\BasicConsumeFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->queue = $buffer->consume($buffer->consumeUint8()); - $frame->consumerTag = $buffer->consume($buffer->consumeUint8()); + $frame->queue = $buffer->consumeString(); + $frame->consumerTag = $buffer->consumeString(); list($frame->noLocal, $frame->noAck, $frame->exclusive, $frame->nowait) = $buffer->consumeBits(4); $frame->arguments = $buffer->consumeTable(); } elseif ($methodId === Constants::METHOD_BASIC_CONSUME_OK) { - $frame = new Protocol\BasicConsumeOkFrame(); - $frame->consumerTag = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\BasicConsumeOkFrame; + $frame->consumerTag = $buffer->consumeString(); } elseif ($methodId === Constants::METHOD_BASIC_CANCEL) { - $frame = new Protocol\BasicCancelFrame(); - $frame->consumerTag = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\BasicCancelFrame; + $frame->consumerTag = $buffer->consumeString(); list($frame->nowait) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_BASIC_CANCEL_OK) { - $frame = new Protocol\BasicCancelOkFrame(); - $frame->consumerTag = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\BasicCancelOkFrame; + $frame->consumerTag = $buffer->consumeString(); } elseif ($methodId === Constants::METHOD_BASIC_PUBLISH) { - $frame = new Protocol\BasicPublishFrame(); + $frame = new Protocol\BasicPublishFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->exchange = $buffer->consume($buffer->consumeUint8()); - $frame->routingKey = $buffer->consume($buffer->consumeUint8()); + $frame->exchange = $buffer->consumeString(); + $frame->routingKey = $buffer->consumeString(); list($frame->mandatory, $frame->immediate) = $buffer->consumeBits(2); } elseif ($methodId === Constants::METHOD_BASIC_RETURN) { - $frame = new Protocol\BasicReturnFrame(); + $frame = new Protocol\BasicReturnFrame; $frame->replyCode = $buffer->consumeInt16(); - $frame->replyText = $buffer->consume($buffer->consumeUint8()); - $frame->exchange = $buffer->consume($buffer->consumeUint8()); - $frame->routingKey = $buffer->consume($buffer->consumeUint8()); + $frame->replyText = $buffer->consumeString(); + $frame->exchange = $buffer->consumeString(); + $frame->routingKey = $buffer->consumeString(); } elseif ($methodId === Constants::METHOD_BASIC_DELIVER) { - $frame = new Protocol\BasicDeliverFrame(); - $frame->consumerTag = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\BasicDeliverFrame; + $frame->consumerTag = $buffer->consumeString(); $frame->deliveryTag = $buffer->consumeInt64(); list($frame->redelivered) = $buffer->consumeBits(1); - $frame->exchange = $buffer->consume($buffer->consumeUint8()); - $frame->routingKey = $buffer->consume($buffer->consumeUint8()); + $frame->exchange = $buffer->consumeString(); + $frame->routingKey = $buffer->consumeString(); } elseif ($methodId === Constants::METHOD_BASIC_GET) { - $frame = new Protocol\BasicGetFrame(); + $frame = new Protocol\BasicGetFrame; $frame->reserved1 = $buffer->consumeInt16(); - $frame->queue = $buffer->consume($buffer->consumeUint8()); + $frame->queue = $buffer->consumeString(); list($frame->noAck) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_BASIC_GET_OK) { - $frame = new Protocol\BasicGetOkFrame(); + $frame = new Protocol\BasicGetOkFrame; $frame->deliveryTag = $buffer->consumeInt64(); list($frame->redelivered) = $buffer->consumeBits(1); - $frame->exchange = $buffer->consume($buffer->consumeUint8()); - $frame->routingKey = $buffer->consume($buffer->consumeUint8()); + $frame->exchange = $buffer->consumeString(); + $frame->routingKey = $buffer->consumeString(); $frame->messageCount = $buffer->consumeInt32(); } elseif ($methodId === Constants::METHOD_BASIC_GET_EMPTY) { - $frame = new Protocol\BasicGetEmptyFrame(); - $frame->clusterId = $buffer->consume($buffer->consumeUint8()); + $frame = new Protocol\BasicGetEmptyFrame; + $frame->clusterId = $buffer->consumeString(); } elseif ($methodId === Constants::METHOD_BASIC_ACK) { - $frame = new Protocol\BasicAckFrame(); + $frame = new Protocol\BasicAckFrame; $frame->deliveryTag = $buffer->consumeInt64(); list($frame->multiple) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_BASIC_REJECT) { - $frame = new Protocol\BasicRejectFrame(); + $frame = new Protocol\BasicRejectFrame; $frame->deliveryTag = $buffer->consumeInt64(); list($frame->requeue) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_BASIC_RECOVER_ASYNC) { - $frame = new Protocol\BasicRecoverAsyncFrame(); + $frame = new Protocol\BasicRecoverAsyncFrame; list($frame->requeue) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_BASIC_RECOVER) { - $frame = new Protocol\BasicRecoverFrame(); + $frame = new Protocol\BasicRecoverFrame; list($frame->requeue) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_BASIC_RECOVER_OK) { - $frame = new Protocol\BasicRecoverOkFrame(); + $frame = new Protocol\BasicRecoverOkFrame; } elseif ($methodId === Constants::METHOD_BASIC_NACK) { - $frame = new Protocol\BasicNackFrame(); + $frame = new Protocol\BasicNackFrame; $frame->deliveryTag = $buffer->consumeInt64(); list($frame->multiple, $frame->requeue) = $buffer->consumeBits(2); } else { @@ -417,26 +363,26 @@ private function consumeMethodFrame(Buffer $buffer): Protocol\MethodFrame } } elseif ($classId === Constants::CLASS_TX) { if ($methodId === Constants::METHOD_TX_SELECT) { - $frame = new Protocol\TxSelectFrame(); + $frame = new Protocol\TxSelectFrame; } elseif ($methodId === Constants::METHOD_TX_SELECT_OK) { - $frame = new Protocol\TxSelectOkFrame(); + $frame = new Protocol\TxSelectOkFrame; } elseif ($methodId === Constants::METHOD_TX_COMMIT) { - $frame = new Protocol\TxCommitFrame(); + $frame = new Protocol\TxCommitFrame; } elseif ($methodId === Constants::METHOD_TX_COMMIT_OK) { - $frame = new Protocol\TxCommitOkFrame(); + $frame = new Protocol\TxCommitOkFrame; } elseif ($methodId === Constants::METHOD_TX_ROLLBACK) { - $frame = new Protocol\TxRollbackFrame(); + $frame = new Protocol\TxRollbackFrame; } elseif ($methodId === Constants::METHOD_TX_ROLLBACK_OK) { - $frame = new Protocol\TxRollbackOkFrame(); + $frame = new Protocol\TxRollbackOkFrame; } else { throw new Exception\MethodInvalid($classId, $methodId); } } elseif ($classId === Constants::CLASS_CONFIRM) { if ($methodId === Constants::METHOD_CONFIRM_SELECT) { - $frame = new Protocol\ConfirmSelectFrame(); + $frame = new Protocol\ConfirmSelectFrame; list($frame->nowait) = $buffer->consumeBits(1); } elseif ($methodId === Constants::METHOD_CONFIRM_SELECT_OK) { - $frame = new Protocol\ConfirmSelectOkFrame(); + $frame = new Protocol\ConfirmSelectOkFrame; } else { throw new Exception\MethodInvalid($classId, $methodId); } @@ -449,4 +395,76 @@ private function consumeMethodFrame(Buffer $buffer): Protocol\MethodFrame return $frame; } + + /** + * @param Buffer $buffer + * + * @return Protocol\ContentHeaderFrame + */ + private static function consumeHeaderFrame(Buffer $buffer): Protocol\ContentHeaderFrame + { + $frame = new Protocol\ContentHeaderFrame; + $frame->classId = $buffer->consumeUint16(); + $frame->weight = $buffer->consumeUint16(); + $frame->bodySize = $buffer->consumeUint64(); + $frame->flags = $flags = $buffer->consumeUint16(); + + if ($flags & Protocol\ContentHeaderFrame::FLAG_CONTENT_TYPE) { + $frame->contentType = $buffer->consumeString(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_CONTENT_ENCODING) { + $frame->contentEncoding = $buffer->consumeString(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_HEADERS) { + $frame->headers = $buffer->consumeTable(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_DELIVERY_MODE) { + $frame->deliveryMode = $buffer->consumeUint8(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_PRIORITY) { + $frame->priority = $buffer->consumeUint8(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_CORRELATION_ID) { + $frame->correlationId = $buffer->consumeString(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_REPLY_TO) { + $frame->replyTo = $buffer->consumeString(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_EXPIRATION) { + $frame->expiration = $buffer->consumeString(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_MESSAGE_ID) { + $frame->messageId = $buffer->consumeString(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_TIMESTAMP) { + $frame->timestamp = $buffer->consumeTimestamp(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_TYPE) { + $frame->typeHeader = $buffer->consumeString(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_USER_ID) { + $frame->userId = $buffer->consumeString(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_APP_ID) { + $frame->appId = $buffer->consumeString(); + } + + if ($flags & Protocol\ContentHeaderFrame::FLAG_CLUSTER_ID) { + $frame->clusterId = $buffer->consumeString(); + } + + return $frame; + } } diff --git a/src/ProtocolWriter.php b/src/ProtocolWriter.php index 59cab34..b688b71 100644 --- a/src/ProtocolWriter.php +++ b/src/ProtocolWriter.php @@ -18,144 +18,155 @@ use PHPinnacle\Ridge\Protocol\HeartbeatFrame; use PHPinnacle\Ridge\Protocol\MethodFrame; -class ProtocolWriter +final class ProtocolWriter { /** * Appends AMQP frame to buffer. * * @param AbstractFrame $frame - * @param Buffer $buffer + * + * @return Buffer */ - public function appendFrame(AbstractFrame $frame, Buffer $buffer) + public static function buffer(AbstractFrame $frame): Buffer { if ($frame instanceof MethodFrame && $frame->payload !== null) { // payload already supplied } elseif ($frame instanceof MethodFrame) { - $frameBuffer = new Buffer(); - - $this->appendMethodFrame($frame, $frameBuffer); + $frameBuffer = self::bufferMethodFrame($frame); - $frame->size = $frameBuffer->getLength(); + $frame->size = $frameBuffer->size(); $frame->payload = $frameBuffer; } elseif ($frame instanceof ContentHeaderFrame) { - $frameBuffer = new Buffer(); - // see https://github.com/pika/pika/blob/master/pika/spec.py class BasicProperties - $frameBuffer->appendUint16($frame->classId); - $frameBuffer->appendUint16($frame->weight); - $frameBuffer->appendUint64($frame->bodySize); + $frameBuffer = self::bufferHeaderFrame($frame); - $flags = $frame->flags; + $frame->size = $frameBuffer->size(); + $frame->payload = $frameBuffer; + } elseif ($frame instanceof ContentBodyFrame) { + // body frame's payload is already loaded + } elseif ($frame instanceof HeartbeatFrame) { + // heartbeat frame is empty + } else { + throw Exception\ProtocolException::unknownFrameClass($frame); + } - $frameBuffer->appendUint16($flags); + $buffer = new Buffer; + $buffer + ->appendUint8($frame->type) + ->appendUint16($frame->channel) + ->appendUint32($frame->size) + ->append($frame->payload) + ->appendUint8(Constants::FRAME_END) + ; - if ($flags & ContentHeaderFrame::FLAG_CONTENT_TYPE) { - $frameBuffer->appendUint8(\strlen($frame->contentType)); - $frameBuffer->append($frame->contentType); - } + return $buffer; + } - if ($flags & ContentHeaderFrame::FLAG_CONTENT_ENCODING) { - $frameBuffer->appendUint8(\strlen($frame->contentEncoding)); - $frameBuffer->append($frame->contentEncoding); - } + /** + * @param Protocol\ContentHeaderFrame $frame + * + * @return Buffer + */ + private static function bufferHeaderFrame(Protocol\ContentHeaderFrame $frame): Buffer + { + $buffer = new Buffer; + $buffer + ->appendUint16($frame->classId) + ->appendUint16($frame->weight) + ->appendUint64($frame->bodySize) + ; + + $flags = $frame->flags; - if ($flags & ContentHeaderFrame::FLAG_HEADERS) { - $frameBuffer->appendTable($frame->headers); - } + $buffer->appendUint16($flags); + + if ($flags & ContentHeaderFrame::FLAG_CONTENT_TYPE) { + $buffer->appendString($frame->contentType); + } + + if ($flags & ContentHeaderFrame::FLAG_CONTENT_ENCODING) { + $buffer->appendString($frame->contentEncoding); + } - if ($flags & ContentHeaderFrame::FLAG_DELIVERY_MODE) { - $frameBuffer->appendUint8($frame->deliveryMode); - } + if ($flags & ContentHeaderFrame::FLAG_HEADERS) { + $buffer->appendTable($frame->headers); + } - if ($flags & ContentHeaderFrame::FLAG_PRIORITY) { - $frameBuffer->appendUint8($frame->priority); - } + if ($flags & ContentHeaderFrame::FLAG_DELIVERY_MODE) { + $buffer->appendUint8($frame->deliveryMode); + } - if ($flags & ContentHeaderFrame::FLAG_CORRELATION_ID) { - $frameBuffer->appendUint8(\strlen($frame->correlationId)); - $frameBuffer->append($frame->correlationId); - } + if ($flags & ContentHeaderFrame::FLAG_PRIORITY) { + $buffer->appendUint8($frame->priority); + } - if ($flags & ContentHeaderFrame::FLAG_REPLY_TO) { - $frameBuffer->appendUint8(\strlen($frame->replyTo)); - $frameBuffer->append($frame->replyTo); - } + if ($flags & ContentHeaderFrame::FLAG_CORRELATION_ID) { + $buffer->appendString($frame->correlationId); + } - if ($flags & ContentHeaderFrame::FLAG_EXPIRATION) { - $frameBuffer->appendUint8(\strlen($frame->expiration)); - $frameBuffer->append($frame->expiration); - } + if ($flags & ContentHeaderFrame::FLAG_REPLY_TO) { + $buffer->appendString($frame->replyTo); + } - if ($flags & ContentHeaderFrame::FLAG_MESSAGE_ID) { - $frameBuffer->appendUint8(\strlen($frame->messageId)); - $frameBuffer->append($frame->messageId); - } + if ($flags & ContentHeaderFrame::FLAG_EXPIRATION) { + $buffer->appendString($frame->expiration); + } - if ($flags & ContentHeaderFrame::FLAG_TIMESTAMP) { - $frameBuffer->appendTimestamp($frame->timestamp); - } + if ($flags & ContentHeaderFrame::FLAG_MESSAGE_ID) { + $buffer->appendString($frame->messageId); + } - if ($flags & ContentHeaderFrame::FLAG_TYPE) { - $frameBuffer->appendUint8(\strlen($frame->typeHeader)); - $frameBuffer->append($frame->typeHeader); - } + if ($flags & ContentHeaderFrame::FLAG_TIMESTAMP) { + $buffer->appendTimestamp($frame->timestamp); + } - if ($flags & ContentHeaderFrame::FLAG_USER_ID) { - $frameBuffer->appendUint8(\strlen($frame->userId)); - $frameBuffer->append($frame->userId); - } + if ($flags & ContentHeaderFrame::FLAG_TYPE) { + $buffer->appendString($frame->typeHeader); + } - if ($flags & ContentHeaderFrame::FLAG_APP_ID) { - $frameBuffer->appendUint8(\strlen($frame->appId)); - $frameBuffer->append($frame->appId); - } + if ($flags & ContentHeaderFrame::FLAG_USER_ID) { + $buffer->appendString($frame->userId); + } - if ($flags & ContentHeaderFrame::FLAG_CLUSTER_ID) { - $frameBuffer->appendUint8(\strlen($frame->clusterId)); - $frameBuffer->append($frame->clusterId); - } + if ($flags & ContentHeaderFrame::FLAG_APP_ID) { + $buffer->appendString($frame->appId); + } - $frame->size = $frameBuffer->getLength(); - $frame->payload = $frameBuffer; - } elseif ($frame instanceof ContentBodyFrame) { - // body frame's payload is already loaded - } elseif ($frame instanceof HeartbeatFrame) { - // heartbeat frame is empty - } else { - throw new Exception\ProtocolException("Unhandled frame '" . get_class($frame) . "'."); + if ($flags & ContentHeaderFrame::FLAG_CLUSTER_ID) { + $buffer->appendString($frame->clusterId); } - $buffer->appendUint8($frame->type); - $buffer->appendUint16($frame->channel); - $buffer->appendUint32($frame->size); - $buffer->append($frame->payload); - $buffer->appendUint8(Constants::FRAME_END); + return $buffer; } /** * @param Protocol\MethodFrame $frame - * @param Buffer $buffer + * + * @return Buffer */ - private function appendMethodFrame(Protocol\MethodFrame $frame, Buffer $buffer): void + private static function bufferMethodFrame(Protocol\MethodFrame $frame): Buffer { - $buffer->appendUint16($frame->classId); - $buffer->appendUint16($frame->methodId); + $buffer = new Buffer; + $buffer + ->appendUint16($frame->classId) + ->appendUint16($frame->methodId) + ; if ($frame instanceof Protocol\ConnectionStartFrame) { $buffer->appendUint8($frame->versionMajor); $buffer->appendUint8($frame->versionMinor); $buffer->appendTable($frame->serverProperties); - $buffer->appendUint32(\strlen($frame->mechanisms)); $buffer->append($frame->mechanisms); - $buffer->appendUint32(\strlen($frame->locales)); $buffer->append($frame->locales); + $buffer->appendText($frame->mechanisms); + $buffer->appendText($frame->locales); } elseif ($frame instanceof Protocol\ConnectionStartOkFrame) { $buffer->appendTable($frame->clientProperties); - $buffer->appendUint8(\strlen($frame->mechanism)); $buffer->append($frame->mechanism); - $buffer->appendUint32(\strlen($frame->response)); $buffer->append($frame->response); - $buffer->appendUint8(\strlen($frame->locale)); $buffer->append($frame->locale); + $buffer->appendString($frame->mechanism); + $buffer->appendText($frame->response); + $buffer->appendString($frame->locale); } elseif ($frame instanceof Protocol\ConnectionSecureFrame) { - $buffer->appendUint32(\strlen($frame->challenge)); $buffer->append($frame->challenge); + $buffer->appendText($frame->challenge); } elseif ($frame instanceof Protocol\ConnectionSecureOkFrame) { - $buffer->appendUint32(\strlen($frame->response)); $buffer->append($frame->response); + $buffer->appendText($frame->response); } elseif ($frame instanceof Protocol\ConnectionTuneFrame) { $buffer->appendInt16($frame->channelMax); $buffer->appendInt32($frame->frameMax); @@ -165,101 +176,101 @@ private function appendMethodFrame(Protocol\MethodFrame $frame, Buffer $buffer): $buffer->appendInt32($frame->frameMax); $buffer->appendInt16($frame->heartbeat); } elseif ($frame instanceof Protocol\ConnectionOpenFrame) { - $buffer->appendUint8(\strlen($frame->virtualHost)); $buffer->append($frame->virtualHost); - $buffer->appendUint8(\strlen($frame->capabilities)); $buffer->append($frame->capabilities); + $buffer->appendString($frame->virtualHost); + $buffer->appendString($frame->capabilities); $buffer->appendBits([$frame->insist]); } elseif ($frame instanceof Protocol\ConnectionOpenOkFrame) { - $buffer->appendUint8(\strlen($frame->knownHosts)); $buffer->append($frame->knownHosts); + $buffer->appendString($frame->knownHosts); } elseif ($frame instanceof Protocol\ConnectionCloseFrame) { $buffer->appendInt16($frame->replyCode); - $buffer->appendUint8(\strlen($frame->replyText)); $buffer->append($frame->replyText); + $buffer->appendString($frame->replyText); $buffer->appendInt16($frame->closeClassId); $buffer->appendInt16($frame->closeMethodId); } elseif ($frame instanceof Protocol\ConnectionCloseOkFrame) { } elseif ($frame instanceof Protocol\ConnectionBlockedFrame) { - $buffer->appendUint8(\strlen($frame->reason)); $buffer->append($frame->reason); + $buffer->appendString($frame->reason); } elseif ($frame instanceof Protocol\ConnectionUnblockedFrame) { } elseif ($frame instanceof Protocol\ChannelOpenFrame) { - $buffer->appendUint8(\strlen($frame->outOfBand)); $buffer->append($frame->outOfBand); + $buffer->appendString($frame->outOfBand); } elseif ($frame instanceof Protocol\ChannelOpenOkFrame) { - $buffer->appendUint32(\strlen($frame->channelId)); $buffer->append($frame->channelId); + $buffer->appendText($frame->channelId); } elseif ($frame instanceof Protocol\ChannelFlowFrame) { $buffer->appendBits([$frame->active]); } elseif ($frame instanceof Protocol\ChannelFlowOkFrame) { $buffer->appendBits([$frame->active]); } elseif ($frame instanceof Protocol\ChannelCloseFrame) { $buffer->appendInt16($frame->replyCode); - $buffer->appendUint8(\strlen($frame->replyText)); $buffer->append($frame->replyText); + $buffer->appendString($frame->replyText); $buffer->appendInt16($frame->closeClassId); $buffer->appendInt16($frame->closeMethodId); } elseif ($frame instanceof Protocol\ChannelCloseOkFrame) { } elseif ($frame instanceof Protocol\AccessRequestFrame) { - $buffer->appendUint8(\strlen($frame->realm)); $buffer->append($frame->realm); + $buffer->appendString($frame->realm); $buffer->appendBits([$frame->exclusive, $frame->passive, $frame->active, $frame->write, $frame->read]); } elseif ($frame instanceof Protocol\AccessRequestOkFrame) { $buffer->appendInt16($frame->reserved1); } elseif ($frame instanceof Protocol\ExchangeDeclareFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->exchange)); $buffer->append($frame->exchange); - $buffer->appendUint8(\strlen($frame->exchangeType)); $buffer->append($frame->exchangeType); + $buffer->appendString($frame->exchange); + $buffer->appendString($frame->exchangeType); $buffer->appendBits([$frame->passive, $frame->durable, $frame->autoDelete, $frame->internal, $frame->nowait]); $buffer->appendTable($frame->arguments); } elseif ($frame instanceof Protocol\ExchangeDeclareOkFrame) { } elseif ($frame instanceof Protocol\ExchangeDeleteFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->exchange)); $buffer->append($frame->exchange); + $buffer->appendString($frame->exchange); $buffer->appendBits([$frame->ifUnused, $frame->nowait]); } elseif ($frame instanceof Protocol\ExchangeDeleteOkFrame) { } elseif ($frame instanceof Protocol\ExchangeBindFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->destination)); $buffer->append($frame->destination); - $buffer->appendUint8(\strlen($frame->source)); $buffer->append($frame->source); - $buffer->appendUint8(\strlen($frame->routingKey)); $buffer->append($frame->routingKey); + $buffer->appendString($frame->destination); + $buffer->appendString($frame->source); + $buffer->appendString($frame->routingKey); $buffer->appendBits([$frame->nowait]); $buffer->appendTable($frame->arguments); } elseif ($frame instanceof Protocol\ExchangeBindOkFrame) { } elseif ($frame instanceof Protocol\ExchangeUnbindFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->destination)); $buffer->append($frame->destination); - $buffer->appendUint8(\strlen($frame->source)); $buffer->append($frame->source); - $buffer->appendUint8(\strlen($frame->routingKey)); $buffer->append($frame->routingKey); + $buffer->appendString($frame->destination); + $buffer->appendString($frame->source); + $buffer->appendString($frame->routingKey); $buffer->appendBits([$frame->nowait]); $buffer->appendTable($frame->arguments); } elseif ($frame instanceof Protocol\ExchangeUnbindOkFrame) { } elseif ($frame instanceof Protocol\QueueDeclareFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->queue)); $buffer->append($frame->queue); + $buffer->appendString($frame->queue); $buffer->appendBits([$frame->passive, $frame->durable, $frame->exclusive, $frame->autoDelete, $frame->nowait]); $buffer->appendTable($frame->arguments); } elseif ($frame instanceof Protocol\QueueDeclareOkFrame) { - $buffer->appendUint8(\strlen($frame->queue)); $buffer->append($frame->queue); + $buffer->appendString($frame->queue); $buffer->appendInt32($frame->messageCount); $buffer->appendInt32($frame->consumerCount); } elseif ($frame instanceof Protocol\QueueBindFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->queue)); $buffer->append($frame->queue); - $buffer->appendUint8(\strlen($frame->exchange)); $buffer->append($frame->exchange); - $buffer->appendUint8(\strlen($frame->routingKey)); $buffer->append($frame->routingKey); + $buffer->appendString($frame->queue); + $buffer->appendString($frame->exchange); + $buffer->appendString($frame->routingKey); $buffer->appendBits([$frame->nowait]); $buffer->appendTable($frame->arguments); } elseif ($frame instanceof Protocol\QueueBindOkFrame) { } elseif ($frame instanceof Protocol\QueuePurgeFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->queue)); $buffer->append($frame->queue); + $buffer->appendString($frame->queue); $buffer->appendBits([$frame->nowait]); } elseif ($frame instanceof Protocol\QueuePurgeOkFrame) { $buffer->appendInt32($frame->messageCount); } elseif ($frame instanceof Protocol\QueueDeleteFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->queue)); $buffer->append($frame->queue); + $buffer->appendString($frame->queue); $buffer->appendBits([$frame->ifUnused, $frame->ifEmpty, $frame->nowait]); } elseif ($frame instanceof Protocol\QueueDeleteOkFrame) { $buffer->appendInt32($frame->messageCount); } elseif ($frame instanceof Protocol\QueueUnbindFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->queue)); $buffer->append($frame->queue); - $buffer->appendUint8(\strlen($frame->exchange)); $buffer->append($frame->exchange); - $buffer->appendUint8(\strlen($frame->routingKey)); $buffer->append($frame->routingKey); + $buffer->appendString($frame->queue); + $buffer->appendString($frame->exchange); + $buffer->appendString($frame->routingKey); $buffer->appendTable($frame->arguments); } elseif ($frame instanceof Protocol\QueueUnbindOkFrame) { } elseif ($frame instanceof Protocol\BasicQosFrame) { @@ -269,45 +280,45 @@ private function appendMethodFrame(Protocol\MethodFrame $frame, Buffer $buffer): } elseif ($frame instanceof Protocol\BasicQosOkFrame) { } elseif ($frame instanceof Protocol\BasicConsumeFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->queue)); $buffer->append($frame->queue); - $buffer->appendUint8(\strlen($frame->consumerTag)); $buffer->append($frame->consumerTag); + $buffer->appendString($frame->queue); + $buffer->appendString($frame->consumerTag); $buffer->appendBits([$frame->noLocal, $frame->noAck, $frame->exclusive, $frame->nowait]); $buffer->appendTable($frame->arguments); } elseif ($frame instanceof Protocol\BasicConsumeOkFrame) { - $buffer->appendUint8(\strlen($frame->consumerTag)); $buffer->append($frame->consumerTag); + $buffer->appendString($frame->consumerTag); } elseif ($frame instanceof Protocol\BasicCancelFrame) { - $buffer->appendUint8(\strlen($frame->consumerTag)); $buffer->append($frame->consumerTag); + $buffer->appendString($frame->consumerTag); $buffer->appendBits([$frame->nowait]); } elseif ($frame instanceof Protocol\BasicCancelOkFrame) { - $buffer->appendUint8(\strlen($frame->consumerTag)); $buffer->append($frame->consumerTag); + $buffer->appendString($frame->consumerTag); } elseif ($frame instanceof Protocol\BasicPublishFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->exchange)); $buffer->append($frame->exchange); - $buffer->appendUint8(\strlen($frame->routingKey)); $buffer->append($frame->routingKey); + $buffer->appendString($frame->exchange); + $buffer->appendString($frame->routingKey); $buffer->appendBits([$frame->mandatory, $frame->immediate]); } elseif ($frame instanceof Protocol\BasicReturnFrame) { $buffer->appendInt16($frame->replyCode); - $buffer->appendUint8(\strlen($frame->replyText)); $buffer->append($frame->replyText); - $buffer->appendUint8(\strlen($frame->exchange)); $buffer->append($frame->exchange); - $buffer->appendUint8(\strlen($frame->routingKey)); $buffer->append($frame->routingKey); + $buffer->appendString($frame->replyText); + $buffer->appendString($frame->exchange); + $buffer->appendString($frame->routingKey); } elseif ($frame instanceof Protocol\BasicDeliverFrame) { - $buffer->appendUint8(\strlen($frame->consumerTag)); $buffer->append($frame->consumerTag); + $buffer->appendString($frame->consumerTag); $buffer->appendInt64($frame->deliveryTag); $buffer->appendBits([$frame->redelivered]); - $buffer->appendUint8(\strlen($frame->exchange)); $buffer->append($frame->exchange); - $buffer->appendUint8(\strlen($frame->routingKey)); $buffer->append($frame->routingKey); + $buffer->appendString($frame->exchange); + $buffer->appendString($frame->routingKey); } elseif ($frame instanceof Protocol\BasicGetFrame) { $buffer->appendInt16($frame->reserved1); - $buffer->appendUint8(\strlen($frame->queue)); $buffer->append($frame->queue); + $buffer->appendString($frame->queue); $buffer->appendBits([$frame->noAck]); } elseif ($frame instanceof Protocol\BasicGetOkFrame) { $buffer->appendInt64($frame->deliveryTag); $buffer->appendBits([$frame->redelivered]); - $buffer->appendUint8(\strlen($frame->exchange)); $buffer->append($frame->exchange); - $buffer->appendUint8(\strlen($frame->routingKey)); $buffer->append($frame->routingKey); + $buffer->appendString($frame->exchange); + $buffer->appendString($frame->routingKey); $buffer->appendInt32($frame->messageCount); } elseif ($frame instanceof Protocol\BasicGetEmptyFrame) { - $buffer->appendUint8(\strlen($frame->clusterId)); $buffer->append($frame->clusterId); + $buffer->appendString($frame->clusterId); } elseif ($frame instanceof Protocol\BasicAckFrame) { $buffer->appendInt64($frame->deliveryTag); $buffer->appendBits([$frame->multiple]); @@ -334,5 +345,7 @@ private function appendMethodFrame(Protocol\MethodFrame $frame, Buffer $buffer): } else { throw new Exception\ProtocolException('Unhandled method frame ' . get_class($frame) . '.'); } + + return $buffer; } } diff --git a/test.php b/test.php index 2c4708e..dc667d0 100644 --- a/test.php +++ b/test.php @@ -1,24 +1,29 @@ connect(); /** @var Channel $channel */ $channel = yield $client->channel(); + yield $channel->queueDeclare('test_queue'); + + for ($i = 0; $i < 100; $i++) { + yield $channel->publish("test_$i", '', 'test_queue'); + } + yield $channel->consume(function (Message $message, Channel $channel) { - yield $channel->ack($message); - }, 'queue_name'); + echo $message->content() . \PHP_EOL; - \Amp\Loop::stop(); + yield $channel->ack($message); + }, 'test_queue'); }); diff --git a/tests/BufferTest.php b/tests/BufferTest.php new file mode 100644 index 0000000..e7ffc12 --- /dev/null +++ b/tests/BufferTest.php @@ -0,0 +1,391 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PHPinnacle\Ridge\Tests; + +use PHPinnacle\Ridge\Buffer; + +class BufferTest extends RidgeTest +{ + public function testGetLength() + { + $buf = new Buffer; + $this->assertEquals(0, $buf->size()); + + $buf->append('a'); + $this->assertEquals(1, $buf->size()); + + $buf->append('a'); + $this->assertEquals(2, $buf->size()); + + $buf->read(1); + $this->assertEquals(2, $buf->size()); + + $buf->read(2); + $this->assertEquals(2, $buf->size()); + + $buf->consume(1); + $this->assertEquals(1, $buf->size()); + + $buf->consume(1); + $this->assertEquals(0, $buf->size()); + } + + public function testIsEmpty() + { + $buf = new Buffer; + $this->assertTrue($buf->empty()); + + $buf->append('a'); + $this->assertFalse($buf->empty()); + } + + public function testRead() + { + $buf = new Buffer('abcd'); + + $this->assertEquals('a', $buf->read(1)); + $this->assertEquals(4, $buf->size()); + + $this->assertEquals('ab', $buf->read(2)); + $this->assertEquals(4, $buf->size()); + + $this->assertEquals('abc', $buf->read(3)); + $this->assertEquals(4, $buf->size()); + + $this->assertEquals('abcd', $buf->read(4)); + $this->assertEquals(4, $buf->size()); + } + + public function testReadOffset() + { + $buf = new Buffer('abcd'); + + $this->assertEquals('a', $buf->read(1, 0)); + $this->assertEquals(4, $buf->size()); + + $this->assertEquals('b', $buf->read(1, 1)); + $this->assertEquals(4, $buf->size()); + + $this->assertEquals('c', $buf->read(1, 2)); + $this->assertEquals(4, $buf->size()); + + $this->assertEquals('d', $buf->read(1, 3)); + $this->assertEquals(4, $buf->size()); + } + + /** + * @expectedException \PHPinnacle\Ridge\Exception\BufferUnderflow + */ + public function testReadThrows() + { + $buf = new Buffer; + $buf->read(1); + } + + public function testConsume() + { + $buf = new Buffer('abcd'); + + $this->assertEquals('a', $buf->consume(1)); + $this->assertEquals(3, $buf->size()); + + $this->assertEquals('bc', $buf->consume(2)); + $this->assertEquals(1, $buf->size()); + + $this->assertEquals('d', $buf->consume(1)); + $this->assertEquals(0, $buf->size()); + } + + /** + * @expectedException \PHPinnacle\Ridge\Exception\BufferUnderflow + */ + public function testConsumeThrows() + { + $buf = new Buffer; + $buf->consume(1); + } + + public function testDiscard() + { + $buf = new Buffer('abcd'); + + $buf->discard(1); + $this->assertEquals('bcd', $buf->read($buf->size())); + $this->assertEquals(3, $buf->size()); + + $buf->discard(2); + $this->assertEquals('d', $buf->read($buf->size())); + $this->assertEquals(1, $buf->size()); + + $buf->discard(1); + $this->assertEquals(0, $buf->size()); + $this->assertTrue($buf->empty()); + } + + /** + * @expectedException \PHPinnacle\Ridge\Exception\BufferUnderflow + */ + public function testDiscardThrows() + { + $buf = new Buffer; + $buf->discard(1); + } + + public function testSlice() + { + $buf = new Buffer('abcd'); + + $slice1 = $buf->slice(1); + $this->assertEquals('a', $slice1->read($slice1->size())); + $this->assertEquals(4, $buf->size()); + + $slice2 = $buf->slice(2); + $this->assertEquals('ab', $slice2->read($slice2->size())); + $this->assertEquals(4, $buf->size()); + + $slice3 = $buf->slice(3); + $this->assertEquals('abc', $slice3->read($slice3->size())); + $this->assertEquals(4, $buf->size()); + + $slice4 = $buf->slice(4); + $this->assertEquals('abcd', $slice4->read($slice4->size())); + $this->assertEquals(4, $buf->size()); + } + + /** + * @expectedException \PHPinnacle\Ridge\Exception\BufferUnderflow + */ + public function testSliceThrows() + { + $buf = new Buffer; + $buf->slice(1); + } + + public function testConsumeSlice() + { + $buf = new Buffer('abcdef'); + + $slice1 = $buf->consumeSlice(1); + $this->assertEquals('a', $slice1->read($slice1->size())); + $this->assertEquals(5, $buf->size()); + + $slice2 = $buf->consumeSlice(2); + $this->assertEquals('bc', $slice2->read($slice2->size())); + $this->assertEquals(3, $buf->size()); + + $slice3 = $buf->consumeSlice(3); + $this->assertEquals('def', $slice3->read($slice3->size())); + $this->assertEquals(0, $buf->size()); + } + + /** + * @expectedException \PHPinnacle\Ridge\Exception\BufferUnderflow + */ + public function testConsumeSliceThrows() + { + $buf = new Buffer; + $buf->consumeSlice(1); + } + + public function testAppend() + { + $buf = new Buffer; + $this->assertEquals(0, $buf->size()); + + $buf->append('abcd'); + $this->assertEquals(4, $buf->size()); + $this->assertEquals('abcd', $buf->read(4)); + + $buf->append('efgh'); + $this->assertEquals(8, $buf->size()); + $this->assertEquals('abcdefgh', $buf->read(8)); + } + + public function testAppendBuffer() + { + $buf = new Buffer; + $this->assertEquals(0, $buf->size()); + + $buf->append(new Buffer('ab')); + $this->assertEquals(2, $buf->size()); + $this->assertEquals('ab', $buf->read(2)); + + $buf->append('cd'); + $this->assertEquals(4, $buf->size()); + $this->assertEquals('abcd', $buf->read(4)); + + $buf->append(new Buffer('ef')); + $this->assertEquals(6, $buf->size()); + $this->assertEquals('abcdef', $buf->read(6)); + } + + // 8-bit integer functions + + public function testReadUint8() + { + $this->assertEquals(0xA9, (new Buffer("\xA9"))->readUint8()); + } + + public function testReadInt8() + { + $this->assertEquals(0xA9 - 0x100, (new Buffer("\xA9"))->readInt8()); + } + + public function testConsumeUint8() + { + $this->assertEquals(0xA9, (new Buffer("\xA9"))->consumeUint8()); + } + + public function testConsumeInt8() + { + $this->assertEquals(0xA9 - 0x100, (new Buffer("\xA9"))->consumeInt8()); + } + + public function testAppendUint8() + { + $this->assertEquals("\xA9", (new Buffer)->appendUint8(0xA9)->read(1)); + } + + public function testAppendInt8() + { + $this->assertEquals("\xA9", (new Buffer)->appendInt8(0xA9 - 0x100)->read(1)); + } + + // 16-bit integer functions + + public function testReadUint16() + { + $this->assertEquals(0xA978, (new Buffer("\xA9\x78"))->readUint16()); + } + + public function testReadInt16() + { + $this->assertEquals(0xA978 - 0x10000, (new Buffer("\xA9\x78"))->readInt16()); + } + + public function testConsumeUint16() + { + $this->assertEquals(0xA978, (new Buffer("\xA9\x78"))->consumeUint16()); + } + + public function testConsumeInt16() + { + $this->assertEquals(0xA978 - 0x10000, (new Buffer("\xA9\x78"))->consumeInt16()); + } + + public function testAppendUint16() + { + $this->assertEquals("\xA9\x78", (new Buffer)->appendUint16(0xA978)->read(2)); + } + + public function testAppendInt16() + { + $this->assertEquals("\xA9\x78", (new Buffer)->appendInt16(0xA978)->read(2)); + } + + // 32-bit integer functions + + public function testReadUint32() + { + $this->assertEquals(0xA9782361, (new Buffer("\xA9\x78\x23\x61"))->readUint32()); + } + + public function testReadInt32() + { + $this->assertEquals(0xA9782361 - 0x100000000, (new Buffer("\xA9\x78\x23\x61"))->readInt32()); + } + + public function testConsumeUint32() + { + $this->assertEquals(0xA9782361, (new Buffer("\xA9\x78\x23\x61"))->consumeUint32()); + } + + public function testConsumeInt32() + { + $this->assertEquals(0xA9782361 - 0x100000000, (new Buffer("\xA9\x78\x23\x61"))->consumeInt32()); + } + + public function testAppendUint32() + { + $this->assertEquals("\xA9\x78\x23\x61", (new Buffer)->appendUint32(0xA9782361)->read(4)); + } + + public function testAppendInt32() + { + $this->assertEquals("\xA9\x78\x23\x61", (new Buffer)->appendInt32(0xA9782361)->read(4)); + } + + // 64-bit integer functions + + public function testReadUint64() + { + $this->assertEquals(0x1978236134738525, (new Buffer("\x19\x78\x23\x61\x34\x73\x85\x25"))->readUint64()); + } + + public function testReadInt64() + { + $this->assertEquals(-2, (new Buffer("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE"))->readInt64()); + } + + public function testConsumeUint64() + { + $this->assertEquals(0x1978236134738525, (new Buffer("\x19\x78\x23\x61\x34\x73\x85\x25"))->consumeUint64()); + } + + public function testConsumeInt64() + { + $this->assertEquals(-2, (new Buffer("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE"))->consumeInt64()); + } + + public function testAppendUint64() + { + $this->assertEquals("\x19\x78\x23\x61\x34\x73\x85\x25", (new Buffer)->appendUint64(0x1978236134738525)->read(8)); + } + + public function testAppendInt64() + { + $this->assertEquals("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE", (new Buffer)->appendInt64(-2)->read(8)); + } + + // float + + public function testReadFloat() + { + $this->assertEquals(1.5, (new Buffer("\x3F\xC0\x00\x00"))->readFloat()); + } + + public function testConsumeFloat() + { + $this->assertEquals(1.5, (new Buffer("\x3F\xC0\x00\x00"))->consumeFloat()); + } + + public function testAppendFloat() + { + $this->assertEquals("\x3F\xC0\x00\x00", (new Buffer)->appendFloat(1.5)->read(4)); + } + + // double + + public function testReadDouble() + { + $this->assertEquals(1.5, (new Buffer("\x3F\xF8\x00\x00\x00\x00\x00\x00"))->readDouble()); + } + + public function testConsumeDouble() + { + $this->assertEquals(1.5, (new Buffer("\x3F\xF8\x00\x00\x00\x00\x00\x00"))->consumeDouble()); + } + + public function testAppendDouble() + { + $this->assertEquals("\x3F\xF8\x00\x00\x00\x00\x00\x00", (new Buffer)->appendDouble(1.5)->read(8)); + } +}