├── .gitignore ├── .travis.yml ├── AUTHORS ├── CONTRIBUTORS ├── LICENSE ├── Makefile ├── Makefile.help.mk ├── README.md ├── VERSION ├── composer.json ├── examples ├── connect.php ├── connectauth.php ├── connectauthtoken.php ├── ping.php ├── pubsub │ ├── jsonencodedpubsub.php │ ├── pub.php │ ├── pubsub.php │ └── sub.php ├── reqres │ ├── jsonencodedreqres.php │ ├── reqres.php │ └── request.php └── simpleclient │ └── main.php ├── phpspec.yml ├── phpunit.xml.dist ├── ruleset.xml ├── spec └── Nats │ ├── ConnectionOptionsSpec.php │ ├── ConnectionSpec.php │ ├── MessageSpec.php │ └── ServerInfoSpec.php ├── src └── Nats │ ├── Connection.php │ ├── ConnectionOptions.php │ ├── EncodedConnection.php │ ├── Encoders │ ├── Encoder.php │ ├── JSONEncoder.php │ ├── PHPEncoder.php │ └── YAMLEncoder.php │ ├── Exception.php │ ├── Message.php │ ├── Php71RandomGenerator.php │ └── ServerInfo.php └── test ├── ConnectionOptionsTest.php ├── ConnectionTest.php ├── EncodedConnectionTest.php ├── Encoders ├── JSONEncoderTest.php ├── PHPEncoderTest.php └── YAMLEncoderTest.php ├── MessageTest.php └── test.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .contrib 2 | bin 3 | vendor 4 | cover 5 | *.phar 6 | /composer.phar 7 | /phpdoc.phar 8 | /composer.lock 9 | /docs/api 10 | build 11 | .idea 12 | coverage 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | language: php 5 | php: 6 | - 5.6 7 | - 7 8 | - 7.1 9 | cache: 10 | directories: 11 | - vendor 12 | - $HOME/.composer/cache 13 | before_install: 14 | - docker run -d -p 4222:4222 --name nats nats 15 | before_script: 16 | - make dist-clean 17 | - make deps 18 | - make dev-deps 19 | script: 20 | - mkdir -p build/logs 21 | - make lint-php 22 | - make test 23 | after_script: 24 | - php vendor/bin/coveralls 25 | - php vendor/bin/coveralls -v 26 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Raül Pérez - http://repejota.com - @repejota 2 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Adria Cidre 2 | Anthony Sterling 3 | DependenCI Bot 4 | Dominique Feyer 5 | Dominique Feyer 6 | Donal Byrne 7 | Donal Byrne 8 | Gorka López de Torre 9 | Issel Guberna 10 | Ivan Padavic 11 | Jose Gil 12 | Oliver Mack 13 | Oliver Mack 14 | Raül Pérez 15 | Raül Pérez 16 | Raül Pérez 17 | Raül Pérez 18 | Raül Pérez 19 | Raül Pérez 20 | Serghei Iakovlev 21 | Superbug 22 | jgil 23 | netroby 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2017 repejota@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PHPCS_PHAR = https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar 2 | COMPOSER_PHAR = https://getcomposer.org/composer.phar 3 | PHPDOCUMENTOR_PHAR_URL = https://github.com/phpDocumentor/phpDocumentor2/releases/download/v2.9.0/phpDocumentor.phar 4 | CLEAN_FILES = composer.phar composer.lock phpdoc.phar phpcs.phar phpcbf.phar .idea 5 | CLEAN_FOLDERS = bin build cover vendor docs/api 6 | CLEAN_PATHS = $(CLEAN_FILES) $(CLEAN_FOLDERS) 7 | SOURCE_CODE_PATHS = src test examples 8 | API_DOCS_PATH = ./docs/api 9 | COVERAGE_PATH = ./cover 10 | 11 | define require_phar 12 | @[ -f ./$(1) ] || wget -q $(2) -O ./$(1) && chmod +x $(1); 13 | endef 14 | 15 | lint: ## Lint source code 16 | lint: lint-php lint-psr2 lint-squiz 17 | 18 | .PHONY: lint-php 19 | lint-php: 20 | find $(SOURCE_CODE_PATHS) spec -name *.php -exec php -l {} \; 21 | 22 | .PHONY: lint-psr2 23 | lint-psr2: 24 | $(call require_phar,phpcs.phar,$(PHPCS_PHAR)) 25 | ./phpcs.phar --standard=PSR2 --colors -w -s --warning-severity=0 $(SOURCE_CODE_PATHS) 26 | 27 | .PHONY: lint-squiz 28 | lint-squiz: 29 | $(call require_phar,phpcs.phar,$(PHPCS_PHAR)) 30 | ./phpcs.phar --standard=Squiz,./ruleset.xml --colors -w -s --warning-severity=0 $(SOURCE_CODE_PATHS) 31 | 32 | 33 | test: ## Execute all tests suites TDD and BDD 34 | test: test-tdd test-bdd 35 | 36 | .PHONY: test-tdd 37 | test-tdd: 38 | ./vendor/bin/phpunit test 39 | 40 | .PHONY: test-bdd 41 | test-bdd: 42 | ./vendor/bin/phpspec run --format=pretty -v 43 | 44 | cover: ## Generate coverage report 45 | ./vendor/bin/phpunit --coverage-html $(COVERAGE_PATH) test 46 | 47 | deps: ## Install dependencies 48 | $(call require_phar,composer.phar,$(COMPOSER_PHAR)) 49 | ./composer.phar install --no-dev 50 | 51 | dev-deps: ## Install development dependencies 52 | $(call require_phar,composer.phar,$(COMPOSER_PHAR)) 53 | ./composer.phar install 54 | 55 | dist-clean: ## Clean developer files, dependenciers and temporary files 56 | rm -rf $(CLEAN_PATHS) 57 | 58 | docker-nats: ## Start NATS container ( for testing purposes ) 59 | docker run --rm -p 8222:8222 -p 4222:4222 -d --name nats-main nats 60 | 61 | phpdoc: ## Generate phpdoc API documentation 62 | $(call require_phar,phpdoc.phar,$(PHPDOCUMENTOR_PHAR_URL)) 63 | ./phpdoc.phar -d ./src/ -t $(API_DOCS_PATH) --template=checkstyle --template=responsive-twig 64 | 65 | serve-phpdoc: ## Serve phpdoc API documentation at http;://localhost:8000 66 | cd $(API_DOCS_PATH) && php -S localhost:8000 && cd ../.. 67 | 68 | include Makefile.help.mk 69 | 70 | -------------------------------------------------------------------------------- /Makefile.help.mk: -------------------------------------------------------------------------------- 1 | .PHONY: help 2 | help: ## Show this help ( default ) 3 | @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//' 4 | 5 | .DEFAULT_GOAL := help 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | phpnats 2 | ======= 3 | 4 | **Travis** 5 | 6 | | Master | Develop | 7 | | ------------- | ------------- | 8 | | [![Build Status](https://travis-ci.org/repejota/phpnats.png?branch=master)](https://travis-ci.org/repejota/phpnats) | [![Build Status](https://travis-ci.org/repejota/phpnats.png?branch=develop)](https://travis-ci.org/repejota/phpnats) | 9 | 10 | **Coverage** 11 | 12 | | Master | Develop | 13 | | ------------- | ------------- | 14 | | [![Coverage Status](https://coveralls.io/repos/repejota/phpnats/badge.svg?branch=master)](https://coveralls.io/r/repejota/phpnats?branch=master) | [![Coverage Status](https://coveralls.io/repos/repejota/phpnats/badge.svg?branch=develop)](https://coveralls.io/r/repejota/phpnats?branch=develop) | 15 | 16 | Introduction 17 | ------------ 18 | 19 | A PHP client for the [NATS messaging system](https://nats.io). 20 | 21 | Requirements 22 | ------------ 23 | 24 | * php 5.6+ 25 | * [gnatsd](https://github.com/apcera/gnatsd) 26 | 27 | 28 | Usage 29 | ----- 30 | 31 | ### Installation 32 | 33 | Let's start by downloading composer into our project dir: 34 | ``` 35 | curl -O http://getcomposer.org/composer.phar 36 | chmod +x composer.phar 37 | ``` 38 | 39 | Now let's tell composer about our project's dependancies, in this case, PHPNats. The way we do this is by creating a composer.json file, and placing it in the root folder of our project, right next to composer.phar 40 | 41 | ``` 42 | { 43 | "require": { 44 | "repejota/nats": "dev-master" 45 | } 46 | } 47 | ``` 48 | Let's let Composer work its magic: 49 | ``` 50 | php composer.phar install 51 | ``` 52 | Composer will download all the dependencies defined in composer.json, and prepare all the files needed to autoload them. 53 | 54 | 55 | ### Basic Usage 56 | 57 | ```php 58 | $client = new \Nats\Connection(); 59 | $client->connect(); 60 | 61 | // Publish Subscribe 62 | 63 | // Simple Subscriber. 64 | $client->subscribe( 65 | 'foo', 66 | function ($message) { 67 | printf("Data: %s\r\n", $message->getBody()); 68 | } 69 | ); 70 | 71 | // Simple Publisher. 72 | $client->publish('foo', 'Marty McFly'); 73 | 74 | // Wait for 1 message. 75 | $client->wait(1); 76 | 77 | // Request Response 78 | 79 | // Responding to requests. 80 | $sid = $client->subscribe( 81 | 'sayhello', 82 | function ($message) { 83 | $message->reply('Reply: Hello, '.$message->getBody().' !!!'); 84 | } 85 | ); 86 | 87 | // Request. 88 | $client->request( 89 | 'sayhello', 90 | 'Marty McFly', 91 | function ($message) { 92 | echo $message->getBody(); 93 | } 94 | ); 95 | ``` 96 | 97 | ### Encoded Connections 98 | 99 | ```php 100 | $encoder = new \Nats\Encoders\JSONEncoder(); 101 | $options = new \Nats\ConnectionOptions(); 102 | $client = new \Nats\EncodedConnection($options, $encoder); 103 | $client->connect(); 104 | 105 | // Publish Subscribe 106 | 107 | // Simple Subscriber. 108 | $client->subscribe( 109 | 'foo', 110 | function ($payload) { 111 | printf("Data: %s\r\n", $payload->getBody()[1]); 112 | } 113 | ); 114 | 115 | // Simple Publisher. 116 | $client->publish( 117 | 'foo', 118 | [ 119 | 'Marty', 120 | 'McFly', 121 | ] 122 | ); 123 | 124 | // Wait for 1 message. 125 | $client->wait(1); 126 | 127 | // Request Response 128 | 129 | // Responding to requests. 130 | $sid = $client->subscribe( 131 | 'sayhello', 132 | function ($message) { 133 | $message->reply('Reply: Hello, '.$message->getBody()[1].' !!!'); 134 | } 135 | ); 136 | 137 | // Request. 138 | $client->request( 139 | 'sayhello', 140 | [ 141 | 'Marty', 142 | 'McFly', 143 | ], 144 | function ($message) { 145 | echo $message->getBody(); 146 | } 147 | ); 148 | ``` 149 | 150 | 151 | Developer's Information 152 | ----------------------- 153 | 154 | ### Releases 155 | 156 | * [Latest stable](https://github.com/repejota/phpnats/tree/master) 157 | * [Latest dev](https://github.com/repejota/phpnats/tree/develop) 158 | 159 | * [PHPNats on Packagist](https://packagist.org/packages/repejota/nats) 160 | 161 | ### Tests 162 | 163 | Tests are in the `tests` folder. 164 | To run them, you need `PHPUnit` and execute `make test-tdd`. 165 | 166 | We also have a BDD test suite under the `spec` folder. 167 | To run the suite, you need `PHPSpec` and execute `make test-bdd`. 168 | 169 | You can also execute the all suites ( TDD + BDD ) with `make test`. 170 | 171 | ### Code Quality 172 | 173 | We are using [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer/docs) 174 | to ensure our code follow an high quality standard. 175 | 176 | To perform an analysis of the code execute `make lint`. 177 | 178 | There is currently three steps when we lint our code: 179 | 180 | * First we lint with php itself `php -l` 181 | * Then we lint with PSR2 standard 182 | * And finally we lint with a custom [ruleset.xml](https://github.com/repejota/phpnats/blob/feature/lint-squiz/ruleset.xml) that checks dockblocks and different performance tips. 183 | 184 | 185 | Creators 186 | -------- 187 | 188 | **Raül Pérez** 189 | 190 | - 191 | - 192 | 193 | License 194 | ------- 195 | 196 | MIT, see [LICENSE](LICENSE) 197 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.8.7 -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "repejota/nats", 3 | "description": "A nats.io client in PHP", 4 | "type": "library", 5 | "minimum-stability": "dev", 6 | "license": "MIT", 7 | "require": { 8 | "ircmaxell/random-lib": "^1.2" 9 | }, 10 | "require-dev": { 11 | "phpunit/phpunit": "5.*", 12 | "phpspec/phpspec": "^3.0", 13 | "satooshi/php-coveralls": "2.0.x-dev", 14 | "leanphp/phpspec-code-coverage": "^3.2@dev" 15 | }, 16 | "authors": [ 17 | { 18 | "name": "Raül Pérez", 19 | "email": "repejota@gmail.com", 20 | "homepage": "http://www.repejota.com", 21 | "role": "Developer" 22 | }, 23 | { 24 | "name": "Adrià Cidre", 25 | "email": "adria.cidre@gmail.com", 26 | "homepage": "http://oridoki.com", 27 | "role": "Developer" 28 | }, 29 | { 30 | "name": "José Gil", 31 | "email": "josgilmo@gmail.com", 32 | "role": "Developer" 33 | }, 34 | { 35 | "name": "Gorka López de Torre", 36 | "email": "glopezdetorre@gmail.com", 37 | "homepage": "http://gorka.io", 38 | "role": "Developer" 39 | } 40 | ], 41 | "autoload": { 42 | "psr-0": { 43 | "Nats": "src", 44 | "Nats\\Test": "src" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/connect.php: -------------------------------------------------------------------------------- 1 | connect(); 6 | $c->close(); 7 | -------------------------------------------------------------------------------- /examples/connectauth.php: -------------------------------------------------------------------------------- 1 | setHost('localhost')->setPort(4222)->setUser('foo')->setPass('bar'); 6 | $c = new Nats\Connection($connectionOptions); 7 | $c->connect(); 8 | $c->close(); 9 | -------------------------------------------------------------------------------- /examples/connectauthtoken.php: -------------------------------------------------------------------------------- 1 | setHost('localhost')->setPort(4222)->setToken('supersecrettoken'); 6 | $c = new Nats\Connection($connectionOptions); 7 | $c->connect(); 8 | $c->close(); 9 | -------------------------------------------------------------------------------- /examples/ping.php: -------------------------------------------------------------------------------- 1 | setHost('localhost')->setPort(4222); 6 | 7 | echo 'Server: nats://'.$connectionOptions->getHost().':'.$connectionOptions->getPort().PHP_EOL; 8 | 9 | $c = new Nats\Connection($connectionOptions); 10 | $c->connect(); 11 | 12 | 13 | // TODO ping is not public. 14 | $c->ping(); 15 | -------------------------------------------------------------------------------- /examples/pubsub/jsonencodedpubsub.php: -------------------------------------------------------------------------------- 1 | connect(); 8 | 9 | // Publish Subscribe. 10 | // Simple Subscriber. 11 | $client->subscribe( 12 | 'foo', 13 | function ($payload) { 14 | printf("Data: %s\r\n", $payload->getBody()[1]); 15 | } 16 | ); 17 | 18 | // Simple Publisher. 19 | $client->publish( 20 | 'foo', 21 | [ 22 | 'Marty', 23 | 'McFly', 24 | ] 25 | ); 26 | 27 | // Wait for 1 message. 28 | $client->wait(1); 29 | -------------------------------------------------------------------------------- /examples/pubsub/pub.php: -------------------------------------------------------------------------------- 1 | connect(); 6 | 7 | // Simple Publisher. 8 | $client->publish('foo', 'bar'); 9 | $client->close(); 10 | -------------------------------------------------------------------------------- /examples/pubsub/pubsub.php: -------------------------------------------------------------------------------- 1 | connect(); 6 | 7 | // Publish Subscribe. 8 | // Simple Subscriber. 9 | $client->subscribe( 10 | 'foo', 11 | function ($message) { 12 | printf("Data: %s\r\n", $message->getBody()); 13 | } 14 | ); 15 | 16 | // Simple Publisher. 17 | $client->publish('foo', 'Marty McFly'); 18 | 19 | // Wait for 1 message. 20 | $client->wait(1); 21 | 22 | $client->close(); 23 | -------------------------------------------------------------------------------- /examples/pubsub/sub.php: -------------------------------------------------------------------------------- 1 | connect(); 6 | 7 | // Simple Subscriber. 8 | $client->subscribe( 9 | 'foo', 10 | function ($message) { 11 | printf("Data: %s\r\n", $message->getBody()); 12 | } 13 | ); 14 | 15 | $client->close(); 16 | -------------------------------------------------------------------------------- /examples/reqres/jsonencodedreqres.php: -------------------------------------------------------------------------------- 1 | connect(); 8 | 9 | // Request Response. 10 | // Responding to requests. 11 | $sid = $client->subscribe( 12 | 'sayhello', 13 | function ($message) { 14 | $message->reply('Reply: Hello, '.$message->getBody()[1].' !!!'); 15 | } 16 | ); 17 | 18 | // Request. 19 | $client->request( 20 | 'sayhello', 21 | [ 22 | 'Marty', 23 | 'McFly', 24 | ], 25 | function ($message) { 26 | echo $message->getBody(); 27 | } 28 | ); 29 | -------------------------------------------------------------------------------- /examples/reqres/reqres.php: -------------------------------------------------------------------------------- 1 | connect(); 6 | 7 | // Request Response. 8 | // Responding to requests. 9 | $sid = $client->subscribe( 10 | 'sayhello', 11 | function ($message) { 12 | $message->reply('Reply: Hello, '.$message->getBody().' !!!'); 13 | } 14 | ); 15 | 16 | // Request. 17 | $client->request( 18 | 'sayhello', 19 | 'Marty McFly', 20 | function ($message) { 21 | echo $message->getBody(); 22 | } 23 | ); 24 | -------------------------------------------------------------------------------- /examples/reqres/request.php: -------------------------------------------------------------------------------- 1 | connect(); 8 | 9 | // Request. 10 | $client->request( 11 | 'foo', 12 | 'Marty McFly', 13 | function ($message) { 14 | echo $message->getBody(); 15 | } 16 | ); 17 | 18 | $client->close(); 19 | -------------------------------------------------------------------------------- /examples/simpleclient/main.php: -------------------------------------------------------------------------------- 1 | connect(); 8 | 9 | printf('Connected to NATS at %s'.PHP_EOL, $nc->connectedServerId()); 10 | 11 | $nc->wait(); 12 | -------------------------------------------------------------------------------- /phpspec.yml: -------------------------------------------------------------------------------- 1 | extensions: 2 | LeanPHP\PhpSpec\CodeCoverage\CodeCoverageExtension: ~ 3 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | ./tests/Unit/ 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | php-nats Coding Standards for Inline Documentation and Comments 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /spec/Nats/ConnectionOptionsSpec.php: -------------------------------------------------------------------------------- 1 | shouldHaveType('Nats\ConnectionOptions'); 14 | } 15 | 16 | function it_has_default_host_value_as_localhost() { 17 | $this->getHost()->shouldEqual("localhost"); 18 | } 19 | 20 | function it_has_default_port_value_as_4222() { 21 | $this->getPort()->shouldBe(4222); 22 | } 23 | 24 | function it_has_default_user_value_as_null() { 25 | $this->getUser()->shouldBe(null); 26 | } 27 | 28 | function it_has_default_pass_value_as_null() { 29 | $this->getPass()->shouldBe(null); 30 | } 31 | 32 | function it_has_default_lang_value_as_php() { 33 | $this->getLang()->shouldEqual("php"); 34 | } 35 | 36 | function it_has_default_verbose_value_as_null() { 37 | $this->isVerbose()->shouldBe(false); 38 | } 39 | 40 | function it_has_default_pedantic_value_as_null() { 41 | $this->isPedantic()->shouldBe(false); 42 | } 43 | 44 | function it_has_default_reconnect_value_as_null() { 45 | $this->isReconnect()->shouldBe(true); 46 | } 47 | 48 | function it_returns_a_valid_default_address() { 49 | $this->getAddress()->shouldEqual("tcp://localhost:4222"); 50 | } 51 | 52 | function it_options_can_be_overrided() 53 | { 54 | $options = array( 55 | 'host' => 'remotehost.com', 56 | 'port' => 4321, 57 | 'user' => 'user', 58 | 'pass' => 'pass', 59 | 'token' => 'token', 60 | 'lang' => 'php', 61 | 'version' => '1.2.3', 62 | 'verbose' => true, 63 | 'pedantic' => true, 64 | 'reconnect' => false, 65 | ); 66 | $this->setConnectionOptions($options); 67 | $this->getHost()->shouldBe('remotehost.com'); 68 | $this->getPort()->shouldBe(4321); 69 | $this->getUser()->shouldBe('user'); 70 | $this->getPass()->shouldBe('pass'); 71 | $this->getToken()->shouldBe('token'); 72 | $this->getLang()->shouldBe('php'); 73 | $this->getVersion()->shouldBe('1.2.3'); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /spec/Nats/ConnectionSpec.php: -------------------------------------------------------------------------------- 1 | shouldHaveType('Nats\Connection'); 12 | } 13 | 14 | function it_has_ping_count_to_zero() 15 | { 16 | $this->pingsCount()->shouldBe(0); 17 | } 18 | 19 | function it_has_pubs_count_to_zero() 20 | { 21 | $this->pubsCount()->shouldBe(0); 22 | } 23 | 24 | function it_has_reconnects_count_to_zero() 25 | { 26 | $this->reconnectsCount()->shouldBe(0); 27 | } 28 | 29 | function it_has_subscriptions_count_to_zero() 30 | { 31 | $this->subscriptionsCount()->shouldBe(0); 32 | } 33 | 34 | function it_subscriptions_array_is_empty() 35 | { 36 | $this->getSubscriptions()->shouldHaveCount(0); 37 | } 38 | 39 | function it_is_disconnected() 40 | { 41 | $this->isConnected()->shouldBe(false); 42 | } 43 | 44 | function it_connects_and_disconnects_with_default_options() 45 | { 46 | $this->connect(); 47 | $this->shouldHaveType('Nats\Connection'); 48 | $this->isConnected()->shouldBe(true); 49 | $this->close(); 50 | $this->isConnected()->shouldBe(false); 51 | } 52 | 53 | function it_increases_reconnects_count_on_each_reconnection() 54 | { 55 | $this->connect(); 56 | $this->reconnect(); 57 | $this->reconnectsCount()->shouldBe(1); 58 | $this->isConnected()->shouldBe(true); 59 | $this->close(); 60 | } 61 | 62 | function it_sends_ping_after_a_successful_connection() 63 | { 64 | $this->connect(); 65 | $this->pingsCount()->shouldBe(1); 66 | $this->close(); 67 | } 68 | 69 | function it_increases_pubs_after_publishing_a_message() 70 | { 71 | $this->connect(); 72 | $this->publish("foo"); 73 | $this->pubsCount()->shouldBe(1); 74 | $this->close(); 75 | } 76 | 77 | function it_increases_subscriptions_after_subscribing_to_a_topic() 78 | { 79 | $this->connect(); 80 | $callback = function($payload) {}; 81 | $sid = $this->subscribe("foo", $callback); 82 | $this->subscriptionsCount()->shouldBe(1); 83 | $this->unsubscribe($sid); 84 | $this->close(); 85 | } 86 | 87 | function it_decreases_subscriptions_after_unsubscribing_to_a_topic() 88 | { 89 | $this->connect(); 90 | $callback = function($payload) {}; 91 | $sid = $this->subscribe("foo", $callback); 92 | $this->unsubscribe($sid); 93 | $this->subscriptionsCount()->shouldBe(0); 94 | $this->close(); 95 | } 96 | 97 | function it_sends_a_message_with_a_1024c_subject() 98 | { 99 | $this->connect(); 100 | $subject = str_pad("", 1024*1, "x"); 101 | $this->publish($subject); 102 | $this->pubsCount()->shouldBe(1); 103 | $this->close(); 104 | } 105 | 106 | function it_sends_a_message_with_a_1024x10c_subject() 107 | { 108 | $this->connect(); 109 | $subject = str_pad("", 1024*10, "x"); 110 | $this->publish($subject); 111 | $this->pubsCount()->shouldBe(1); 112 | $this->close(); 113 | } 114 | 115 | function it_sends_a_message_with_a_1024x100c_subject() 116 | { 117 | $this->connect(); 118 | $subject = str_pad("", 1024*100, "x"); 119 | $this->publish($subject); 120 | $this->pubsCount()->shouldBe(1); 121 | $this->close(); 122 | } 123 | } -------------------------------------------------------------------------------- /spec/Nats/MessageSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith('subject', 'body', 'sid', $conn); 15 | } 16 | 17 | function it_is_initializable() 18 | { 19 | $this->shouldHaveType('Nats\Message'); 20 | } 21 | 22 | function it_has_a_subject() 23 | { 24 | $this->getSubject()->shouldBe('subject'); 25 | } 26 | 27 | function it_has_a_body() 28 | { 29 | $this->getBody()->shouldBe('body'); 30 | } 31 | 32 | function it_has_a_sid() 33 | { 34 | $this->getSid()->shouldBe('sid'); 35 | } 36 | 37 | function it_has_connection() 38 | { 39 | $this->getConn()->shouldHaveType('Nats\Connection'); 40 | } 41 | } -------------------------------------------------------------------------------- /spec/Nats/ServerInfoSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($message); 12 | } 13 | 14 | function it_is_initializable() 15 | { 16 | $this->shouldHaveType('Nats\ServerInfo'); 17 | } 18 | 19 | function it_has_server_id() 20 | { 21 | $this->getServerID()->shouldNotBeNull(); 22 | } 23 | 24 | function it_has_server_host() 25 | { 26 | $this->getHost()->shouldBe("0.0.0.0"); 27 | } 28 | 29 | function it_has_server_port() 30 | { 31 | $this->getPort()->shouldBe(4222); 32 | } 33 | 34 | function it_has_version() 35 | { 36 | $this->getVersion()->shouldBe("0.9.6"); 37 | } 38 | 39 | function it_has_go_version() 40 | { 41 | $this->getGoVersion()->shouldBe("go1.7.3"); 42 | } 43 | 44 | function it_has_is_auth_required() 45 | { 46 | $this->isAuthRequired()->shouldBeBoolean(); 47 | } 48 | 49 | function it_has_is_tls_required() 50 | { 51 | $this->isTLSRequired()->shouldBeBoolean(); 52 | } 53 | 54 | function it_has_is_tls_verified() 55 | { 56 | $this->isTLSVerify()->shouldBeBoolean(); 57 | } 58 | 59 | function it_has_is_ssl_required() 60 | { 61 | $this->isSSLRequired()->shouldBeBoolean(); 62 | } 63 | 64 | function it_has_max_payload() 65 | { 66 | $this->getMaxPayload()->shouldBe(1048576); 67 | } 68 | } -------------------------------------------------------------------------------- /src/Nats/Connection.php: -------------------------------------------------------------------------------- 1 | debug = $debug; 35 | } 36 | 37 | /** 38 | * Number of PINGs. 39 | * 40 | * @var integer number of pings. 41 | */ 42 | private $pings = 0; 43 | 44 | 45 | /** 46 | * Return the number of pings. 47 | * 48 | * @return integer Number of pings 49 | */ 50 | public function pingsCount() 51 | { 52 | return $this->pings; 53 | } 54 | 55 | /** 56 | * Chunk size in bytes to use when reading an stream of data. 57 | * 58 | * @var integer size of chunk. 59 | */ 60 | private $chunkSize = 1500; 61 | 62 | /** 63 | * Number of messages published. 64 | * 65 | * @var int number of messages 66 | */ 67 | private $pubs = 0; 68 | 69 | 70 | /** 71 | * Return the number of messages published. 72 | * 73 | * @return integer number of messages published 74 | */ 75 | public function pubsCount() 76 | { 77 | return $this->pubs; 78 | } 79 | 80 | /** 81 | * Number of reconnects to the server. 82 | * 83 | * @var int Number of reconnects 84 | */ 85 | private $reconnects = 0; 86 | 87 | 88 | /** 89 | * Return the number of reconnects to the server. 90 | * 91 | * @return integer number of reconnects 92 | */ 93 | public function reconnectsCount() 94 | { 95 | return $this->reconnects; 96 | } 97 | 98 | /** 99 | * List of available subscriptions. 100 | * 101 | * @var array list of subscriptions 102 | */ 103 | private $subscriptions = []; 104 | 105 | 106 | /** 107 | * Return the number of subscriptions available. 108 | * 109 | * @return integer number of subscription 110 | */ 111 | public function subscriptionsCount() 112 | { 113 | return count($this->subscriptions); 114 | } 115 | 116 | 117 | /** 118 | * Return subscriptions list. 119 | * 120 | * @return array list of subscription ids 121 | */ 122 | public function getSubscriptions() 123 | { 124 | return array_keys($this->subscriptions); 125 | } 126 | 127 | /** 128 | * Connection options object. 129 | * 130 | * @var ConnectionOptions|null 131 | */ 132 | private $options = null; 133 | 134 | /** 135 | * Connection timeout 136 | * 137 | * @var float 138 | */ 139 | private $timeout = null; 140 | 141 | /** 142 | * Stream File Pointer. 143 | * 144 | * @var mixed Socket file pointer 145 | */ 146 | private $streamSocket; 147 | 148 | /** 149 | * Generator object. 150 | * 151 | * @var Generator|Php71RandomGenerator 152 | */ 153 | private $randomGenerator; 154 | 155 | 156 | /** 157 | * Sets the chunck size in bytes to be processed when reading. 158 | * 159 | * @param integer $chunkSize Set byte chunk len to read when reading from wire. 160 | * 161 | * @return void 162 | */ 163 | public function setChunkSize($chunkSize) 164 | { 165 | $this->chunkSize = $chunkSize; 166 | } 167 | 168 | /** 169 | * Set Stream Timeout. 170 | * 171 | * @param float $seconds Before timeout on stream. 172 | * 173 | * @return boolean 174 | */ 175 | public function setStreamTimeout($seconds) 176 | { 177 | if ($this->isConnected() === true) { 178 | if (is_numeric($seconds) === true) { 179 | try { 180 | $timeout = number_format($seconds, 3); 181 | $seconds = floor($timeout); 182 | $microseconds = (($timeout - $seconds) * 1000); 183 | return stream_set_timeout($this->streamSocket, $seconds, $microseconds); 184 | } catch (\Exception $e) { 185 | return false; 186 | } 187 | } 188 | } 189 | 190 | return false; 191 | } 192 | 193 | /** 194 | * Returns an stream socket for this connection. 195 | * 196 | * @return resource 197 | */ 198 | public function getStreamSocket() 199 | { 200 | return $this->streamSocket; 201 | } 202 | 203 | /** 204 | * Indicates whether $response is an error response. 205 | * 206 | * @param string $response The Nats Server response. 207 | * 208 | * @return boolean 209 | */ 210 | private function isErrorResponse($response) 211 | { 212 | return substr($response, 0, 4) === '-ERR'; 213 | } 214 | 215 | 216 | /** 217 | * Checks if the client is connected to a server. 218 | * 219 | * @return boolean 220 | */ 221 | public function isConnected() 222 | { 223 | return isset($this->streamSocket); 224 | } 225 | 226 | /** 227 | * Returns an stream socket to the desired server. 228 | * 229 | * @param string $address Server url string. 230 | * @param float $timeout Number of seconds until the connect() system call should timeout. 231 | * 232 | * @throws \Exception Exception raised if connection fails. 233 | * @return resource 234 | */ 235 | private function getStream($address, $timeout, $context) 236 | { 237 | $errno = null; 238 | $errstr = null; 239 | 240 | set_error_handler( 241 | function () { 242 | return true; 243 | } 244 | ); 245 | 246 | $fp = stream_socket_client($address, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $context); 247 | restore_error_handler(); 248 | 249 | if ($fp === false) { 250 | throw Exception::forStreamSocketClientError($errstr, $errno); 251 | } 252 | 253 | $timeout = number_format($timeout, 3); 254 | $seconds = floor($timeout); 255 | $microseconds = (($timeout - $seconds) * 1000); 256 | stream_set_timeout($fp, $seconds, $microseconds); 257 | 258 | return $fp; 259 | } 260 | 261 | /** 262 | * Server information. 263 | * 264 | * @var mixed 265 | */ 266 | private $serverInfo; 267 | 268 | 269 | /** 270 | * Process information returned by the server after connection. 271 | * 272 | * @param string $connectionResponse INFO message. 273 | * 274 | * @return void 275 | */ 276 | private function processServerInfo($connectionResponse) 277 | { 278 | $this->serverInfo = new ServerInfo($connectionResponse); 279 | } 280 | 281 | /** 282 | * Returns current connected server ID. 283 | * 284 | * @return string Server ID. 285 | */ 286 | public function connectedServerID() 287 | { 288 | return $this->serverInfo->getServerID(); 289 | } 290 | 291 | /** 292 | * Constructor. 293 | * 294 | * @param ConnectionOptions $options Connection options object. 295 | */ 296 | public function __construct(ConnectionOptions $options = null) 297 | { 298 | $this->pings = 0; 299 | $this->pubs = 0; 300 | $this->subscriptions = []; 301 | $this->options = $options; 302 | if (version_compare(phpversion(), '7.0', '>') === true) { 303 | $this->randomGenerator = new Php71RandomGenerator(); 304 | } else { 305 | $randomFactory = new Factory(); 306 | $this->randomGenerator = $randomFactory->getLowStrengthGenerator(); 307 | } 308 | 309 | if ($options === null) { 310 | $this->options = new ConnectionOptions(); 311 | } 312 | } 313 | 314 | /** 315 | * Sends data thought the stream. 316 | * 317 | * @param string $payload Message data. 318 | * 319 | * @throws \Exception Raises if fails sending data. 320 | * @return void 321 | */ 322 | private function send($payload) 323 | { 324 | $msg = $payload."\r\n"; 325 | $len = strlen($msg); 326 | while (true) { 327 | $written = @fwrite($this->streamSocket, $msg); 328 | if ($written === false) { 329 | throw new \Exception('Error sending data'); 330 | } 331 | 332 | if ($written === 0) { 333 | throw new \Exception('Broken pipe or closed connection'); 334 | } 335 | 336 | $len = ($len - $written); 337 | if ($len > 0) { 338 | $msg = substr($msg, (0 - $len)); 339 | } else { 340 | break; 341 | } 342 | } 343 | 344 | if ($this->debug === true) { 345 | printf('>>>> %s', $msg); 346 | } 347 | } 348 | 349 | /** 350 | * Receives a message thought the stream. 351 | * 352 | * @param integer $len Number of bytes to receive. 353 | * 354 | * @return string 355 | */ 356 | private function receive($len = 0) 357 | { 358 | if ($len > 0) { 359 | $chunkSize = $this->chunkSize; 360 | $line = null; 361 | $receivedBytes = 0; 362 | while ($receivedBytes < $len) { 363 | $bytesLeft = ($len - $receivedBytes); 364 | if ($bytesLeft < $this->chunkSize) { 365 | $chunkSize = $bytesLeft; 366 | } 367 | 368 | $readChunk = fread($this->streamSocket, $chunkSize); 369 | $receivedBytes += strlen($readChunk); 370 | $line .= $readChunk; 371 | } 372 | } else { 373 | $line = fgets($this->streamSocket); 374 | } 375 | 376 | if ($this->debug === true) { 377 | printf('<<<< %s\r\n', $line); 378 | } 379 | 380 | return $line; 381 | } 382 | 383 | /** 384 | * Handles PING command. 385 | * 386 | * @return void 387 | */ 388 | private function handlePING() 389 | { 390 | $this->send('PONG'); 391 | } 392 | 393 | /** 394 | * Handles MSG command. 395 | * 396 | * @param string $line Message command from Nats. 397 | * 398 | * @throws Exception If subscription not found. 399 | * @return void 400 | * @codeCoverageIgnore 401 | */ 402 | private function handleMSG($line) 403 | { 404 | $parts = explode(' ', $line); 405 | $subject = null; 406 | $length = trim($parts[3]); 407 | $sid = $parts[2]; 408 | 409 | if (count($parts) === 5) { 410 | $length = trim($parts[4]); 411 | $subject = $parts[3]; 412 | } elseif (count($parts) === 4) { 413 | $length = trim($parts[3]); 414 | $subject = $parts[1]; 415 | } 416 | 417 | $payload = $this->receive($length); 418 | $msg = new Message($subject, $payload, $sid, $this); 419 | 420 | if (isset($this->subscriptions[$sid]) === false) { 421 | throw Exception::forSubscriptionNotFound($sid); 422 | } 423 | 424 | $func = $this->subscriptions[$sid]; 425 | if (is_callable($func) === true) { 426 | $func($msg); 427 | } else { 428 | throw Exception::forSubscriptionCallbackInvalid($sid); 429 | } 430 | } 431 | 432 | /** 433 | * Connect to server. 434 | * 435 | * @param float $timeout Number of seconds until the connect() system call should timeout. 436 | * 437 | * @throws \Exception Exception raised if connection fails. 438 | * @return void 439 | */ 440 | public function connect($timeout = null) 441 | { 442 | if ($timeout === null) { 443 | $timeout = intval(ini_get('default_socket_timeout')); 444 | } 445 | 446 | $this->timeout = $timeout; 447 | $this->streamSocket = $this->getStream( 448 | $this->options->getAddress(), $timeout, $this->options->getStreamContext()); 449 | $this->setStreamTimeout($timeout); 450 | 451 | $infoResponse = $this->receive(); 452 | 453 | if ($this->isErrorResponse($infoResponse) === true) { 454 | throw Exception::forFailedConnection($infoResponse); 455 | } else { 456 | $this->processServerInfo($infoResponse); 457 | if ($this->serverInfo->isTLSRequired()) { 458 | set_error_handler( 459 | function ($errno, $errstr, $errfile, $errline) { 460 | restore_error_handler(); 461 | throw Exception::forFailedConnection($errstr); 462 | }); 463 | 464 | if (!stream_socket_enable_crypto( 465 | $this->streamSocket, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT)) { 466 | throw Exception::forFailedConnection('Error negotiating crypto'); 467 | } 468 | 469 | restore_error_handler(); 470 | } 471 | } 472 | 473 | $msg = 'CONNECT '.$this->options; 474 | $this->send($msg); 475 | $this->ping(); 476 | $pingResponse = $this->receive(); 477 | 478 | if ($this->isErrorResponse($pingResponse) === true) { 479 | throw Exception::forFailedPing($pingResponse); 480 | } 481 | } 482 | 483 | /** 484 | * Sends PING message. 485 | * 486 | * @return void 487 | */ 488 | public function ping() 489 | { 490 | $msg = 'PING'; 491 | $this->send($msg); 492 | $this->pings += 1; 493 | } 494 | 495 | /** 496 | * Request does a request and executes a callback with the response. 497 | * 498 | * @param string $subject Message topic. 499 | * @param string $payload Message data. 500 | * @param \Closure $callback Closure to be executed as callback. 501 | * 502 | * @return void 503 | */ 504 | public function request($subject, $payload, \Closure $callback) 505 | { 506 | $inbox = uniqid('_INBOX.'); 507 | $sid = $this->subscribe( 508 | $inbox, 509 | $callback 510 | ); 511 | $this->unsubscribe($sid, 1); 512 | $this->publish($subject, $payload, $inbox); 513 | $this->wait(1); 514 | } 515 | 516 | /** 517 | * Subscribes to an specific event given a subject. 518 | * 519 | * @param string $subject Message topic. 520 | * @param \Closure $callback Closure to be executed as callback. 521 | * 522 | * @return string 523 | */ 524 | public function subscribe($subject, \Closure $callback) 525 | { 526 | $sid = $this->randomGenerator->generateString(16); 527 | $msg = 'SUB '.$subject.' '.$sid; 528 | $this->send($msg); 529 | $this->subscriptions[$sid] = $callback; 530 | return $sid; 531 | } 532 | 533 | /** 534 | * Subscribes to an specific event given a subject and a queue. 535 | * 536 | * @param string $subject Message topic. 537 | * @param string $queue Queue name. 538 | * @param \Closure $callback Closure to be executed as callback. 539 | * 540 | * @return string 541 | */ 542 | public function queueSubscribe($subject, $queue, \Closure $callback) 543 | { 544 | $sid = $this->randomGenerator->generateString(16); 545 | $msg = 'SUB '.$subject.' '.$queue.' '.$sid; 546 | $this->send($msg); 547 | $this->subscriptions[$sid] = $callback; 548 | return $sid; 549 | } 550 | 551 | /** 552 | * Unsubscribe from a event given a subject. 553 | * 554 | * @param string $sid Subscription ID. 555 | * @param integer $quantity Quantity of messages. 556 | * 557 | * @return void 558 | */ 559 | public function unsubscribe($sid, $quantity = null) 560 | { 561 | $msg = 'UNSUB '.$sid; 562 | if ($quantity !== null) { 563 | $msg = $msg.' '.$quantity; 564 | } 565 | 566 | $this->send($msg); 567 | if ($quantity === null) { 568 | unset($this->subscriptions[$sid]); 569 | } 570 | } 571 | 572 | /** 573 | * Publish publishes the data argument to the given subject. 574 | * 575 | * @param string $subject Message topic. 576 | * @param string $payload Message data. 577 | * @param string $inbox Message inbox. 578 | * 579 | * @throws Exception If subscription not found. 580 | * @return void 581 | * 582 | */ 583 | public function publish($subject, $payload = null, $inbox = null) 584 | { 585 | $msg = 'PUB '.$subject; 586 | if ($inbox !== null) { 587 | $msg = $msg.' '.$inbox; 588 | } 589 | 590 | $msg = $msg.' '.strlen($payload); 591 | $this->send($msg."\r\n".$payload); 592 | $this->pubs += 1; 593 | } 594 | 595 | /** 596 | * Waits for messages. 597 | * 598 | * @param integer $quantity Number of messages to wait for. 599 | * 600 | * @return Connection $connection Connection object 601 | */ 602 | public function wait($quantity = 0) 603 | { 604 | $count = 0; 605 | $info = stream_get_meta_data($this->streamSocket); 606 | while (is_resource($this->streamSocket) === true && feof($this->streamSocket) === false && empty($info['timed_out']) === true) { 607 | $line = $this->receive(); 608 | 609 | if ($line === false) { 610 | return null; 611 | } 612 | 613 | if (strpos($line, 'PING') === 0) { 614 | $this->handlePING(); 615 | } 616 | 617 | if (strpos($line, 'MSG') === 0) { 618 | $count++; 619 | $this->handleMSG($line); 620 | if (($quantity !== 0) && ($count >= $quantity)) { 621 | return $this; 622 | } 623 | } 624 | 625 | $info = stream_get_meta_data($this->streamSocket); 626 | } 627 | 628 | $this->close(); 629 | 630 | return $this; 631 | } 632 | 633 | /** 634 | * Reconnects to the server. 635 | * 636 | * @return void 637 | */ 638 | public function reconnect() 639 | { 640 | $this->reconnects += 1; 641 | $this->close(); 642 | $this->connect($this->timeout); 643 | } 644 | 645 | /** 646 | * Close will close the connection to the server. 647 | * 648 | * @return void 649 | */ 650 | public function close() 651 | { 652 | if ($this->streamSocket === null) { 653 | return; 654 | } 655 | 656 | fclose($this->streamSocket); 657 | $this->streamSocket = null; 658 | } 659 | } 660 | -------------------------------------------------------------------------------- /src/Nats/ConnectionOptions.php: -------------------------------------------------------------------------------- 1 | 115 | * use Nats\ConnectionOptions; 116 | * 117 | * $options = new ConnectionOptions([ 118 | * 'host' => '127.0.0.1', 119 | * 'port' => 4222, 120 | * 'user' => 'nats', 121 | * 'pass' => 'nats', 122 | * 'lang' => 'php', 123 | * // ... 124 | * ]); 125 | * 126 | * 127 | * @param Traversable|array $options The connection options. 128 | */ 129 | public function __construct($options = null) 130 | { 131 | //Default stream context 132 | $this->streamContext = stream_context_get_default(); 133 | 134 | if (empty($options) === false) { 135 | $this->initialize($options); 136 | } 137 | } 138 | 139 | /** 140 | * Get the URI for a server. 141 | * 142 | * @return string 143 | */ 144 | public function getAddress() 145 | { 146 | return 'tcp://'.$this->host.':'.$this->port; 147 | } 148 | 149 | 150 | /** 151 | * Get the options JSON string. 152 | * 153 | * @return string 154 | */ 155 | public function __toString() 156 | { 157 | $a = [ 158 | 'lang' => $this->lang, 159 | 'version' => $this->version, 160 | 'verbose' => $this->verbose, 161 | 'pedantic' => $this->pedantic, 162 | ]; 163 | if (empty($this->user) === false) { 164 | $a['user'] = $this->user; 165 | } 166 | 167 | if (empty($this->pass) === false) { 168 | $a['pass'] = $this->pass; 169 | } 170 | 171 | if (empty($this->token) === false) { 172 | $a['auth_token'] = $this->token; 173 | } 174 | 175 | return json_encode($a); 176 | } 177 | 178 | 179 | /** 180 | * Get host. 181 | * 182 | * @return string 183 | */ 184 | public function getHost() 185 | { 186 | return $this->host; 187 | } 188 | 189 | 190 | /** 191 | * Set host. 192 | * 193 | * @param string $host Host. 194 | * 195 | * @return $this 196 | */ 197 | public function setHost($host) 198 | { 199 | $this->host = $host; 200 | 201 | return $this; 202 | } 203 | 204 | 205 | /** 206 | * Get port. 207 | * 208 | * @return integer 209 | */ 210 | public function getPort() 211 | { 212 | return $this->port; 213 | } 214 | 215 | 216 | /** 217 | * Set port. 218 | * 219 | * @param integer $port Port. 220 | * 221 | * @return $this 222 | */ 223 | public function setPort($port) 224 | { 225 | $this->port = $port; 226 | 227 | return $this; 228 | } 229 | 230 | 231 | /** 232 | * Get user. 233 | * 234 | * @return string 235 | */ 236 | public function getUser() 237 | { 238 | return $this->user; 239 | } 240 | 241 | 242 | /** 243 | * Set user. 244 | * 245 | * @param string $user User. 246 | * 247 | * @return $this 248 | */ 249 | public function setUser($user) 250 | { 251 | $this->user = $user; 252 | 253 | return $this; 254 | } 255 | 256 | 257 | /** 258 | * Get password. 259 | * 260 | * @return string 261 | */ 262 | public function getPass() 263 | { 264 | return $this->pass; 265 | } 266 | 267 | /** 268 | * Set password. 269 | * 270 | * @param string $pass Password. 271 | * 272 | * @return $this 273 | */ 274 | public function setPass($pass) 275 | { 276 | $this->pass = $pass; 277 | 278 | return $this; 279 | } 280 | 281 | /** 282 | * Get token. 283 | * 284 | * @return string 285 | */ 286 | public function getToken() 287 | { 288 | return $this->token; 289 | } 290 | 291 | /** 292 | * Set token. 293 | * 294 | * @param string $token Token. 295 | * 296 | * @return $this 297 | */ 298 | public function setToken($token) 299 | { 300 | $this->token = $token; 301 | 302 | return $this; 303 | } 304 | 305 | /** 306 | * Get language. 307 | * 308 | * @return string 309 | */ 310 | public function getLang() 311 | { 312 | return $this->lang; 313 | } 314 | 315 | 316 | /** 317 | * Set language. 318 | * 319 | * @param string $lang Language. 320 | * 321 | * @return $this 322 | */ 323 | public function setLang($lang) 324 | { 325 | $this->lang = $lang; 326 | 327 | return $this; 328 | } 329 | 330 | 331 | /** 332 | * Get version. 333 | * 334 | * @return string 335 | */ 336 | public function getVersion() 337 | { 338 | return $this->version; 339 | } 340 | 341 | 342 | /** 343 | * Set version. 344 | * 345 | * @param string $version Version number. 346 | * 347 | * @return $this 348 | */ 349 | public function setVersion($version) 350 | { 351 | $this->version = $version; 352 | 353 | return $this; 354 | } 355 | 356 | 357 | /** 358 | * Get verbose. 359 | * 360 | * @return boolean 361 | */ 362 | public function isVerbose() 363 | { 364 | return $this->verbose; 365 | } 366 | 367 | 368 | /** 369 | * Set verbose. 370 | * 371 | * @param boolean $verbose Verbose flag. 372 | * 373 | * @return $this 374 | */ 375 | public function setVerbose($verbose) 376 | { 377 | $this->verbose = $verbose; 378 | 379 | return $this; 380 | } 381 | 382 | 383 | /** 384 | * Get pedantic. 385 | * 386 | * @return boolean 387 | */ 388 | public function isPedantic() 389 | { 390 | return $this->pedantic; 391 | } 392 | 393 | 394 | /** 395 | * Set pedantic. 396 | * 397 | * @param boolean $pedantic Pedantic flag. 398 | * 399 | * @return $this 400 | */ 401 | public function setPedantic($pedantic) 402 | { 403 | $this->pedantic = $pedantic; 404 | 405 | return $this; 406 | } 407 | 408 | 409 | /** 410 | * Get reconnect. 411 | * 412 | * @return boolean 413 | */ 414 | public function isReconnect() 415 | { 416 | return $this->reconnect; 417 | } 418 | 419 | 420 | /** 421 | * Set reconnect. 422 | * 423 | * @param boolean $reconnect Reconnect flag. 424 | * 425 | * @return $this 426 | */ 427 | public function setReconnect($reconnect) 428 | { 429 | $this->reconnect = $reconnect; 430 | 431 | return $this; 432 | } 433 | 434 | /** 435 | * Get stream context. 436 | * 437 | * @return resource 438 | */ 439 | public function getStreamContext() 440 | { 441 | return $this->streamContext; 442 | } 443 | 444 | /** 445 | * Set stream context. 446 | * 447 | * @param resource $streamContext Stream context. 448 | * 449 | * @return $this 450 | */ 451 | public function setStreamContext($streamContext) 452 | { 453 | $this->streamContext = $streamContext; 454 | 455 | return $this; 456 | } 457 | 458 | /** 459 | * Set the connection options. 460 | * 461 | * @param Traversable|array $options The connection options. 462 | * 463 | * @return void 464 | */ 465 | public function setConnectionOptions($options) 466 | { 467 | $this->initialize($options); 468 | } 469 | 470 | /** 471 | * Initialize the parameters. 472 | * 473 | * @param Traversable|array $options The connection options. 474 | * 475 | * @throws Exception When $options are an invalid type. 476 | * 477 | * @return void 478 | */ 479 | protected function initialize($options) 480 | { 481 | if (is_array($options) === false && ($options instanceof Traversable) === false) { 482 | throw new Exception('The $options argument must be either an array or Traversable'); 483 | } 484 | 485 | foreach ($options as $key => $value) { 486 | if (in_array($key, $this->configurable, true) === false) { 487 | continue; 488 | } 489 | 490 | $method = 'set'.ucfirst($key); 491 | 492 | if (method_exists($this, $method) === true) { 493 | $this->$method($value); 494 | } 495 | } 496 | } 497 | } 498 | -------------------------------------------------------------------------------- /src/Nats/EncodedConnection.php: -------------------------------------------------------------------------------- 1 | encoder = $encoder; 31 | parent::__construct($options); 32 | } 33 | 34 | /** 35 | * Publish publishes the data argument to the given subject. 36 | * 37 | * @param string $subject Message topic. 38 | * @param string $payload Message data. 39 | * @param string $inbox Message inbox. 40 | * 41 | * @return void 42 | */ 43 | public function publish($subject, $payload = null, $inbox = null) 44 | { 45 | $payload = $this->encoder->encode($payload); 46 | parent::publish($subject, $payload, $inbox); 47 | } 48 | 49 | /** 50 | * Subscribes to an specific event given a subject. 51 | * 52 | * @param string $subject Message topic. 53 | * @param \Closure $callback Closure to be executed as callback. 54 | * 55 | * @return string 56 | */ 57 | public function subscribe($subject, \Closure $callback) 58 | { 59 | $c = function ($message) use ($callback) { 60 | $message->setBody($this->encoder->decode($message->getBody())); 61 | $callback($message); 62 | }; 63 | return parent::subscribe($subject, $c); 64 | } 65 | 66 | /** 67 | * Subscribes to an specific event given a subject and a queue. 68 | * 69 | * @param string $subject Message topic. 70 | * @param string $queue Queue name. 71 | * @param \Closure $callback Closure to be executed as callback. 72 | * 73 | * @return void 74 | */ 75 | public function queueSubscribe($subject, $queue, \Closure $callback) 76 | { 77 | $c = function ($message) use ($callback) { 78 | $message->setBody($this->encoder->decode($message->getBody())); 79 | $callback($message); 80 | }; 81 | parent::queueSubscribe($subject, $queue, $c); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Nats/Encoders/Encoder.php: -------------------------------------------------------------------------------- 1 | setSubject($subject); 52 | $this->setBody($body); 53 | $this->setSid($sid); 54 | $this->setConn($conn); 55 | } 56 | 57 | 58 | /** 59 | * Set subject. 60 | * 61 | * @param string $subject Subject. 62 | * 63 | * @return $this 64 | */ 65 | public function setSubject($subject) 66 | { 67 | $this->subject = $subject; 68 | 69 | return $this; 70 | } 71 | 72 | 73 | /** 74 | * Get subject. 75 | * 76 | * @return string 77 | */ 78 | public function getSubject() 79 | { 80 | return $this->subject; 81 | } 82 | 83 | 84 | /** 85 | * Set body. 86 | * 87 | * @param string $body Body. 88 | * 89 | * @return $this 90 | */ 91 | public function setBody($body) 92 | { 93 | $this->body = $body; 94 | return $this; 95 | } 96 | 97 | 98 | /** 99 | * Get body. 100 | * 101 | * @return string 102 | */ 103 | public function getBody() 104 | { 105 | return $this->body; 106 | } 107 | 108 | 109 | /** 110 | * Set Ssid. 111 | * 112 | * @param string $sid Ssid. 113 | * 114 | * @return $this 115 | */ 116 | public function setSid($sid) 117 | { 118 | $this->sid = $sid; 119 | return $this; 120 | } 121 | 122 | 123 | /** 124 | * Get Ssid. 125 | * 126 | * @return string 127 | */ 128 | public function getSid() 129 | { 130 | return $this->sid; 131 | } 132 | 133 | 134 | /** 135 | * String representation of a message. 136 | * 137 | * @return string 138 | */ 139 | public function __toString() 140 | { 141 | return $this->getBody(); 142 | } 143 | 144 | 145 | /** 146 | * Set Conn. 147 | * 148 | * @param Connection $conn Connection. 149 | * 150 | * @return $this 151 | */ 152 | public function setConn(Connection $conn) 153 | { 154 | $this->conn = $conn; 155 | return $this; 156 | } 157 | 158 | 159 | /** 160 | * Get Conn. 161 | * 162 | * @return Connection 163 | */ 164 | public function getConn() 165 | { 166 | return $this->conn; 167 | } 168 | 169 | 170 | /** 171 | * Allows you reply the message with a specific body. 172 | * 173 | * @param string $body Body to be set. 174 | * 175 | * @return void 176 | */ 177 | public function reply($body) 178 | { 179 | $this->conn->publish( 180 | $this->subject, 181 | $body 182 | ); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/Nats/Php71RandomGenerator.php: -------------------------------------------------------------------------------- 1 | setServerID($data['server_id']); 101 | $this->setHost($data['host']); 102 | $this->setPort($data['port']); 103 | $this->setVersion($data['version']); 104 | $this->setGoVersion($data['go']); 105 | $this->setAuthRequired(isset($data['auth_required']) ? $data['auth_required'] : false); 106 | $this->setTLSRequired(isset($data['tls_required']) ? $data['tls_required'] : false); 107 | $this->setTLSVerify(isset($data['tls_verify']) ? $data['tls_verify'] : false); 108 | $this->setMaxPayload($data['max_payload']); 109 | 110 | if (version_compare($data['version'], '1.1.0') === -1) { 111 | $this->setSSLRequired($data['ssl_required']); 112 | } 113 | } 114 | 115 | /** 116 | * Get the server ID. 117 | * 118 | * @return string Server ID. 119 | */ 120 | public function getServerID() 121 | { 122 | return $this->serverID; 123 | } 124 | 125 | /** 126 | * Set the server ID 127 | * 128 | * @param string $serverID Server ID. 129 | * 130 | * @return void 131 | */ 132 | public function setServerID($serverID) 133 | { 134 | $this->serverID = $serverID; 135 | } 136 | 137 | /** 138 | * Get the server host name or ip. 139 | * 140 | * @return string Server host. 141 | */ 142 | public function getHost() 143 | { 144 | return $this->host; 145 | } 146 | 147 | /** 148 | * Set the server host name or ip. 149 | * 150 | * @param string $host Server host. 151 | * 152 | * @return void 153 | */ 154 | public function setHost($host) 155 | { 156 | $this->host = $host; 157 | } 158 | 159 | /** 160 | * Get server port number. 161 | * 162 | * @return integer Server port number. 163 | */ 164 | public function getPort() 165 | { 166 | return $this->port; 167 | } 168 | 169 | /** 170 | * Set server port number. 171 | * 172 | * @param integer $port Server port number. 173 | * 174 | * @return void 175 | */ 176 | public function setPort($port) 177 | { 178 | $this->port = $port; 179 | } 180 | 181 | /** 182 | * Get server version number. 183 | * 184 | * @return string Server version number. 185 | */ 186 | public function getVersion() 187 | { 188 | return $this->version; 189 | } 190 | 191 | /** 192 | * Set server version number. 193 | * 194 | * @param string $version Server version number. 195 | * 196 | * @return void 197 | */ 198 | public function setVersion($version) 199 | { 200 | $this->version = $version; 201 | } 202 | 203 | /** 204 | * Get the golang version number. 205 | * 206 | * @return string Go version number. 207 | */ 208 | public function getGoVersion() 209 | { 210 | return $this->goVersion; 211 | } 212 | 213 | /** 214 | * Set the golang version number. 215 | * 216 | * @param string $goVersion Go version number. 217 | * 218 | * @return void 219 | */ 220 | public function setGoVersion($goVersion) 221 | { 222 | $this->goVersion = $goVersion; 223 | } 224 | 225 | /** 226 | * Check if server requires authorization. 227 | * 228 | * @return boolean If auth is required. 229 | */ 230 | public function isAuthRequired() 231 | { 232 | return $this->authRequired; 233 | } 234 | 235 | /** 236 | * Set if the server requires authorization. 237 | * 238 | * @param boolean $authRequired If auth is required. 239 | * 240 | * @return void 241 | */ 242 | public function setAuthRequired($authRequired) 243 | { 244 | $this->authRequired = $authRequired; 245 | } 246 | 247 | /** 248 | * Check if server requires TLS. 249 | * 250 | * @return boolean If TLS is required. 251 | */ 252 | public function isTLSRequired() 253 | { 254 | return $this->TLSRequired; 255 | } 256 | 257 | /** 258 | * Set if server requires TLS. 259 | * 260 | * @param boolean $TLSRequired If TLS is required. 261 | * 262 | * @return void 263 | */ 264 | public function setTLSRequired($TLSRequired) 265 | { 266 | $this->TLSRequired = $TLSRequired; 267 | } 268 | 269 | /** 270 | * Check if TLS certificate is verified. 271 | * 272 | * @return boolean If TLS certificate is verified. 273 | */ 274 | public function isTLSVerify() 275 | { 276 | return $this->TLSVerify; 277 | } 278 | 279 | /** 280 | * Set if server verifies TLS certificate. 281 | * 282 | * @param boolean $TLSVerify If TLS certificate is verified. 283 | * 284 | * @return void 285 | */ 286 | public function setTLSVerify($TLSVerify) 287 | { 288 | $this->TLSVerify = $TLSVerify; 289 | } 290 | 291 | /** 292 | * Check if SSL is required. 293 | * 294 | * @return boolean If SSL is required. 295 | */ 296 | public function isSSLRequired() 297 | { 298 | return $this->SSLRequired; 299 | } 300 | 301 | /** 302 | * Set if SSL is required. 303 | * 304 | * @param boolean $SSLRequired If SSL is required. 305 | * 306 | * @return void 307 | */ 308 | public function setSSLRequired($SSLRequired) 309 | { 310 | $this->SSLRequired = $SSLRequired; 311 | } 312 | 313 | /** 314 | * Get the max size of the payload. 315 | * 316 | * @return integer Size in bytes. 317 | */ 318 | public function getMaxPayload() 319 | { 320 | return $this->maxPayload; 321 | } 322 | 323 | /** 324 | * Set the max size of the payload. 325 | * 326 | * @param integer $maxPayload Size in bytes. 327 | * 328 | * @return void 329 | */ 330 | public function setMaxPayload($maxPayload) 331 | { 332 | $this->maxPayload = $maxPayload; 333 | } 334 | 335 | /** 336 | * Get the server connection URLs. 337 | * 338 | * @return array List of server connection urls. 339 | */ 340 | public function getConnectURLs() 341 | { 342 | return $this->connectURLs; 343 | } 344 | 345 | /** 346 | * Set the server connection URLs. 347 | * 348 | * @param array $connectURLs List of server connection urls. 349 | * 350 | * @return void 351 | */ 352 | public function setConnectURLs(array $connectURLs) 353 | { 354 | $this->connectURLs = $connectURLs; 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /test/ConnectionOptionsTest.php: -------------------------------------------------------------------------------- 1 | setHost('host')->setPort(4222)->setUser('user')->setPass('password')->setLang('lang')->setVersion('version')->setVerbose(true)->setPedantic(true)->setReconnect(true); 22 | 23 | $this->assertEquals('host', $options->getHost()); 24 | $this->assertEquals(4222, $options->getPort()); 25 | $this->assertEquals('user', $options->getUser()); 26 | $this->assertEquals('password', $options->getPass()); 27 | $this->assertNull($options->getToken()); 28 | $this->assertEquals('lang', $options->getLang()); 29 | $this->assertEquals('version', $options->getVersion()); 30 | $this->assertTrue($options->isVerbose()); 31 | $this->assertTrue($options->isPedantic()); 32 | $this->assertTrue($options->isReconnect()); 33 | } 34 | 35 | /** 36 | * Test Connection Options getters and setters using auth token. 37 | * 38 | * @return void 39 | */ 40 | public function testAuthToken() 41 | { 42 | $options = new ConnectionOptions(); 43 | $options->setHost('host')->setPort(4222)->setToken('token')->setLang('lang')->setVersion('version')->setVerbose(true)->setPedantic(true)->setReconnect(true); 44 | 45 | $this->assertEquals('host', $options->getHost()); 46 | $this->assertEquals(4222, $options->getPort()); 47 | $this->assertEquals('token', $options->getToken()); 48 | $this->assertNull($options->getUser()); 49 | $this->assertNull($options->getPass()); 50 | $this->assertEquals('lang', $options->getLang()); 51 | $this->assertEquals('version', $options->getVersion()); 52 | $this->assertTrue($options->isVerbose()); 53 | $this->assertTrue($options->isPedantic()); 54 | $this->assertTrue($options->isReconnect()); 55 | } 56 | 57 | 58 | /** 59 | * Tests Connection Options getters and setters without setting user and password. 60 | * 61 | * @return void 62 | */ 63 | public function testSettersAndGettersWithoutCredentials() 64 | { 65 | $options = new ConnectionOptions(); 66 | $options->setHost('host')->setPort(4222)->setLang('lang')->setVersion('version')->setVerbose(true)->setPedantic(true)->setReconnect(true); 67 | 68 | $this->assertEquals('host', $options->getHost()); 69 | $this->assertEquals(4222, $options->getPort()); 70 | $this->assertNull($options->getUser()); 71 | $this->assertNull($options->getPass()); 72 | $this->assertEquals('lang', $options->getLang()); 73 | $this->assertEquals('version', $options->getVersion()); 74 | $this->assertTrue($options->isVerbose()); 75 | $this->assertTrue($options->isPedantic()); 76 | $this->assertTrue($options->isReconnect()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/ConnectionTest.php: -------------------------------------------------------------------------------- 1 | c = new Connection($options); 31 | $this->c->connect(); 32 | } 33 | 34 | 35 | /** 36 | * Test Connection. 37 | * 38 | * @return void 39 | */ 40 | public function testConnection() 41 | { 42 | // Connect. 43 | $this->c->connect(); 44 | $this->assertTrue($this->c->isConnected()); 45 | 46 | // Disconnect. 47 | $this->c->close(); 48 | $this->assertFalse($this->c->isConnected()); 49 | } 50 | 51 | 52 | /** 53 | * Test Ping command. 54 | * 55 | * @return void 56 | */ 57 | public function testPing() 58 | { 59 | $this->c->ping(); 60 | $count = $this->c->pingsCount(); 61 | $this->assertInternalType('int', $count); 62 | $this->assertGreaterThan(0, $count); 63 | $this->c->close(); 64 | } 65 | 66 | 67 | /** 68 | * Test Publish command. 69 | * 70 | * @return void 71 | */ 72 | public function testPublish() 73 | { 74 | $this->c->ping(); 75 | $this->c->publish('foo', 'bar'); 76 | $count = $this->c->pubsCount(); 77 | $this->assertInternalType('int', $count); 78 | $this->assertGreaterThan(0, $count); 79 | $this->c->close(); 80 | } 81 | 82 | 83 | /** 84 | * Test Reconnect command. 85 | * 86 | * @return void 87 | */ 88 | public function testReconnect() 89 | { 90 | $this->c->reconnect(); 91 | $count = $this->c->reconnectsCount(); 92 | $this->assertInternalType('int', $count); 93 | $this->assertGreaterThan(0, $count); 94 | $this->c->close(); 95 | } 96 | 97 | 98 | /** 99 | * Test Request command. 100 | * 101 | * @return void 102 | */ 103 | public function testRequest() 104 | { 105 | $i = 0; 106 | do { 107 | $this->c->subscribe( 108 | 'sayhello'.$i, 109 | function ($res) { 110 | $res->reply('Hello, '.$res->getBody().' !!!'); 111 | } 112 | ); 113 | 114 | $this->c->request( 115 | 'sayhello'.$i, 116 | 'McFly', 117 | function ($res) { 118 | $this->assertEquals('Hello, McFly !!!', $res->getBody()); 119 | } 120 | ); 121 | 122 | $i++; 123 | } while ($i < 100); 124 | } 125 | 126 | 127 | /** 128 | * Test Request command with large payload. 129 | * 130 | * @return void 131 | */ 132 | public function testLargeRequest() 133 | { 134 | $content = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 51200); 135 | 136 | $contentLen = strlen($content); 137 | 138 | $contentSum = md5($content); 139 | 140 | $i = 0; 141 | do { 142 | $this->c->subscribe( 143 | 'saybighello'.$i, 144 | function ($res) use ($contentLen, $contentSum) { 145 | $gotLen = strlen($res->getBody()); 146 | $gotSum = md5($res->getBody()); 147 | $this->assertEquals($contentLen, $gotLen); 148 | $this->assertEquals($contentSum, $gotSum); 149 | $res->reply($gotLen); 150 | } 151 | ); 152 | 153 | $this->c->request( 154 | 'saybighello'.$i, 155 | $content, 156 | function ($res) { 157 | } 158 | ); 159 | 160 | $i++; 161 | } while ($i < 100); 162 | } 163 | 164 | 165 | /** 166 | * Test setting a timeout on the stream 167 | * 168 | * @return void 169 | */ 170 | public function testSetStreamTimeout() 171 | { 172 | $this->c->setStreamTimeout(1); 173 | $before = time(); 174 | $this->c->request( 175 | 'nonexistantsubject', 176 | 'test', 177 | function ($res) { 178 | } 179 | ); 180 | $timeTaken = (time() - $before); 181 | 182 | $this->assertGreaterThan(0, $timeTaken); 183 | $this->assertLessThan(3, $timeTaken); 184 | 185 | $meta = stream_get_meta_data($this->c->getStreamSocket()); 186 | 187 | $this->assertTrue($meta['timed_out']); 188 | } 189 | 190 | 191 | /** 192 | * Test Unsubscribe command. 193 | * 194 | * @return void 195 | */ 196 | public function testUnsubscribe() 197 | { 198 | $sid = $this->c->subscribe( 199 | 'unsub', 200 | function ($res) { 201 | $this->assertTrue(false); 202 | } 203 | ); 204 | $this->c->unsubscribe($sid); 205 | $this->c->publish('unsub', 'bar'); 206 | 207 | $this->assertTrue(true); 208 | } 209 | 210 | 211 | /** 212 | * Test Connecting when nats server is not available 213 | * 214 | * @return void 215 | */ 216 | public function testRefusedConnection() 217 | { 218 | $this->setExpectedException(\Exception::class); 219 | 220 | $options = new ConnectionOptions(); 221 | $options->setHost('localhost'); 222 | $options->setPort(4223); 223 | 224 | $c = new Connection($options); 225 | $c->connect(); 226 | $this->assertFalse($this->c->isConnected()); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /test/EncodedConnectionTest.php: -------------------------------------------------------------------------------- 1 | c = new Connection($options, $encoder); 33 | $this->c->connect(); 34 | } 35 | 36 | 37 | /** 38 | * Test Connection. 39 | * 40 | * @return void 41 | */ 42 | public function testConnection() 43 | { 44 | // Connect. 45 | $this->c->connect(); 46 | $this->assertTrue($this->c->isConnected()); 47 | 48 | // Disconnect. 49 | $this->c->close(); 50 | $this->assertFalse($this->c->isConnected()); 51 | } 52 | 53 | /** 54 | * Test Publish command. 55 | * 56 | * @return void 57 | */ 58 | public function testPublish() 59 | { 60 | $this->c->ping(); 61 | $this->c->publish('foo', 'bar'); 62 | $count = $this->c->pubsCount(); 63 | $this->assertInternalType('int', $count); 64 | $this->assertGreaterThan(0, $count); 65 | $this->c->close(); 66 | } 67 | 68 | 69 | /** 70 | * Test Request command. 71 | * 72 | * @return void 73 | */ 74 | public function testRequest() 75 | { 76 | $this->c->subscribe( 77 | 'sayhello', 78 | function ($res) { 79 | $res->reply('Hello, '.$res->getBody().' !!!'); 80 | } 81 | ); 82 | 83 | $this->c->request( 84 | 'sayhello', 85 | 'McFly', 86 | function ($res) { 87 | $this->assertEquals('Hello, McFly !!!', $res->getBody()); 88 | } 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /test/Encoders/JSONEncoderTest.php: -------------------------------------------------------------------------------- 1 | c = new EncodedConnection($options, $encoder); 33 | $this->c->connect(); 34 | } 35 | 36 | /** 37 | * Test Request command. 38 | * 39 | * @return void 40 | */ 41 | public function testRequestArray() 42 | { 43 | $this->c->subscribe( 44 | 'sayhello', 45 | function ($res) { 46 | $res->reply('Hello, '.$res->getBody()[1].' !!!'); 47 | } 48 | ); 49 | 50 | $this->c->request( 51 | 'sayhello', 52 | [ 53 | 'foo', 54 | 'McFly', 55 | ], 56 | function ($res) { 57 | $this->assertEquals('Hello, McFly !!!', $res->getBody()); 58 | } 59 | ); 60 | 61 | $this->c->wait(1); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/Encoders/PHPEncoderTest.php: -------------------------------------------------------------------------------- 1 | c = new EncodedConnection($options, $encoder); 33 | $this->c->connect(); 34 | } 35 | 36 | /** 37 | * Test Request command. 38 | * 39 | * @return void 40 | */ 41 | public function testRequestArray() 42 | { 43 | $this->c->subscribe( 44 | 'sayhello', 45 | function ($res) { 46 | $res->reply('Hello, '.$res->getBody()[1].' !!!'); 47 | } 48 | ); 49 | 50 | $this->c->request( 51 | 'sayhello', 52 | [ 53 | 'foo', 54 | 'McFly', 55 | ], 56 | function ($res) { 57 | $this->assertEquals('Hello, McFly !!!', $res->getBody()); 58 | } 59 | ); 60 | 61 | $this->c->wait(1); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/Encoders/YAMLEncoderTest.php: -------------------------------------------------------------------------------- 1 | c = new EncodedConnection($options, $encoder); 33 | $this->c->connect(); 34 | } 35 | 36 | /** 37 | * Test Request command. 38 | * 39 | * @return void 40 | */ 41 | public function testRequestArray() 42 | { 43 | $this->markTestSkipped( 44 | 'The YAML extension is not available.' 45 | ); 46 | 47 | $this->c->subscribe( 48 | 'sayhello', 49 | function ($res) { 50 | $res->reply('Hello, '.$res->getBody()[1].' !!!'); 51 | } 52 | ); 53 | 54 | $this->c->request( 55 | 'sayhello', 56 | [ 57 | 'foo', 58 | 'McFly', 59 | ], 60 | function ($res) { 61 | $this->assertEquals('Hello, McFly !!!', $res->getBody()); 62 | } 63 | ); 64 | 65 | $this->c->wait(1); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/MessageTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('subject', $msg->getSubject()); 26 | $this->assertEquals('body', $msg->getBody()); 27 | $this->assertEquals('sid', $msg->getSid()); 28 | $this->assertEquals($conn, $msg->getConn()); 29 | 30 | $msg->setSubject('subject2')->setBody('body2')->setSid('sid2'); 31 | 32 | $this->assertEquals('subject2', $msg->getSubject()); 33 | $this->assertEquals('body2', $msg->getBody()); 34 | $this->assertEquals('sid2', $msg->getSid()); 35 | } 36 | 37 | 38 | /** 39 | * Tests Message string representation. 40 | * 41 | * @return void 42 | */ 43 | public function testMessageStringRepresentation() 44 | { 45 | $conn = new Connection(); 46 | 47 | $msg = new Message('subject', 'body', 'sid', $conn); 48 | 49 | $this->assertEquals('body', $msg->__toString()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/test.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/repejota/phpnats/bd089ea66ef41f557830449872389750dd12a99f/test/test.pdf --------------------------------------------------------------------------------