From 1e02ada63106fe72533f6aceafcbd9cdaa3ac4e1 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 20 Mar 2017 21:30:32 +0100 Subject: [PATCH 01/28] Updated to latest rx/websocket --- composer.json | 2 +- composer.lock | 204 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 165 insertions(+), 41 deletions(-) diff --git a/composer.json b/composer.json index 5e74f0a..a964a49 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "clue/block-react": "^1.1", "ocramius/package-versions": "^1.1", "reactivex/rxphp": "^1.5.2", - "rx/websocket": "^0.9.2" + "rx/websocket": "^0.10" }, "require-dev": { "api-clients/test-utilities": "^2.0" diff --git a/composer.lock b/composer.lock index 70a1a12..6dda23e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "846cdf28cea0ffeca189211094247dd8", + "content-hash": "1a3911642311d40748197f3a0a5240ee", "packages": [ { "name": "api-clients/command-bus", @@ -421,16 +421,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.3.1", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b" + "reference": "0d6c7ca039329247e4f0f8f8f6506810e8248855" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", - "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/0d6c7ca039329247e4f0f8f8f6506810e8248855", + "reference": "0d6c7ca039329247e4f0f8f8f6506810e8248855", "shasum": "" }, "require": { @@ -466,16 +466,23 @@ "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" } ], - "description": "PSR-7 message implementation", + "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ "http", "message", + "request", + "response", "stream", - "uri" + "uri", + "url" ], - "time": "2016-06-24T23:00:38+00:00" + "time": "2017-02-27T10:51:17+00:00" }, { "name": "igorw/get-in", @@ -771,6 +778,54 @@ ], "time": "2016-08-06T14:39:51+00:00" }, + { + "name": "ratchet/rfc6455", + "version": "v0.2.2", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/RFC6455.git", + "reference": "56aecde679d72d7b1087bbe1d6f7d96e123d396a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/56aecde679d72d7b1087bbe1d6f7d96e123d396a", + "reference": "56aecde679d72d7b1087bbe1d6f7d96e123d396a", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^1.0", + "php": ">=5.4.2" + }, + "require-dev": { + "react/http": "^0.4.1", + "react/socket-client": "^0.4.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ratchet\\RFC6455\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "role": "Developer" + } + ], + "description": "RFC6455 WebSocket protocol handler", + "homepage": "http://socketo.me", + "keywords": [ + "WebSockets", + "rfc6455", + "websocket" + ], + "time": "2017-01-01T15:41:18+00:00" + }, { "name": "react/cache", "version": "v0.4.1", @@ -807,23 +862,28 @@ }, { "name": "react/dns", - "version": "v0.4.3", + "version": "v0.4.6", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "751b3129556e04944f164e3556a20ca6e201e459" + "reference": "a4c32f0021c742a1781c445270cb29a2d4b76fed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/751b3129556e04944f164e3556a20ca6e201e459", - "reference": "751b3129556e04944f164e3556a20ca6e201e459", + "url": "https://api.github.com/repos/reactphp/dns/zipball/a4c32f0021c742a1781c445270cb29a2d4b76fed", + "reference": "a4c32f0021c742a1781c445270cb29a2d4b76fed", "shasum": "" }, "require": { "php": ">=5.3.0", "react/cache": "~0.4.0|~0.3.0", "react/promise": "~2.1|~1.2", - "react/socket": "~0.4.0|~0.3.0" + "react/promise-timer": "~1.1", + "react/socket": "^0.5 || ^0.4.4", + "react/stream": "^0.6 || ^0.5 || ^0.4.5" + }, + "require-dev": { + "phpunit/phpunit": "^5.0 || ^4.8.10" }, "type": "library", "autoload": { @@ -835,12 +895,12 @@ "license": [ "MIT" ], - "description": "Async DNS resolver.", + "description": "Async DNS resolver for ReactPHP", "keywords": [ "dns", "dns-resolver" ], - "time": "2016-08-01T10:09:07+00:00" + "time": "2017-03-11T13:46:09+00:00" }, { "name": "react/event-loop", @@ -888,24 +948,27 @@ }, { "name": "react/http", - "version": "v0.4.2", + "version": "v0.4.4", "source": { "type": "git", "url": "https://github.com/reactphp/http.git", - "reference": "abedac54967d7ea237ad104cff8274e2c4077cf4" + "reference": "aac319bd789cbc7b478d42cde2d03596e97e3222" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/http/zipball/abedac54967d7ea237ad104cff8274e2c4077cf4", - "reference": "abedac54967d7ea237ad104cff8274e2c4077cf4", + "url": "https://api.github.com/repos/reactphp/http/zipball/aac319bd789cbc7b478d42cde2d03596e97e3222", + "reference": "aac319bd789cbc7b478d42cde2d03596e97e3222", "shasum": "" }, "require": { - "evenement/evenement": "^2.0", - "guzzlehttp/psr7": "^1.0", - "php": ">=5.4.0", + "evenement/evenement": "^2.0 || ^1.0", + "php": ">=5.3.0", "react/socket": "^0.4", - "react/stream": "^0.4" + "react/stream": "^0.4.4", + "ringcentral/psr7": "^1.2" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.10||^5.0" }, "type": "library", "autoload": { @@ -921,20 +984,20 @@ "keywords": [ "http" ], - "time": "2016-11-09T15:20:39+00:00" + "time": "2017-02-13T14:12:50+00:00" }, { "name": "react/http-client", - "version": "v0.4.15", + "version": "v0.4.16", "source": { "type": "git", "url": "https://github.com/reactphp/http-client.git", - "reference": "01e919008363622334f91419a9908b3a51754ccd" + "reference": "307d8f9c9062c9f2fb21cde6ad13afee040cce15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/http-client/zipball/01e919008363622334f91419a9908b3a51754ccd", - "reference": "01e919008363622334f91419a9908b3a51754ccd", + "url": "https://api.github.com/repos/reactphp/http-client/zipball/307d8f9c9062c9f2fb21cde6ad13afee040cce15", + "reference": "307d8f9c9062c9f2fb21cde6ad13afee040cce15", "shasum": "" }, "require": { @@ -961,7 +1024,7 @@ "keywords": [ "http" ], - "time": "2016-12-02T10:17:42+00:00" + "time": "2017-03-01T11:07:56+00:00" }, { "name": "react/promise", @@ -1249,6 +1312,64 @@ ], "time": "2017-02-07T19:28:24+00:00" }, + { + "name": "ringcentral/psr7", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/ringcentral/psr7.git", + "reference": "2594fb47cdc659f3fcf0aa1559b7355460555303" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ringcentral/psr7/zipball/2594fb47cdc659f3fcf0aa1559b7355460555303", + "reference": "2594fb47cdc659f3fcf0aa1559b7355460555303", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "RingCentral\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "PSR-7 message implementation", + "keywords": [ + "http", + "message", + "stream", + "uri" + ], + "time": "2016-03-25T17:36:49+00:00" + }, { "name": "rx/operator-extras", "version": "0.3.0", @@ -1298,29 +1419,32 @@ }, { "name": "rx/websocket", - "version": "0.9.2", + "version": "0.10.0", "source": { "type": "git", "url": "https://github.com/RxPHP/RxWebsocket.git", - "reference": "b93125668a0f89b3ade8100f50318e12ac4bbd41" + "reference": "dce6a0540a282d015fc240b28ffa3119010ed9a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/RxPHP/RxWebsocket/zipball/b93125668a0f89b3ade8100f50318e12ac4bbd41", - "reference": "b93125668a0f89b3ade8100f50318e12ac4bbd41", + "url": "https://api.github.com/repos/RxPHP/RxWebsocket/zipball/dce6a0540a282d015fc240b28ffa3119010ed9a6", + "reference": "dce6a0540a282d015fc240b28ffa3119010ed9a6", "shasum": "" }, "require": { - "guzzlehttp/psr7": "^1.2", + "ratchet/rfc6455": "^0.2.2", "react/http": "^0.4.1", - "react/http-client": "^0.4.8", - "reactivex/rxphp": "^1.0", - "voryx/event-loop": "^0.2.0" + "react/http-client": "^0.4.10", + "reactivex/rxphp": "^1.5.1", + "voryx/event-loop": "^0.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.0" }, "type": "library", "autoload": { "psr-4": { - "Rx\\Websocket\\": "src/Websocket/" + "Rx\\Websocket\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1350,11 +1474,11 @@ "rxphp", "websocket" ], - "time": "2016-02-09T03:27:41+00:00" + "time": "2017-03-18T19:55:30+00:00" }, { "name": "voryx/event-loop", - "version": "0.2.0", + "version": "0.2.1", "source": { "type": "git", "url": "https://github.com/voryx/event-loop.git", From 23dbdbc69e44ec5600c22fcbd9674a7264ff1b84 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 28 Mar 2017 22:50:16 +0200 Subject: [PATCH 02/28] Updated to RxPHP 2.0 --- composer.json | 7 +- composer.lock | 385 ++++++++++-------- examples/reddit-async.php | 23 +- examples/reddit-single.php | 2 +- examples/reddit.php | 2 +- src/AsyncClient.php | 61 ++- src/Client.php | 18 +- .../Handler/SharedAppClientHandler.php | 3 +- src/Service/SharedAppClientService.php | 7 +- tests/AsyncClientTest.php | 8 +- .../Handler/SharedAppClientHandlerTest.php | 4 +- tests/Service/SharedAppClientServiceTest.php | 10 +- tests/bootstrap.php | 2 +- 13 files changed, 300 insertions(+), 232 deletions(-) diff --git a/composer.json b/composer.json index a964a49..ee9502c 100644 --- a/composer.json +++ b/composer.json @@ -13,12 +13,11 @@ "require": { "php": "^7.0", "api-clients/command-bus": "^2.0", - "api-clients/rx-operators": "dev-master", - "api-clients/service": "dev-master", + "api-clients/rx-operators": "^2.0", "clue/block-react": "^1.1", "ocramius/package-versions": "^1.1", - "reactivex/rxphp": "^1.5.2", - "rx/websocket": "^0.10" + "reactivex/rxphp": "^2.0", + "rx/websocket": "^1.0" }, "require-dev": { "api-clients/test-utilities": "^2.0" diff --git a/composer.lock b/composer.lock index 6dda23e..f375a65 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "1a3911642311d40748197f3a0a5240ee", + "content-hash": "2c061a6feaf1adc6cb1d87f80cb90cc0", "packages": [ { "name": "api-clients/command-bus", @@ -56,25 +56,25 @@ }, { "name": "api-clients/rx", - "version": "dev-master", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/php-api-clients/rx.git", - "reference": "a1d797606e1367a51055634eb73cba20112a99ed" + "reference": "baadcfcc2f088423bce8ee66f1d46cdeac1192b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-api-clients/rx/zipball/a1d797606e1367a51055634eb73cba20112a99ed", - "reference": "a1d797606e1367a51055634eb73cba20112a99ed", + "url": "https://api.github.com/repos/php-api-clients/rx/zipball/baadcfcc2f088423bce8ee66f1d46cdeac1192b6", + "reference": "baadcfcc2f088423bce8ee66f1d46cdeac1192b6", "shasum": "" }, "require": { "php": "^7.0", "react/promise": "^2.4", - "reactivex/rxphp": "^1.5" + "reactivex/rxphp": "^2.0" }, "require-dev": { - "api-clients/test-utilities": "^2.0" + "api-clients/test-utilities": "^3.0.1" }, "type": "library", "autoload": { @@ -82,7 +82,8 @@ "ApiClients\\Tools\\Rx\\": "src/" }, "files": [ - "src/functions_include.php" + "src/functions_include.php", + "src/bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -95,29 +96,29 @@ "email": "ceesjank@gmail.com" } ], - "time": "2017-01-09 14:39:21" + "time": "2017-03-22T20:37:41+00:00" }, { "name": "api-clients/rx-operators", - "version": "dev-master", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/php-api-clients/rx-operators.git", - "reference": "d58e1744c5a4540a1d2ec69f08d4ea5627eb1cf6" + "reference": "974e38c8ce2457567d9a36f3430c524b594417e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-api-clients/rx-operators/zipball/d58e1744c5a4540a1d2ec69f08d4ea5627eb1cf6", - "reference": "d58e1744c5a4540a1d2ec69f08d4ea5627eb1cf6", + "url": "https://api.github.com/repos/php-api-clients/rx-operators/zipball/974e38c8ce2457567d9a36f3430c524b594417e7", + "reference": "974e38c8ce2457567d9a36f3430c524b594417e7", "shasum": "" }, "require": { - "api-clients/rx": "dev-master", + "api-clients/rx": "^2.0", "php": "^7.0", - "rx/operator-extras": "^0.3.0" + "rx/operator-extras": "^2.0" }, "require-dev": { - "api-clients/test-utilities": "dev-master" + "api-clients/test-utilities": "^3.0.1" }, "type": "library", "autoload": { @@ -135,46 +136,7 @@ "email": "ceesjank@gmail.com" } ], - "time": "2017-01-06 06:48:42" - }, - { - "name": "api-clients/service", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/php-api-clients/service.git", - "reference": "535eb87807fd23996153a84cf864dd1c461de6bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-api-clients/service/zipball/535eb87807fd23996153a84cf864dd1c461de6bd", - "reference": "535eb87807fd23996153a84cf864dd1c461de6bd", - "shasum": "" - }, - "require": { - "php": "^7.0", - "react/promise": "^2.4" - }, - "require-dev": { - "api-clients/test-utilities": "dev-master" - }, - "type": "library", - "autoload": { - "psr-4": { - "ApiClients\\Foundation\\Service\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Cees-Jan Kiewiet", - "email": "ceesjank@gmail.com" - } - ], - "time": "2016-12-16 22:34:48" + "time": "2017-03-23T07:06:10+00:00" }, { "name": "clue/block-react", @@ -226,18 +188,21 @@ }, { "name": "container-interop/container-interop", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/container-interop/container-interop.git", - "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", - "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", "shasum": "" }, + "require": { + "psr/container": "^1.0" + }, "type": "library", "autoload": { "psr-4": { @@ -249,20 +214,21 @@ "MIT" ], "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", - "time": "2014-12-30T15:22:37+00:00" + "homepage": "https://github.com/container-interop/container-interop", + "time": "2017-02-14T19:40:03+00:00" }, { "name": "doctrine/annotations", - "version": "v1.3.1", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "bd4461328621bde0ae6b1b2675fbc6aca4ceb558" + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/bd4461328621bde0ae6b1b2675fbc6aca4ceb558", - "reference": "bd4461328621bde0ae6b1b2675fbc6aca4ceb558", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", "shasum": "" }, "require": { @@ -271,7 +237,7 @@ }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "^5.6.1" + "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { @@ -317,7 +283,7 @@ "docblock", "parser" ], - "time": "2016-12-30T15:59:45+00:00" + "time": "2017-02-24T16:22:25+00:00" }, { "name": "doctrine/lexer", @@ -421,16 +387,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.4.1", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "0d6c7ca039329247e4f0f8f8f6506810e8248855" + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/0d6c7ca039329247e4f0f8f8f6506810e8248855", - "reference": "0d6c7ca039329247e4f0f8f8f6506810e8248855", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", "shasum": "" }, "require": { @@ -482,7 +448,7 @@ "uri", "url" ], - "time": "2017-02-27T10:51:17+00:00" + "time": "2017-03-20T17:10:46+00:00" }, { "name": "igorw/get-in", @@ -728,6 +694,55 @@ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "time": "2016-12-30T09:49:15+00:00" }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -988,16 +1003,16 @@ }, { "name": "react/http-client", - "version": "v0.4.16", + "version": "v0.4.17", "source": { "type": "git", "url": "https://github.com/reactphp/http-client.git", - "reference": "307d8f9c9062c9f2fb21cde6ad13afee040cce15" + "reference": "75ee8a113f156834aaabfe0055e8db531cb4892c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/http-client/zipball/307d8f9c9062c9f2fb21cde6ad13afee040cce15", - "reference": "307d8f9c9062c9f2fb21cde6ad13afee040cce15", + "url": "https://api.github.com/repos/reactphp/http-client/zipball/75ee8a113f156834aaabfe0055e8db531cb4892c", + "reference": "75ee8a113f156834aaabfe0055e8db531cb4892c", "shasum": "" }, "require": { @@ -1010,6 +1025,9 @@ "react/socket-client": "^0.5 || ^0.4 || ^0.3", "react/stream": "0.4.*" }, + "require-dev": { + "phpunit/phpunit": "^5.0 || ^4.8.10" + }, "type": "library", "autoload": { "psr-4": { @@ -1024,25 +1042,28 @@ "keywords": [ "http" ], - "time": "2017-03-01T11:07:56+00:00" + "time": "2017-03-20T09:55:48+00:00" }, { "name": "react/promise", - "version": "v2.5.0", + "version": "v2.5.1", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "2760f3898b7e931aa71153852dcd48a75c9b95db" + "reference": "62785ae604c8d69725d693eb370e1d67e94c4053" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/2760f3898b7e931aa71153852dcd48a75c9b95db", - "reference": "2760f3898b7e931aa71153852dcd48a75c9b95db", + "url": "https://api.github.com/repos/reactphp/promise/zipball/62785ae604c8d69725d693eb370e1d67e94c4053", + "reference": "62785ae604c8d69725d693eb370e1d67e94c4053", "shasum": "" }, "require": { "php": ">=5.4.0" }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, "type": "library", "autoload": { "psr-4": { @@ -1067,7 +1088,7 @@ "promise", "promises" ], - "time": "2016-12-22T14:09:01+00:00" + "time": "2017-03-25T12:08:31+00:00" }, { "name": "react/promise-timer", @@ -1248,41 +1269,40 @@ }, { "name": "reactivex/rxphp", - "version": "1.5.2", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/ReactiveX/RxPHP.git", - "reference": "84bfbd05e79d924b3d2597eaeef6156475e2a514" + "reference": "4ad811032428074485d7ca99b779aec54b242982" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ReactiveX/RxPHP/zipball/84bfbd05e79d924b3d2597eaeef6156475e2a514", - "reference": "84bfbd05e79d924b3d2597eaeef6156475e2a514", + "url": "https://api.github.com/repos/ReactiveX/RxPHP/zipball/4ad811032428074485d7ca99b779aec54b242982", + "reference": "4ad811032428074485d7ca99b779aec54b242982", "shasum": "" }, "require": { - "php": "~5.5|~7.0" + "php": "~7.0", + "react/promise": "~2.2" }, "require-dev": { "phpunit/phpcov": "^3.1", "phpunit/phpunit": "^5.5", - "react/event-loop": "~0.4.1", - "react/promise": "~2.2", + "react/event-loop": "^0.4.2", "satooshi/php-coveralls": "~1.0" }, "suggest": { - "react/event-loop": "For using event-loop based scheduling.", - "react/promise": "For converting promises to and from observables." + "react/event-loop": "Used for scheduling async operations" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "2.0-dev" } }, "autoload": { "psr-4": { - "Rx\\": "lib/Rx" + "Rx\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1310,7 +1330,7 @@ "reactive", "rx" ], - "time": "2017-02-07T19:28:24+00:00" + "time": "2017-03-21T13:29:22+00:00" }, { "name": "ringcentral/psr7", @@ -1372,25 +1392,26 @@ }, { "name": "rx/operator-extras", - "version": "0.3.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/RxPHP/RxOperatorExtras.git", - "reference": "851febebd3558350c55dcfecb8fd7df05b6dd88d" + "reference": "3ccbd8dfb414ffe2c86199a98361903d383b9072" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/RxPHP/RxOperatorExtras/zipball/851febebd3558350c55dcfecb8fd7df05b6dd88d", - "reference": "851febebd3558350c55dcfecb8fd7df05b6dd88d", + "url": "https://api.github.com/repos/RxPHP/RxOperatorExtras/zipball/3ccbd8dfb414ffe2c86199a98361903d383b9072", + "reference": "3ccbd8dfb414ffe2c86199a98361903d383b9072", "shasum": "" }, "require": { - "reactivex/rxphp": "^1.0.0" + "php": "^7.0", + "reactivex/rxphp": "^2.0" }, "type": "library", "autoload": { "psr-4": { - "Rx\\Extra\\": "src/" + "Rx\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1415,31 +1436,31 @@ "rx.php", "rxphp" ], - "time": "2016-05-21T02:51:35+00:00" + "time": "2017-03-21T17:25:58+00:00" }, { "name": "rx/websocket", - "version": "0.10.0", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/RxPHP/RxWebsocket.git", - "reference": "dce6a0540a282d015fc240b28ffa3119010ed9a6" + "reference": "9e8e40f5356105965adb47731823a43edabd9069" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/RxPHP/RxWebsocket/zipball/dce6a0540a282d015fc240b28ffa3119010ed9a6", - "reference": "dce6a0540a282d015fc240b28ffa3119010ed9a6", + "url": "https://api.github.com/repos/RxPHP/RxWebsocket/zipball/9e8e40f5356105965adb47731823a43edabd9069", + "reference": "9e8e40f5356105965adb47731823a43edabd9069", "shasum": "" }, "require": { "ratchet/rfc6455": "^0.2.2", "react/http": "^0.4.1", "react/http-client": "^0.4.10", - "reactivex/rxphp": "^1.5.1", - "voryx/event-loop": "^0.2.1" + "reactivex/rxphp": "^2.0", + "voryx/event-loop": "^2.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.0" + "phpunit/phpunit": "~5.7.0" }, "type": "library", "autoload": { @@ -1463,7 +1484,7 @@ "role": "Developer" } ], - "description": "Async Websockets for PHP using Rx", + "description": "Websockets for PHP using Rx", "keywords": [ "WebSockets", "react", @@ -1474,24 +1495,24 @@ "rxphp", "websocket" ], - "time": "2017-03-18T19:55:30+00:00" + "time": "2017-03-21T17:41:42+00:00" }, { "name": "voryx/event-loop", - "version": "0.2.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/voryx/event-loop.git", - "reference": "8ef08db3d7646fe60d1791ba9625ce65adf935cd" + "reference": "4cd6bc28465303bf6da63202201463e420d4ec79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voryx/event-loop/zipball/8ef08db3d7646fe60d1791ba9625ce65adf935cd", - "reference": "8ef08db3d7646fe60d1791ba9625ce65adf935cd", + "url": "https://api.github.com/repos/voryx/event-loop/zipball/4cd6bc28465303bf6da63202201463e420d4ec79", + "reference": "4cd6bc28465303bf6da63202201463e420d4ec79", "shasum": "" }, "require": { - "react/event-loop": "~0.4.1" + "react/event-loop": "^0.4.2" }, "type": "library", "autoload": { @@ -1499,7 +1520,8 @@ "EventLoop\\": "src" }, "files": [ - "src/functions.php" + "src/functions.php", + "src/bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1526,7 +1548,7 @@ "static", "timer" ], - "time": "2015-11-23T22:10:36+00:00" + "time": "2017-03-14T01:10:32+00:00" }, { "name": "wyrihaximus/tactician-command-handler-mapper", @@ -1895,16 +1917,16 @@ }, { "name": "phake/phake", - "version": "v2.3.0", + "version": "v2.3.2", "source": { "type": "git", "url": "https://github.com/mlively/Phake.git", - "reference": "2202d361d34f2712dcc257afdedc6c30060dc9bd" + "reference": "d5832f1a0dd2370e14d38bcbaeb6770e8546cff2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mlively/Phake/zipball/2202d361d34f2712dcc257afdedc6c30060dc9bd", - "reference": "2202d361d34f2712dcc257afdedc6c30060dc9bd", + "url": "https://api.github.com/repos/mlively/Phake/zipball/d5832f1a0dd2370e14d38bcbaeb6770e8546cff2", + "reference": "d5832f1a0dd2370e14d38bcbaeb6770e8546cff2", "shasum": "" }, "require": { @@ -1949,7 +1971,7 @@ "mock", "testing" ], - "time": "2016-03-04T18:49:33+00:00" + "time": "2017-03-20T05:16:34+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -2099,27 +2121,27 @@ }, { "name": "phpspec/prophecy", - "version": "v1.6.2", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb" + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", - "sebastian/comparator": "^1.1", - "sebastian/recursion-context": "^1.0|^2.0" + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { - "phpspec/phpspec": "^2.0", + "phpspec/phpspec": "^2.5|^3.2", "phpunit/phpunit": "^4.8 || ^5.6.5" }, "type": "library", @@ -2158,27 +2180,29 @@ "spy", "stub" ], - "time": "2016-11-21T14:58:47+00:00" + "time": "2017-03-02T20:05:34+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "5.0.0", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "e7d7a4acca58e45bdfd00221563d131cfb04ba96" + "reference": "4e99e1c4f9b05cbf4d6e84b100b3ff4107cf8cd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e7d7a4acca58e45bdfd00221563d131cfb04ba96", - "reference": "e7d7a4acca58e45bdfd00221563d131cfb04ba96", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4e99e1c4f9b05cbf4d6e84b100b3ff4107cf8cd1", + "reference": "4e99e1c4f9b05cbf4d6e84b100b3ff4107cf8cd1", "shasum": "" }, "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", "php": "^7.0", "phpunit/php-file-iterator": "^1.3", "phpunit/php-text-template": "^1.2", - "phpunit/php-token-stream": "^1.4.2", + "phpunit/php-token-stream": "^1.4.11 || ^2.0", "sebastian/code-unit-reverse-lookup": "^1.0", "sebastian/environment": "^2.0", "sebastian/version": "^2.0" @@ -2188,8 +2212,7 @@ "phpunit/phpunit": "^6.0" }, "suggest": { - "ext-dom": "*", - "ext-xmlwriter": "*" + "ext-xdebug": "^2.5.1" }, "type": "library", "extra": { @@ -2220,7 +2243,7 @@ "testing", "xunit" ], - "time": "2017-02-02T10:35:41+00:00" + "time": "2017-03-06T14:22:16+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2312,25 +2335,30 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.8", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4|~5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -2352,20 +2380,20 @@ "keywords": [ "timer" ], - "time": "2016-05-12T18:03:57+00:00" + "time": "2017-02-26T11:10:40+00:00" }, { "name": "phpunit/php-token-stream", - "version": "1.4.9", + "version": "1.4.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b" + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", "shasum": "" }, "require": { @@ -2401,20 +2429,20 @@ "keywords": [ "tokenizer" ], - "time": "2016-11-15T14:06:22+00:00" + "time": "2017-02-27T10:12:30+00:00" }, { "name": "phpunit/phpunit", - "version": "6.0.5", + "version": "6.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1fb01cbd5aa7fa2457cc10170070676a9ad315d2" + "reference": "47ee3fa1bca5c50f1d25105201eb20df777bd7b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1fb01cbd5aa7fa2457cc10170070676a9ad315d2", - "reference": "1fb01cbd5aa7fa2457cc10170070676a9ad315d2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/47ee3fa1bca5c50f1d25105201eb20df777bd7b6", + "reference": "47ee3fa1bca5c50f1d25105201eb20df777bd7b6", "shasum": "" }, "require": { @@ -2431,12 +2459,12 @@ "phpunit/php-text-template": "^1.2", "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "^4.0", - "sebastian/comparator": "^1.2.4", + "sebastian/comparator": "^1.2.4 || ^2.0", "sebastian/diff": "^1.2", "sebastian/environment": "^2.0", - "sebastian/exporter": "^2.0", - "sebastian/global-state": "^1.1", - "sebastian/object-enumerator": "^2.0", + "sebastian/exporter": "^2.0 || ^3.0", + "sebastian/global-state": "^1.1 || ^2.0", + "sebastian/object-enumerator": "^2.0 || ^3.0", "sebastian/resource-operations": "^1.0", "sebastian/version": "^2.0" }, @@ -2483,7 +2511,7 @@ "testing", "xunit" ], - "time": "2017-02-05T15:33:43+00:00" + "time": "2017-03-02T15:24:03+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -2546,23 +2574,23 @@ }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe" + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe", - "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", "shasum": "" }, "require": { - "php": ">=5.6" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~5" + "phpunit/phpunit": "^5.7 || ^6.0" }, "type": "library", "extra": { @@ -2587,7 +2615,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2016-02-13T06:45:14+00:00" + "time": "2017-03-04T06:30:41+00:00" }, { "name": "sebastian/comparator", @@ -2875,16 +2903,16 @@ }, { "name": "sebastian/object-enumerator", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35" + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", - "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", "shasum": "" }, "require": { @@ -2917,7 +2945,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2016-11-19T07:35:10+00:00" + "time": "2017-02-18T15:18:39+00:00" }, { "name": "sebastian/recursion-context", @@ -3059,16 +3087,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "2.8.0", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "86dd55a522238211f9f3631e3361703578941d9a" + "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/86dd55a522238211f9f3631e3361703578941d9a", - "reference": "86dd55a522238211f9f3631e3361703578941d9a", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", + "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", "shasum": "" }, "require": { @@ -3133,7 +3161,7 @@ "phpcs", "standards" ], - "time": "2017-02-02T03:30:00+00:00" + "time": "2017-03-01T22:17:45+00:00" }, { "name": "webmozart/assert", @@ -3188,10 +3216,7 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": { - "api-clients/rx-operators": 20, - "api-clients/service": 20 - }, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/examples/reddit-async.php b/examples/reddit-async.php index b858387..b02a300 100644 --- a/examples/reddit-async.php +++ b/examples/reddit-async.php @@ -1,9 +1,8 @@ channel($subReddit)->subscribe(new CallbackObserver(function (Event $event) { - echo 'Channel: ', $event->getChannel(), PHP_EOL; - echo 'Event: ', $event->getEvent(), PHP_EOL; - echo 'Data: ', json_encode($event->getData()), PHP_EOL; - })); + $client->channel($subReddit)->subscribe( + function (Event $event) { + echo 'Channel: ', $event->getChannel(), PHP_EOL; + echo 'Event: ', $event->getEvent(), PHP_EOL; + echo 'Data: ', json_encode($event->getData()), PHP_EOL; + }, + function ($e) { + echo (string)$e; + }, + function () { + echo 'Done!', PHP_EOL; + } + ); } $loop->run(); diff --git a/examples/reddit-single.php b/examples/reddit-single.php index 9a9cd57..b970833 100644 --- a/examples/reddit-single.php +++ b/examples/reddit-single.php @@ -9,7 +9,7 @@ * The App ID isn't a secret and comes from a Pusher blog post: * @link https://blog.pusher.com/pusher-realtime-reddit-api/ */ -$client = new Client(require 'reddit.key.php'); +$client = Client::create(require 'reddit.key.php'); $client->channel((string) $argv[1], function (Event $event) { echo 'Channel: ', $event->getChannel(), PHP_EOL; diff --git a/examples/reddit.php b/examples/reddit.php index 36d0ac0..6b94516 100644 --- a/examples/reddit.php +++ b/examples/reddit.php @@ -9,7 +9,7 @@ * The App ID isn't a secret and comes from a Pusher blog post: * @link https://blog.pusher.com/pusher-realtime-reddit-api/ */ -$client = new Client(require 'reddit.key.php'); +$client = Client::create(require 'reddit.key.php'); $subReddits = $argv; array_shift($subReddits); diff --git a/src/AsyncClient.php b/src/AsyncClient.php index 9155b0c..b55ca12 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -2,17 +2,15 @@ namespace ApiClients\Client\Pusher; +use React\Dns\Resolver\Resolver; use React\EventLoop\LoopInterface; use Rx\Disposable\CallbackDisposable; use Rx\Observable; -use Rx\ObservableInterface; use Rx\ObserverInterface; -use Rx\SchedulerInterface; +use Rx\Scheduler; use Rx\Websocket\Client as WebsocketClient; use Rx\Websocket\MessageSubject; -use function React\Promise\resolve; -use function EventLoop\getLoop; -use function EventLoop\setLoop; +use Throwable; final class AsyncClient { @@ -34,20 +32,46 @@ final class AsyncClient /** * @param LoopInterface $loop * @param string $app Application ID + * @param Resolver $resolver Optional DNS resolver + * @return AsyncClient */ - public function __construct(LoopInterface $loop, string $app) + public static function create(LoopInterface $loop, string $app, Resolver $resolver = null): AsyncClient { - // Set loop into global look accessor - setLoop($loop); + try { + Scheduler::setAsyncFactory(function () use ($loop) { + return new Scheduler\EventLoopScheduler($loop); + }); + } catch (Throwable $t) { + } + return new self( + new WebsocketClient( + ApiSettings::createUrl($app), + false, + [], + $loop, + $resolver + ) + ); + } + + /** + * @internal + */ + public function __construct(WebsocketClient $client) + { //Only create one connection and share the most recent among all subscriber - $this->client = (new WebsocketClient(ApiSettings::createUrl($app)))->shareReplay(1); + $this->client = $client->retryWhen(function (Observable $errors) { + return $this->handleLowLevelError($errors); + })->shareReplay(1); $this->messages = $this->client ->flatMap(function (MessageSubject $ms) { + //var_export($ms); return $ms; }) ->_ApiClients_jsonDecode() ->map(function (array $message) { + //var_export($message); return Event::createFromMessage($message); }); } @@ -69,8 +93,7 @@ public function channel(string $channel): Observable }); $events = Observable::create(function ( - ObserverInterface $observer, - SchedulerInterface $scheduler + ObserverInterface $observer ) use ( $channel, $channelMessages @@ -79,13 +102,14 @@ public function channel(string $channel): Observable ->filter(function (Event $event) { return $event->getEvent() !== 'pusher_internal:subscription_succeeded'; }) - ->subscribe($observer, $scheduler); + ->subscribe($observer); $this->send(['event' => 'pusher:subscribe', 'data' => ['channel' => $channel]]); return new CallbackDisposable(function () use ($channel, $subscription) { $this->send(['event' => 'pusher:unsubscribe', 'data' => ['channel' => $channel]]); $subscription->dispose(); + unset($this->channels[$channel]); }); }); @@ -102,8 +126,19 @@ public function send(array $message) { $this->client ->take(1) - ->subscribeCallback(function (MessageSubject $ms) use ($message) { + ->subscribe(function (MessageSubject $ms) use ($message) { $ms->send(json_encode($message)); }); } + + private function handleLowLevelError(Observable $errors) + { + $stream = $errors->subscribe( + function (Throwable $throwable) use (&$stream) { + echo (string)$throwable, PHP_EOL; + } + ); + echo __LINE__, ': ', time(), PHP_EOL; + return $errors->delay(200); + } } diff --git a/src/Client.php b/src/Client.php index c4b9936..4785f0f 100644 --- a/src/Client.php +++ b/src/Client.php @@ -5,7 +5,7 @@ use React\EventLoop\Factory; use React\EventLoop\LoopInterface; use React\Promise\Deferred; -use Rx\Observer\CallbackObserver; +use Throwable; use function Clue\React\Block\await; use function React\Promise\all; @@ -27,7 +27,7 @@ final class Client public function __construct(string $app) { $this->loop = Factory::create(); - $this->client = new AsyncClient($this->loop, $app); + $this->client = AsyncClient::create($this->loop, $app); } /** @@ -54,13 +54,13 @@ public function channels(array $channels, callable $listener) foreach ($channels as $channel) { $deferred = new Deferred(); $this->client->channel($channel)->subscribe( - new CallbackObserver( - $listener, - null, - function () use ($deferred) { - $deferred->resolve(); - } - ) + $listener, + function (Throwable $throwable) { + throw $throwable; + }, + function () use ($deferred) { + $deferred->resolve(); + } ); $promises[] = $deferred->promise(); } diff --git a/src/CommandBus/Handler/SharedAppClientHandler.php b/src/CommandBus/Handler/SharedAppClientHandler.php index 7cfb481..19263e5 100644 --- a/src/CommandBus/Handler/SharedAppClientHandler.php +++ b/src/CommandBus/Handler/SharedAppClientHandler.php @@ -6,7 +6,6 @@ use ApiClients\Client\Pusher\Service\SharedAppClientService; use React\Promise\PromiseInterface; use function React\Promise\resolve; -use function WyriHaximus\React\futureFunctionPromise; final class SharedAppClientHandler { @@ -30,6 +29,6 @@ public function __construct(SharedAppClientService $service) */ public function handle(SharedAppClientCommand $command): PromiseInterface { - return resolve($this->service->handle($command->getAppId())); + return resolve($this->service->share($command->getAppId())); } } diff --git a/src/Service/SharedAppClientService.php b/src/Service/SharedAppClientService.php index 8361923..497525a 100644 --- a/src/Service/SharedAppClientService.php +++ b/src/Service/SharedAppClientService.php @@ -7,9 +7,8 @@ use React\EventLoop\LoopInterface; use React\Promise\CancellablePromiseInterface; use function React\Promise\resolve; -use function WyriHaximus\React\futureFunctionPromise; -final class SharedAppClientService implements ServiceInterface +final class SharedAppClientService { /** * @var LoopInterface @@ -34,13 +33,13 @@ public function __construct(LoopInterface $loop) * @param string|null $appId * @return CancellablePromiseInterface */ - public function handle(string $appId = null): CancellablePromiseInterface + public function share(string $appId): CancellablePromiseInterface { if (isset($this->apps[$appId])) { return resolve($this->apps[$appId]); } - $this->apps[$appId] = new AsyncClient($this->loop, $appId); + $this->apps[$appId] = AsyncClient::create($this->loop, $appId); return resolve($this->apps[$appId]); } } diff --git a/tests/AsyncClientTest.php b/tests/AsyncClientTest.php index ece1949..7542acb 100644 --- a/tests/AsyncClientTest.php +++ b/tests/AsyncClientTest.php @@ -3,12 +3,16 @@ namespace ApiClients\Tests\Client\Pusher; +use ApiClients\Client\Pusher\AsyncClient; use ApiClients\Tools\TestUtilities\TestCase; +use React\EventLoop\Factory; final class AsyncClientTest extends TestCase { - public function testTrue() + public function testCreateFactory() { - self::assertTrue(true); + $loop = Factory::create(); + $appId = uniqid('app-id-', true); + self::assertInstanceOf(AsyncClient::class, AsyncClient::create($loop, $appId)); } } diff --git a/tests/CommandBus/Handler/SharedAppClientHandlerTest.php b/tests/CommandBus/Handler/SharedAppClientHandlerTest.php index d337212..634cb38 100644 --- a/tests/CommandBus/Handler/SharedAppClientHandlerTest.php +++ b/tests/CommandBus/Handler/SharedAppClientHandlerTest.php @@ -7,8 +7,8 @@ use ApiClients\Client\Pusher\CommandBus\Handler\SharedAppClientHandler; use ApiClients\Client\Pusher\Service\SharedAppClientService; use ApiClients\Tools\TestUtilities\TestCase; -use function EventLoop\getLoop; use function Clue\React\Block\await; +use function EventLoop\getLoop; final class SharedAppClientHandlerTest extends TestCase { @@ -19,7 +19,7 @@ public function testHandle() $handler = new SharedAppClientHandler(new SharedAppClientService($loop)); $app = await($handler->handle(new SharedAppClientCommand($appId)), $loop); - self::assertInstanceOf(AsyncClient::class, await($handler->handle(new SharedAppClientCommand($appId)), $loop)); + self::assertInstanceOf(AsyncClient::class, $app); self::assertSame($app, await($handler->handle(new SharedAppClientCommand($appId)), $loop)); self::assertNotSame($app, await($handler->handle(new SharedAppClientCommand(md5($appId))), $loop)); } diff --git a/tests/Service/SharedAppClientServiceTest.php b/tests/Service/SharedAppClientServiceTest.php index 4b46480..4ecd04a 100644 --- a/tests/Service/SharedAppClientServiceTest.php +++ b/tests/Service/SharedAppClientServiceTest.php @@ -5,8 +5,8 @@ use ApiClients\Client\Pusher\AsyncClient; use ApiClients\Client\Pusher\Service\SharedAppClientService; use ApiClients\Tools\TestUtilities\TestCase; -use function EventLoop\getLoop; use function Clue\React\Block\await; +use function EventLoop\getLoop; final class SharedAppClientServiceTest extends TestCase { @@ -16,9 +16,9 @@ public function testHandle() $appId = uniqid('app-id-', true); $handler = new SharedAppClientService($loop); - $app = await($handler->handle($appId), $loop); - self::assertInstanceOf(AsyncClient::class, await($handler->handle($appId), $loop)); - self::assertSame($app, await($handler->handle($appId), $loop)); - self::assertNotSame($app, await($handler->handle(md5($appId)), $loop)); + $app = await($handler->share($appId), $loop); + self::assertInstanceOf(AsyncClient::class, $app); + self::assertSame($app, await($handler->share($appId), $loop)); + self::assertNotSame($app, await($handler->share(md5($appId)), $loop)); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 6de025e..0bbcaa8 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,7 +1,7 @@ Date: Wed, 29 Mar 2017 17:36:17 +0200 Subject: [PATCH 03/28] Exponentially increase delay --- src/AsyncClient.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index b55ca12..4c34438 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -29,6 +29,11 @@ final class AsyncClient */ protected $channels = []; + /** + * @var int + */ + protected $delay = 200; + /** * @param LoopInterface $loop * @param string $app Application ID @@ -61,8 +66,10 @@ public static function create(LoopInterface $loop, string $app, Resolver $resolv public function __construct(WebsocketClient $client) { //Only create one connection and share the most recent among all subscriber - $this->client = $client->retryWhen(function (Observable $errors) { - return $this->handleLowLevelError($errors); + $this->client = $client->retryWhen(function (Observable $errors) { + return $errors->flatMap(function (Throwable $throwable) { + return $this->handleLowLevelError($throwable); + }); })->shareReplay(1); $this->messages = $this->client ->flatMap(function (MessageSubject $ms) { @@ -131,14 +138,10 @@ public function send(array $message) }); } - private function handleLowLevelError(Observable $errors) + private function handleLowLevelError(Throwable $throwable) { - $stream = $errors->subscribe( - function (Throwable $throwable) use (&$stream) { - echo (string)$throwable, PHP_EOL; - } - ); + $this->delay *= 2; echo __LINE__, ': ', time(), PHP_EOL; - return $errors->delay(200); + return Observable::timer($this->delay); } } From de085a6f89b0eecdd6d1189efa33497c4b15f749 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Fri, 31 Mar 2017 21:42:07 +0200 Subject: [PATCH 04/28] Removed tests bootstrap --- phpunit.xml.dist | 2 +- tests/bootstrap.php | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 tests/bootstrap.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 428c916..d9b3788 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,5 @@ - + tests/ diff --git a/tests/bootstrap.php b/tests/bootstrap.php deleted file mode 100644 index 0bbcaa8..0000000 --- a/tests/bootstrap.php +++ /dev/null @@ -1,8 +0,0 @@ - Date: Sat, 1 Apr 2017 10:56:13 +0200 Subject: [PATCH 05/28] WIP Dead connection detection --- src/AsyncClient.php | 64 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index 4c34438..24ce5d0 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -4,6 +4,7 @@ use React\Dns\Resolver\Resolver; use React\EventLoop\LoopInterface; +use React\EventLoop\Timer\TimerInterface; use Rx\Disposable\CallbackDisposable; use Rx\Observable; use Rx\ObserverInterface; @@ -14,6 +15,19 @@ final class AsyncClient { + const NO_ACTIVITY_TIMEOUT = 120; + const NO_PING_RESPONSE_TIMEOUT = 30; + + /** + * @var LoopInterface + */ + protected $loop; + + /** + * @var WebsocketClient + */ + protected $lowLevelClient; + /** * @var Observable\RefCountObservable */ @@ -34,6 +48,16 @@ final class AsyncClient */ protected $delay = 200; + /** + * @var TimerInterface + */ + private $noActivityTimer; + + /** + * @var TimerInterface + */ + private $pingIimeoutTimer; + /** * @param LoopInterface $loop * @param string $app Application ID @@ -50,6 +74,7 @@ public static function create(LoopInterface $loop, string $app, Resolver $resolv } return new self( + $loop, new WebsocketClient( ApiSettings::createUrl($app), false, @@ -63,22 +88,25 @@ public static function create(LoopInterface $loop, string $app, Resolver $resolv /** * @internal */ - public function __construct(WebsocketClient $client) + public function __construct(LoopInterface $loop, WebsocketClient $client) { + $this->loop = $loop; //Only create one connection and share the most recent among all subscriber - $this->client = $client->retryWhen(function (Observable $errors) { + $this->lowLevelClient = $client; + $this->client = $this->lowLevelClient->retryWhen(function (Observable $errors) { + echo __LINE__, ': ', time(), PHP_EOL; + $this->resetActivityTimer(); return $errors->flatMap(function (Throwable $throwable) { return $this->handleLowLevelError($throwable); }); })->shareReplay(1); $this->messages = $this->client ->flatMap(function (MessageSubject $ms) { - //var_export($ms); return $ms; }) ->_ApiClients_jsonDecode() ->map(function (array $message) { - //var_export($message); + $this->resetActivityTimer(); return Event::createFromMessage($message); }); } @@ -134,14 +162,42 @@ public function send(array $message) $this->client ->take(1) ->subscribe(function (MessageSubject $ms) use ($message) { + $this->resetActivityTimer(); $ms->send(json_encode($message)); }); } private function handleLowLevelError(Throwable $throwable) { + $this->resetActivityTimer(); $this->delay *= 2; + echo get_class($throwable), PHP_EOL; + echo get_class($throwable->getPrevious()), PHP_EOL; + echo get_class($throwable->getPrevious()->getPrevious()), PHP_EOL; + echo get_class($throwable->getPrevious()->getPrevious()->getPrevious()), PHP_EOL; echo __LINE__, ': ', time(), PHP_EOL; return Observable::timer($this->delay); } + + private function resetActivityTimer() + { + echo 'resetActivityTimer', PHP_EOL; + if ($this->noActivityTimer instanceof TimerInterface) { + $this->noActivityTimer->cancel(); + } + + $this->noActivityTimer = $this->loop->addTimer( + self::NO_ACTIVITY_TIMEOUT, + function () { + echo 'resetActivityTimer:tick', PHP_EOL; + $this->send(['event' => 'pusher:ping']); + $this->pingIimeoutTimer = $this->loop->addTimer( + self::NO_PING_RESPONSE_TIMEOUT, + function () { + $this->lowLevelClient->dispose(); + } + ); + } + ); + } } From 816f0505841ca5bd625712c62f9bddf09de5f8f2 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Sat, 1 Apr 2017 17:59:18 +0200 Subject: [PATCH 06/28] Working dead connection detection, thanks to @mbonneau via https://gist.github.com/mbonneau/e470e87c4139733cd132931aea74d0cc --- src/AsyncClient.php | 102 ++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index 24ce5d0..554ae55 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -17,17 +17,14 @@ final class AsyncClient { const NO_ACTIVITY_TIMEOUT = 120; const NO_PING_RESPONSE_TIMEOUT = 30; + //const NO_ACTIVITY_TIMEOUT = 12; + //const NO_PING_RESPONSE_TIMEOUT = 3; /** * @var LoopInterface */ protected $loop; - /** - * @var WebsocketClient - */ - protected $lowLevelClient; - /** * @var Observable\RefCountObservable */ @@ -38,6 +35,11 @@ final class AsyncClient */ protected $messages; + /** + * @var MessageSubject + */ + protected $sendSubject; + /** * @var array */ @@ -91,22 +93,50 @@ public static function create(LoopInterface $loop, string $app, Resolver $resolv public function __construct(LoopInterface $loop, WebsocketClient $client) { $this->loop = $loop; - //Only create one connection and share the most recent among all subscriber - $this->lowLevelClient = $client; - $this->client = $this->lowLevelClient->retryWhen(function (Observable $errors) { - echo __LINE__, ': ', time(), PHP_EOL; - $this->resetActivityTimer(); - return $errors->flatMap(function (Throwable $throwable) { - return $this->handleLowLevelError($throwable); - }); - })->shareReplay(1); - $this->messages = $this->client + $this->messages = $client->shareReplay(1) + // Save this subject for sending stuff + ->do(function (MessageSubject $ms) { + echo 'set snedSubject', PHP_EOL; + $this->sendSubject = $ms; + }) + + // Make sure if there is a disconnect or something + // that we unset the sendSubject + ->finally(function () { + echo 'unset snedSubject', PHP_EOL; + $this->sendSubject = null; + }) + + ->flatMap(function (MessageSubject $ms) { return $ms; }) + + // This is the ping/timeout functionality + ->flatMapLatest(function ($x) { + // this Observable emits the current value immediately + // if another value comes along, this all gets disposed (because we are using flatMapLatest) + // before the timeouts start get triggered + return Observable::never() + ->timeout(self::NO_ACTIVITY_TIMEOUT * 1000) + ->catch(function () use ($x) { + echo 'send ping', PHP_EOL; + // ping (do something that causes incoming stream to get a message) + $this->send(['event' => 'pusher:ping']); + // this timeout will actually timeout with a TimeoutException - causing + // everything above this to dispose + return Observable::never()->timeout(self::NO_PING_RESPONSE_TIMEOUT * 1000); + }) + ->startWith($x); + }) + ->retryWhen(function (Observable $errors) { + echo __LINE__, ': ', time(), PHP_EOL; + return $errors->flatMap(function (Throwable $throwable) { + return $this->handleLowLevelError($throwable); + }); + }) ->_ApiClients_jsonDecode() ->map(function (array $message) { - $this->resetActivityTimer(); return Event::createFromMessage($message); }); } @@ -159,45 +189,23 @@ public function channel(string $channel): Observable */ public function send(array $message) { - $this->client - ->take(1) - ->subscribe(function (MessageSubject $ms) use ($message) { - $this->resetActivityTimer(); - $ms->send(json_encode($message)); - }); + if ($this->sendSubject === null) { + echo 'send subject is null when trying to send', PHP_EOL; + return; + } + + echo __LINE__, ' Sending JSON: ', json_encode($message), PHP_EOL; + $this->sendSubject->onNext(json_encode($message)); } private function handleLowLevelError(Throwable $throwable) { - $this->resetActivityTimer(); $this->delay *= 2; echo get_class($throwable), PHP_EOL; - echo get_class($throwable->getPrevious()), PHP_EOL; + /*echo get_class($throwable->getPrevious()), PHP_EOL; echo get_class($throwable->getPrevious()->getPrevious()), PHP_EOL; - echo get_class($throwable->getPrevious()->getPrevious()->getPrevious()), PHP_EOL; + echo get_class($throwable->getPrevious()->getPrevious()->getPrevious()), PHP_EOL;*/ echo __LINE__, ': ', time(), PHP_EOL; return Observable::timer($this->delay); } - - private function resetActivityTimer() - { - echo 'resetActivityTimer', PHP_EOL; - if ($this->noActivityTimer instanceof TimerInterface) { - $this->noActivityTimer->cancel(); - } - - $this->noActivityTimer = $this->loop->addTimer( - self::NO_ACTIVITY_TIMEOUT, - function () { - echo 'resetActivityTimer:tick', PHP_EOL; - $this->send(['event' => 'pusher:ping']); - $this->pingIimeoutTimer = $this->loop->addTimer( - self::NO_PING_RESPONSE_TIMEOUT, - function () { - $this->lowLevelClient->dispose(); - } - ); - } - ); - } } From 65fb1e57834781667eb227f61b349bb82966f465 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Sun, 2 Apr 2017 22:38:40 +0200 Subject: [PATCH 07/28] Updated license --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index cd2d429..b7fcfc6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Cees-Jan Kiewiet +Copyright (c) 2017 Cees-Jan Kiewiet Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index d6fe5af..aa91f91 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The MIT License (MIT) -Copyright (c) 2016 Cees-Jan Kiewiet +Copyright (c) 2017 Cees-Jan Kiewiet Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 6fb10f4caae9ab7d7fe34c9e3101fe2484b96838 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Wed, 5 Apr 2017 17:44:50 +0200 Subject: [PATCH 08/28] Further reworking of how connections, and specifically errors, are handled --- src/AsyncClient.php | 93 ++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index 554ae55..c917693 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -4,7 +4,6 @@ use React\Dns\Resolver\Resolver; use React\EventLoop\LoopInterface; -use React\EventLoop\Timer\TimerInterface; use Rx\Disposable\CallbackDisposable; use Rx\Observable; use Rx\ObserverInterface; @@ -17,8 +16,8 @@ final class AsyncClient { const NO_ACTIVITY_TIMEOUT = 120; const NO_PING_RESPONSE_TIMEOUT = 30; - //const NO_ACTIVITY_TIMEOUT = 12; - //const NO_PING_RESPONSE_TIMEOUT = 3; + + protected $noActivityTimeout = self::NO_ACTIVITY_TIMEOUT; /** * @var LoopInterface @@ -50,16 +49,6 @@ final class AsyncClient */ protected $delay = 200; - /** - * @var TimerInterface - */ - private $noActivityTimer; - - /** - * @var TimerInterface - */ - private $pingIimeoutTimer; - /** * @param LoopInterface $loop * @param string $app Application ID @@ -93,17 +82,20 @@ public static function create(LoopInterface $loop, string $app, Resolver $resolv public function __construct(LoopInterface $loop, WebsocketClient $client) { $this->loop = $loop; - $this->messages = $client->shareReplay(1) + $this->messages = $client // Save this subject for sending stuff ->do(function (MessageSubject $ms) { - echo 'set snedSubject', PHP_EOL; $this->sendSubject = $ms; + + // Resubscribe to an channels we where subscribed to when disconnected + foreach ($this->channels as $channel => $_) { + $this->subscribeOnChannel($channel); + } }) // Make sure if there is a disconnect or something // that we unset the sendSubject ->finally(function () { - echo 'unset snedSubject', PHP_EOL; $this->sendSubject = null; }) @@ -118,9 +110,8 @@ public function __construct(LoopInterface $loop, WebsocketClient $client) // if another value comes along, this all gets disposed (because we are using flatMapLatest) // before the timeouts start get triggered return Observable::never() - ->timeout(self::NO_ACTIVITY_TIMEOUT * 1000) + ->timeout($this->noActivityTimeout * 1000) ->catch(function () use ($x) { - echo 'send ping', PHP_EOL; // ping (do something that causes incoming stream to get a message) $this->send(['event' => 'pusher:ping']); // this timeout will actually timeout with a TimeoutException - causing @@ -129,16 +120,28 @@ public function __construct(LoopInterface $loop, WebsocketClient $client) }) ->startWith($x); }) + + // Handle connection level errors ->retryWhen(function (Observable $errors) { - echo __LINE__, ': ', time(), PHP_EOL; return $errors->flatMap(function (Throwable $throwable) { return $this->handleLowLevelError($throwable); }); }) + + // Decode JSON ->_ApiClients_jsonDecode() + + // Deal with connection established messages ->map(function (array $message) { - return Event::createFromMessage($message); - }); + $event = Event::createFromMessage($message); + + if ($event->getEvent() === 'pusher:connection_established') { + $this->setActivityTimeout($event); + } + + return $event; + }) + ->share(); } /** @@ -169,7 +172,7 @@ public function channel(string $channel): Observable }) ->subscribe($observer); - $this->send(['event' => 'pusher:subscribe', 'data' => ['channel' => $channel]]); + $this->subscribeOnChannel($channel); return new CallbackDisposable(function () use ($channel, $subscription) { $this->send(['event' => 'pusher:unsubscribe', 'data' => ['channel' => $channel]]); @@ -186,26 +189,54 @@ public function channel(string $channel): Observable * Send a message through the client * * @param array $message Message to send, will be json encoded + * + * @return A bool indicating whether or not the connection was active + * and the given message has been pass onto the connection. */ - public function send(array $message) + public function send(array $message): bool { if ($this->sendSubject === null) { - echo 'send subject is null when trying to send', PHP_EOL; - return; + return false; } - echo __LINE__, ' Sending JSON: ', json_encode($message), PHP_EOL; $this->sendSubject->onNext(json_encode($message)); + return true; } private function handleLowLevelError(Throwable $throwable) { $this->delay *= 2; - echo get_class($throwable), PHP_EOL; - /*echo get_class($throwable->getPrevious()), PHP_EOL; - echo get_class($throwable->getPrevious()->getPrevious()), PHP_EOL; - echo get_class($throwable->getPrevious()->getPrevious()->getPrevious()), PHP_EOL;*/ - echo __LINE__, ': ', time(), PHP_EOL; return Observable::timer($this->delay); } + + /** + * @param string $channel + */ + private function subscribeOnChannel(string $channel) + { + $this->send(['event' => 'pusher:subscribe', 'data' => ['channel' => $channel]]); + } + + + /** + * Get connection activity timeout from connection established event + * + * @param Event $event + */ + private function setActivityTimeout(Event $event) + { + $data = $event->getData(); + + // No activity_timeout found on event + if (!isset($data['activity_timeout'])) { + return; + } + + // activity_timeout holds zero or invalid value (we don't want to hammer Pusher) + if ((int)$data['activity_timeout'] <= 0) { + return; + } + + $this->noActivityTimeout = (int)$data['activity_timeout']; + } } From 265a08358dacd04c6f1a97850f3e0d7183873f96 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Thu, 6 Apr 2017 07:49:17 +0200 Subject: [PATCH 09/28] Removed whitespace --- src/AsyncClient.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index c917693..4acc7a5 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -99,7 +99,6 @@ public function __construct(LoopInterface $loop, WebsocketClient $client) $this->sendSubject = null; }) - ->flatMap(function (MessageSubject $ms) { return $ms; }) From 6c3e5b75578008d4d8e0f066c5e9168f333c3bb4 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Thu, 6 Apr 2017 08:13:36 +0200 Subject: [PATCH 10/28] Comments --- src/AsyncClient.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index 4acc7a5..9432481 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -155,6 +155,7 @@ public function channel(string $channel): Observable return $this->channels[$channel]; } + // Ensure we only get messages for the given channel $channelMessages = $this->messages->filter(function (Event $event) use ($channel) { return $event->getChannel() !== '' && $event->getChannel() === $channel; }); @@ -174,12 +175,16 @@ public function channel(string $channel): Observable $this->subscribeOnChannel($channel); return new CallbackDisposable(function () use ($channel, $subscription) { + // Send unsubscribe event $this->send(['event' => 'pusher:unsubscribe', 'data' => ['channel' => $channel]]); + // Dispose our own subscription to messages $subscription->dispose(); + // Remove our channel from the channel list so we don't resubscribe in case we reconnect unset($this->channels[$channel]); }); }); + // Share stream amount subscribers to this channel $this->channels[$channel] = $events->share(); return $this->channels[$channel]; } @@ -194,6 +199,7 @@ public function channel(string $channel): Observable */ public function send(array $message): bool { + // Don't send messages when we aren't connected if ($this->sendSubject === null) { return false; } From 0e712044f96241d07ced71ba3ae2e8bbd915860f Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Fri, 7 Apr 2017 17:19:39 +0200 Subject: [PATCH 11/28] No need anymore for minimum-stability dev --- composer.json | 2 -- composer.lock | 50 +++++++++++++++++++++++++------------------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/composer.json b/composer.json index ee9502c..0c38b11 100644 --- a/composer.json +++ b/composer.json @@ -2,8 +2,6 @@ "name": "api-clients/pusher", "homepage": "https://php-api-clients.org/clients/pusher", "license": "MIT", - "minimum-stability": "dev", - "prefer-stable": true, "authors": [ { "name": "Cees-Jan Kiewiet", diff --git a/composer.lock b/composer.lock index f375a65..850825e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "2c061a6feaf1adc6cb1d87f80cb90cc0", + "content-hash": "7909fef9eaa097508216f7396d5457e8", "packages": [ { "name": "api-clients/command-bus", @@ -877,16 +877,16 @@ }, { "name": "react/dns", - "version": "v0.4.6", + "version": "v0.4.7", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "a4c32f0021c742a1781c445270cb29a2d4b76fed" + "reference": "e09ca286e3e18fea93317315dd7fc59d7963af8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/a4c32f0021c742a1781c445270cb29a2d4b76fed", - "reference": "a4c32f0021c742a1781c445270cb29a2d4b76fed", + "url": "https://api.github.com/repos/reactphp/dns/zipball/e09ca286e3e18fea93317315dd7fc59d7963af8a", + "reference": "e09ca286e3e18fea93317315dd7fc59d7963af8a", "shasum": "" }, "require": { @@ -894,7 +894,7 @@ "react/cache": "~0.4.0|~0.3.0", "react/promise": "~2.1|~1.2", "react/promise-timer": "~1.1", - "react/socket": "^0.5 || ^0.4.4", + "react/socket": "^0.7 || ^0.6 || ^0.5 || ^0.4.4", "react/stream": "^0.6 || ^0.5 || ^0.4.5" }, "require-dev": { @@ -915,7 +915,7 @@ "dns", "dns-resolver" ], - "time": "2017-03-11T13:46:09+00:00" + "time": "2017-03-31T16:21:48+00:00" }, { "name": "react/event-loop", @@ -1269,16 +1269,16 @@ }, { "name": "reactivex/rxphp", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/ReactiveX/RxPHP.git", - "reference": "4ad811032428074485d7ca99b779aec54b242982" + "reference": "ca5e94f7e7bacb9adcd4a3ed2b06a4a53e9ebcbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ReactiveX/RxPHP/zipball/4ad811032428074485d7ca99b779aec54b242982", - "reference": "4ad811032428074485d7ca99b779aec54b242982", + "url": "https://api.github.com/repos/ReactiveX/RxPHP/zipball/ca5e94f7e7bacb9adcd4a3ed2b06a4a53e9ebcbe", + "reference": "ca5e94f7e7bacb9adcd4a3ed2b06a4a53e9ebcbe", "shasum": "" }, "require": { @@ -1330,7 +1330,7 @@ "reactive", "rx" ], - "time": "2017-03-21T13:29:22+00:00" + "time": "2017-04-01T02:13:25+00:00" }, { "name": "ringcentral/psr7", @@ -1440,16 +1440,16 @@ }, { "name": "rx/websocket", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/RxPHP/RxWebsocket.git", - "reference": "9e8e40f5356105965adb47731823a43edabd9069" + "reference": "fc60831de1863ed0469f97a1735f6ba07e3b572e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/RxPHP/RxWebsocket/zipball/9e8e40f5356105965adb47731823a43edabd9069", - "reference": "9e8e40f5356105965adb47731823a43edabd9069", + "url": "https://api.github.com/repos/RxPHP/RxWebsocket/zipball/fc60831de1863ed0469f97a1735f6ba07e3b572e", + "reference": "fc60831de1863ed0469f97a1735f6ba07e3b572e", "shasum": "" }, "require": { @@ -1495,7 +1495,7 @@ "rxphp", "websocket" ], - "time": "2017-03-21T17:41:42+00:00" + "time": "2017-03-28T16:51:17+00:00" }, { "name": "voryx/event-loop", @@ -2184,16 +2184,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "5.0.3", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "4e99e1c4f9b05cbf4d6e84b100b3ff4107cf8cd1" + "reference": "cff36444733ac6d3f153866f55898373ca184610" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4e99e1c4f9b05cbf4d6e84b100b3ff4107cf8cd1", - "reference": "4e99e1c4f9b05cbf4d6e84b100b3ff4107cf8cd1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cff36444733ac6d3f153866f55898373ca184610", + "reference": "cff36444733ac6d3f153866f55898373ca184610", "shasum": "" }, "require": { @@ -2217,7 +2217,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0.x-dev" + "dev-master": "5.1.x-dev" } }, "autoload": { @@ -2243,7 +2243,7 @@ "testing", "xunit" ], - "time": "2017-03-06T14:22:16+00:00" + "time": "2017-04-07T04:39:58+00:00" }, { "name": "phpunit/php-file-iterator", @@ -3215,9 +3215,9 @@ } ], "aliases": [], - "minimum-stability": "dev", + "minimum-stability": "stable", "stability-flags": [], - "prefer-stable": true, + "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^7.0" From 133625bfaf27128950c33d1c51ff40b9f7bf94e7 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Sun, 7 May 2017 14:49:47 +0200 Subject: [PATCH 12/28] Updated bitstamp to RxPHP 2.0 --- examples/bitstamp-async.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/bitstamp-async.php b/examples/bitstamp-async.php index b7d29d0..cb0a1e5 100644 --- a/examples/bitstamp-async.php +++ b/examples/bitstamp-async.php @@ -13,15 +13,15 @@ * The App ID isn't a secret and comes from the bitstamp docs: * @link https://www.bitstamp.net/websocket/ */ -$client = new AsyncClient($loop, 'de504dc5763aeef9ff52'); +$client = AsyncClient::create($loop, 'de504dc5763aeef9ff52'); $channelitems = array('live_trades','live_trades_xrpusd'); foreach ($channelitems as $channelitem) { - $client->channel($channelitem)->subscribe(new CallbackObserver(function (Event $event) { + $client->channel($channelitem)->subscribe(function (Event $event) { echo 'Channel: '. $event->getChannel(). PHP_EOL; echo 'Event: '. $event->getEvent(). PHP_EOL; echo 'Data: '. print_r($event->getData(),1). PHP_EOL; - })); + }); } $loop->run(); From 96d9f5b88ee0ec2418b7d6cd2185825cf70557a8 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Wed, 10 May 2017 07:51:48 +0200 Subject: [PATCH 13/28] Updated AppVeyor config --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d51dda0..d2cf44c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - dependencies: highest php_ver_target: 7.1 -## Cache composer bits +## Cache composer, chocolatey and php bits cache: - '%LOCALAPPDATA%\Composer\files -> composer.lock' @@ -33,7 +33,7 @@ init: ## Install PHP and composer, and run the appropriate composer command install: - IF EXIST c:\tools\php (SET PHP=0) - - ps: appveyor-retry cinst -y php --version ((choco search php --exact --all-versions -r | select-string -pattern $Env:php_ver_target | Select-Object -first 1) -replace '[php|]','') + - ps: appveyor-retry cinst --params '""/InstallDir:C:\tools\php""' --ignore-checksums -y php --version ((choco search php --exact --all-versions -r | select-string -pattern $env:php_ver_target | sort { [version]($_ -split '\|' | select -last 1) } -Descending | Select-Object -first 1) -replace '[php|]','') - cd c:\tools\php - IF %PHP%==1 copy php.ini-production php.ini /Y - IF %PHP%==1 echo date.timezone="UTC" >> php.ini From 2f4c25da39b835fc14a07cd544a4f4a27f27e4e5 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Sun, 21 May 2017 19:08:40 +0200 Subject: [PATCH 14/28] Updated CS fixer --- .php_cs | 17 +++++++++++++++ .travis.yml | 42 ++++++++++++++++++++++++------------ Makefile | 38 +++++++++++++++++++++----------- appveyor.yml | 1 + composer.json | 60 +++++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 130 insertions(+), 28 deletions(-) create mode 100644 .php_cs diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..181fd22 --- /dev/null +++ b/.php_cs @@ -0,0 +1,17 @@ +setFinder( + PhpCsFixer\Finder::create() + ->in($path) + ->append([$path]) + ) + ->setUsingCache(false) + ; +})(); diff --git a/.travis.yml b/.travis.yml index 3d24f7e..81ea2fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,37 +5,51 @@ cache: directories: - $HOME/.composer/cache/files -## PHP versions we test against -php: - - 7.0 - - 7.1 - - nightly - ## Build matrix for lowest and highest possible targets matrix: include: - php: 7.0 - env: dependencies=lowest + env: + - qaExtended=true + - php: 7.1 + - php: nightly + env: + - dropPlatform=false + - php: 7.0 + env: + - dependencies=lowest - php: 7.1 - env: dependencies=lowest + env: + - dependencies=lowest - php: nightly - env: dependencies=lowest + env: + - dependencies=lowest + - dropPlatform=false - php: 7.0 - env: dependencies=highest + env: + - dependencies=highest - php: 7.1 - env: dependencies=highest + env: + - dependencies=highest - php: nightly - env: dependencies=highest + env: + - dependencies=highest + - dropPlatform=false ## Install or update dependencies install: + - composer validate + - if [ -z "$dropPlatform" ]; then composer config --unset platform.php; fi; + - if [ -z "$qaExtended" ]; then phpenv config-rm xdebug.ini || :; fi; - if [ -z "$dependencies" ]; then composer install --prefer-dist; fi; - if [ "$dependencies" = "lowest" ]; then composer update --prefer-lowest --prefer-dist -n; fi; - if [ "$dependencies" = "highest" ]; then composer update --prefer-dist -n; fi; - composer show ## Run the actual test -script: make travis +script: + - if [ -z "$qaExtended" ]; then make ci; fi; + - if [ "$qaExtended" = "true" ]; then make ci-extended; fi; ## Gather coverage and set it to coverage servers -after_script: make travis-coverage +after_script: if [ "$qaExtended" = "true" ]; then make ci-coverage; fi; diff --git a/Makefile b/Makefile index 17f6380..f0f1bca 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,32 @@ -all: cs unit -travis: cs travis-unit -contrib: cs unit +all: + composer run-script qa-all --timeout=0 + +all-coverage: + composer run-script qa-all-coverage --timeout=0 + +ci: + composer run-script qa-ci --timeout=0 + +ci-extended: + composer run-script qa-ci-extended --timeout=0 + +contrib: + composer run-script qa-contrib --timeout=0 init: - if [ ! -d vendor ]; then composer install; fi; + composer ensure-installed + +cs: + composer cs -cs: init - ./vendor/bin/phpcs --standard=PSR2 src/ +cs-fix: + composer cs-fix -unit: init - ./vendor/bin/phpunit --coverage-text --coverage-html covHtml +unit: + composer run-script unit --timeout=0 -travis-unit: init - ./vendor/bin/phpunit --coverage-text --coverage-clover ./build/logs/clover.xml +unit-coverage: + composer run-script unit-coverage --timeout=0 -travis-coverage: init - if [ -f ./build/logs/clover.xml ]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover ./build/logs/clover.xml; fi +ci-coverage: init + composer ci-coverage diff --git a/appveyor.yml b/appveyor.yml index d2cf44c..40bc389 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -44,6 +44,7 @@ install: - IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat - appveyor-retry appveyor DownloadFile https://getcomposer.org/composer.phar - cd c:\projects\php-project-workspace + - composer config --unset platform.php - IF %dependencies%==lowest appveyor-retry composer update --prefer-lowest --no-progress --profile -n - IF %dependencies%==current appveyor-retry composer install --no-progress --profile - IF %dependencies%==highest appveyor-retry composer update --no-progress --profile -n diff --git a/composer.json b/composer.json index 0c38b11..94f670c 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "rx/websocket": "^1.0" }, "require-dev": { - "api-clients/test-utilities": "^2.0" + "api-clients/test-utilities": "^4.1" }, "autoload": { "psr-4": { @@ -31,7 +31,10 @@ } }, "config": { - "sort-packages": true + "sort-packages": true, + "platform": { + "php": "7.0" + } }, "extra": { "api-clients": { @@ -40,5 +43,58 @@ "namespace": "ApiClients\\Client\\Pusher\\CommandBus" } } + }, + "scripts": { + "ensure-installed": "composer install --ansi -n -q", + "cs": [ + "@ensure-installed", + "php-cs-fixer fix --config=.php_cs --ansi --dry-run --diff --verbose --allow-risky=yes --show-progress=estimating" + ], + "cs-fix": [ + "@ensure-installed", + "php-cs-fixer fix --config=.php_cs --ansi --verbose --allow-risky=yes --show-progress=estimating" + ], + "unit": [ + "@ensure-installed", + "phpunit --colors=always -c phpunit.xml.dist" + ], + "unit-coverage": [ + "@ensure-installed", + "phpunit --colors=always -c phpunit.xml.dist --coverage-text --coverage-html covHtml --coverage-clover ./build/logs/clover.xml" + ], + "lint-php": [ + "@ensure-installed", + "parallel-lint --exclude vendor ." + ], + "qa-all": [ + "@lint-php", + "@cs", + "@unit" + ], + "qa-all-coverage": [ + "@lint-php", + "@cs", + "@unit-coverage" + ], + "qa-windows": [ + "@lint-php", + "@cs", + "@unit" + ], + "qa-ci": [ + "@unit" + ], + "qa-ci-extended": [ + "@qa-all-coverage" + ], + "qa-ci-windows": [ + "@qa-windows" + ], + "qa-contrib": [ + "@qa-all" + ], + "ci-coverage": [ + "if [ -f ./build/logs/clover.xml ]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover ./build/logs/clover.xml; fi" + ] } } From 0e5c0f94c5836727f207c1f590d33b4224b62022 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Sun, 21 May 2017 19:12:13 +0200 Subject: [PATCH 15/28] Added description to composer.json --- composer.json | 1 + composer.lock | 1124 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 1012 insertions(+), 113 deletions(-) diff --git a/composer.json b/composer.json index 94f670c..b6a2563 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,6 @@ { "name": "api-clients/pusher", + "description": "Async first Pusher client", "homepage": "https://php-api-clients.org/clients/pusher", "license": "MIT", "authors": [ diff --git a/composer.lock b/composer.lock index 850825e..e62cef1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "7909fef9eaa097508216f7396d5457e8", + "content-hash": "028cd434720eb35d748a6aaf01d68047", "packages": [ { "name": "api-clients/command-bus", @@ -56,16 +56,16 @@ }, { "name": "api-clients/rx", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/php-api-clients/rx.git", - "reference": "baadcfcc2f088423bce8ee66f1d46cdeac1192b6" + "reference": "e5fc4181a1050e0a6ff14ba0875647ac88e71d33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-api-clients/rx/zipball/baadcfcc2f088423bce8ee66f1d46cdeac1192b6", - "reference": "baadcfcc2f088423bce8ee66f1d46cdeac1192b6", + "url": "https://api.github.com/repos/php-api-clients/rx/zipball/e5fc4181a1050e0a6ff14ba0875647ac88e71d33", + "reference": "e5fc4181a1050e0a6ff14ba0875647ac88e71d33", "shasum": "" }, "require": { @@ -96,7 +96,7 @@ "email": "ceesjank@gmail.com" } ], - "time": "2017-03-22T20:37:41+00:00" + "time": "2017-05-07T16:23:52+00:00" }, { "name": "api-clients/rx-operators", @@ -877,16 +877,16 @@ }, { "name": "react/dns", - "version": "v0.4.7", + "version": "v0.4.9", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "e09ca286e3e18fea93317315dd7fc59d7963af8a" + "reference": "288b4f36972cdc2f81dae1d1a58a0467e3f625cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/e09ca286e3e18fea93317315dd7fc59d7963af8a", - "reference": "e09ca286e3e18fea93317315dd7fc59d7963af8a", + "url": "https://api.github.com/repos/reactphp/dns/zipball/288b4f36972cdc2f81dae1d1a58a0467e3f625cb", + "reference": "288b4f36972cdc2f81dae1d1a58a0467e3f625cb", "shasum": "" }, "require": { @@ -894,8 +894,8 @@ "react/cache": "~0.4.0|~0.3.0", "react/promise": "~2.1|~1.2", "react/promise-timer": "~1.1", - "react/socket": "^0.7 || ^0.6 || ^0.5 || ^0.4.4", - "react/stream": "^0.6 || ^0.5 || ^0.4.5" + "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5 || ^0.4.4", + "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4.5" }, "require-dev": { "phpunit/phpunit": "^5.0 || ^4.8.10" @@ -915,36 +915,34 @@ "dns", "dns-resolver" ], - "time": "2017-03-31T16:21:48+00:00" + "time": "2017-05-01T17:21:03+00:00" }, { "name": "react/event-loop", - "version": "v0.4.2", + "version": "v0.4.3", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "164799f73175e1c80bba92a220ea35df6ca371dd" + "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/164799f73175e1c80bba92a220ea35df6ca371dd", - "reference": "164799f73175e1c80bba92a220ea35df6ca371dd", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/8bde03488ee897dc6bb3d91e4e17c353f9c5252f", + "reference": "8bde03488ee897dc6bb3d91e4e17c353f9c5252f", "shasum": "" }, "require": { "php": ">=5.4.0" }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, "suggest": { "ext-event": "~1.0", "ext-libev": "*", "ext-libevent": ">=0.1.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5-dev" - } - }, "autoload": { "psr-4": { "React\\EventLoop\\": "src" @@ -959,7 +957,7 @@ "asynchronous", "event-loop" ], - "time": "2016-03-08T02:09:32+00:00" + "time": "2017-04-27T10:56:23+00:00" }, { "name": "react/http", @@ -1269,16 +1267,16 @@ }, { "name": "reactivex/rxphp", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/ReactiveX/RxPHP.git", - "reference": "ca5e94f7e7bacb9adcd4a3ed2b06a4a53e9ebcbe" + "reference": "7f5f669850cc035d78f7dafb45fd797fe0ba9e7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ReactiveX/RxPHP/zipball/ca5e94f7e7bacb9adcd4a3ed2b06a4a53e9ebcbe", - "reference": "ca5e94f7e7bacb9adcd4a3ed2b06a4a53e9ebcbe", + "url": "https://api.github.com/repos/ReactiveX/RxPHP/zipball/7f5f669850cc035d78f7dafb45fd797fe0ba9e7d", + "reference": "7f5f669850cc035d78f7dafb45fd797fe0ba9e7d", "shasum": "" }, "require": { @@ -1330,7 +1328,7 @@ "reactive", "rx" ], - "time": "2017-04-01T02:13:25+00:00" + "time": "2017-05-21T01:48:34+00:00" }, { "name": "ringcentral/psr7", @@ -1645,27 +1643,26 @@ "packages-dev": [ { "name": "api-clients/test-utilities", - "version": "2.1.0", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/php-api-clients/test-utilities.git", - "reference": "427429883c6d811c4c76d97f065a7db4b384cbad" + "reference": "bdf066b2396d60f5d575fd7a5caf51de634e8ca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-api-clients/test-utilities/zipball/427429883c6d811c4c76d97f065a7db4b384cbad", - "reference": "427429883c6d811c4c76d97f065a7db4b384cbad", + "url": "https://api.github.com/repos/php-api-clients/test-utilities/zipball/bdf066b2396d60f5d575fd7a5caf51de634e8ca9", + "reference": "bdf066b2396d60f5d575fd7a5caf51de634e8ca9", "shasum": "" }, "require": { "clue/block-react": "^1.1", + "friendsofphp/php-cs-fixer": "^2.3", "jakub-onderka/php-console-highlighter": "^0.3.2", "jakub-onderka/php-parallel-lint": "^0.9.2", "phake/phake": "^2.3", "php": "^7.0", - "phpspec/prophecy": "^1.6", - "phpunit/phpunit": "^6.0 || ^5.6", - "squizlabs/php_codesniffer": "^2.6" + "phpunit/phpunit": "^6.0" }, "type": "library", "autoload": { @@ -1683,7 +1680,8 @@ "email": "ceesjank@gmail.com" } ], - "time": "2017-02-03T20:53:52+00:00" + "description": "Test utilities for api-clients packages", + "time": "2017-05-14T15:00:49+00:00" }, { "name": "doctrine/instantiator", @@ -1739,6 +1737,125 @@ ], "time": "2015-06-14T21:17:01+00:00" }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "d5257f7433bb490299c4f300d95598fd911a8ab0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/d5257f7433bb490299c4f300d95598fd911a8ab0", + "reference": "d5257f7433bb490299c4f300d95598fd911a8ab0", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.2", + "ext-tokenizer": "*", + "php": "^5.6 || >=7.0 <7.2", + "sebastian/diff": "^1.4", + "symfony/console": "^3.0", + "symfony/event-dispatcher": "^3.0", + "symfony/filesystem": "^3.0", + "symfony/finder": "^3.0", + "symfony/options-resolver": "^3.0", + "symfony/polyfill-php54": "^1.0", + "symfony/polyfill-php55": "^1.3", + "symfony/polyfill-php70": "^1.0", + "symfony/polyfill-xml": "^1.3", + "symfony/process": "^3.0", + "symfony/stopwatch": "^3.0" + }, + "conflict": { + "hhvm": "<3.18" + }, + "require-dev": { + "gecko-packages/gecko-php-unit": "^2.0", + "justinrainbow/json-schema": "^5.0", + "phpunit/phpunit": "^4.5 || ^5.0", + "satooshi/php-coveralls": "^1.0", + "symfony/phpunit-bridge": "^3.2.2" + }, + "suggest": { + "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "ext-xml": "For better performance.", + "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "time": "2017-04-25T20:50:30+00:00" + }, + { + "name": "ircmaxell/password-compat", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/ircmaxell/password_compat.git", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "autoload": { + "files": [ + "lib/password.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anthony Ferrara", + "email": "ircmaxell@php.net", + "homepage": "http://blog.ircmaxell.com" + } + ], + "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", + "homepage": "https://github.com/ircmaxell/password_compat", + "keywords": [ + "hashing", + "password" + ], + "time": "2014-11-20T16:49:30+00:00" + }, { "name": "jakub-onderka/php-console-color", "version": "0.1", @@ -1875,16 +1992,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe" + "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5a5a9fc8025a08d8919be87d6884d5a92520cefe", - "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102", + "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102", "shasum": "" }, "require": { @@ -1913,7 +2030,55 @@ "object", "object graph" ], - "time": "2017-01-26T22:05:40+00:00" + "time": "2017-04-12T18:52:22+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.10", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2017-03-13T16:27:32+00:00" }, { "name": "phake/phake", @@ -2184,16 +2349,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "5.1.0", + "version": "5.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "cff36444733ac6d3f153866f55898373ca184610" + "reference": "e648abfd8ffb1d54ad549b027b75e376e9055d02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cff36444733ac6d3f153866f55898373ca184610", - "reference": "cff36444733ac6d3f153866f55898373ca184610", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e648abfd8ffb1d54ad549b027b75e376e9055d02", + "reference": "e648abfd8ffb1d54ad549b027b75e376e9055d02", "shasum": "" }, "require": { @@ -2205,19 +2370,20 @@ "phpunit/php-token-stream": "^1.4.11 || ^2.0", "sebastian/code-unit-reverse-lookup": "^1.0", "sebastian/environment": "^2.0", - "sebastian/version": "^2.0" + "sebastian/version": "^2.0", + "theseer/tokenizer": "^1.1" }, "require-dev": { "ext-xdebug": "^2.5", "phpunit/phpunit": "^6.0" }, "suggest": { - "ext-xdebug": "^2.5.1" + "ext-xdebug": "^2.5.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1.x-dev" + "dev-master": "5.2.x-dev" } }, "autoload": { @@ -2243,7 +2409,7 @@ "testing", "xunit" ], - "time": "2017-04-07T04:39:58+00:00" + "time": "2017-04-20T10:00:57+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2572,6 +2738,53 @@ ], "time": "2017-02-02T10:36:38+00:00" }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10T12:19:37+00:00" + }, { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", @@ -2683,23 +2896,23 @@ }, { "name": "sebastian/diff", - "version": "1.4.1", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + "reference": "3c7d21999e815cdfac70c6c7d79d3a9cb1bc7bc2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3c7d21999e815cdfac70c6c7d79d3a9cb1bc7bc2", + "reference": "3c7d21999e815cdfac70c6c7d79d3a9cb1bc7bc2", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", "extra": { @@ -2731,7 +2944,7 @@ "keywords": [ "diff" ], - "time": "2015-12-08T07:14:41+00:00" + "time": "2017-05-18T13:44:30+00:00" }, { "name": "sebastian/environment", @@ -2852,23 +3065,23 @@ }, { "name": "sebastian/global-state", - "version": "1.1.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "^6.0" }, "suggest": { "ext-uopz": "*" @@ -2876,7 +3089,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2899,7 +3112,7 @@ "keywords": [ "global state" ], - "time": "2015-10-12T03:26:01+00:00" + "time": "2017-04-27T15:39:26+00:00" }, { "name": "sebastian/object-enumerator", @@ -3086,82 +3299,764 @@ "time": "2016-10-03T07:35:21+00:00" }, { - "name": "squizlabs/php_codesniffer", - "version": "2.8.1", + "name": "symfony/console", + "version": "v3.2.8", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d" + "url": "https://github.com/symfony/console.git", + "reference": "a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", - "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", + "url": "https://api.github.com/repos/symfony/console/zipball/a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38", + "reference": "a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38", "shasum": "" }, "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.1.2" + "php": ">=5.5.9", + "symfony/debug": "~2.8|~3.0", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/filesystem": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" }, - "bin": [ - "scripts/phpcs", - "scripts/phpcbf" + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/filesystem": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2017-04-26T01:39:17+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "fd6eeee656a5a7b384d56f1072243fe1c0e81686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/fd6eeee656a5a7b384d56f1072243fe1c0e81686", + "reference": "fd6eeee656a5a7b384d56f1072243fe1c0e81686", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/class-loader": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2017-04-19T20:17:50+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "b8a401f733b43251e1d088c589368b2a94155e40" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b8a401f733b43251e1d088c589368b2a94155e40", + "reference": "b8a401f733b43251e1d088c589368b2a94155e40", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "3.2-dev" } }, "autoload": { - "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Fixer.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Greg Sherwood", - "role": "lead" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "http://www.squizlabs.com/php-codesniffer", - "keywords": [ - "phpcs", - "standards" + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2017-05-01T14:58:48+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v3.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "040651db13cf061827a460cc10f6e36a445c45b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/040651db13cf061827a460cc10f6e36a445c45b4", + "reference": "040651db13cf061827a460cc10f6e36a445c45b4", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2017-04-12T14:13:17+00:00" + }, + { + "name": "symfony/finder", + "version": "v3.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "9cf076f8f492f4b1ffac40aae9c2d287b4ca6930" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/9cf076f8f492f4b1ffac40aae9c2d287b4ca6930", + "reference": "9cf076f8f492f4b1ffac40aae9c2d287b4ca6930", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2017-04-12T14:13:17+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v3.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "8cbb4f23414e2a5e92690cf67680a979a461113c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/8cbb4f23414e2a5e92690cf67680a979a461113c", + "reference": "8cbb4f23414e2a5e92690cf67680a979a461113c", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2017-04-12T14:13:17+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14T01:06:16+00:00" + }, + { + "name": "symfony/polyfill-php54", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php54.git", + "reference": "90e085822963fdcc9d1c5b73deb3d2e5783b16a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/90e085822963fdcc9d1c5b73deb3d2e5783b16a0", + "reference": "90e085822963fdcc9d1c5b73deb3d2e5783b16a0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php54\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14T01:06:16+00:00" + }, + { + "name": "symfony/polyfill-php55", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php55.git", + "reference": "03e3f0350bca2220e3623a0e340eef194405fc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/03e3f0350bca2220e3623a0e340eef194405fc67", + "reference": "03e3f0350bca2220e3623a0e340eef194405fc67", + "shasum": "" + }, + "require": { + "ircmaxell/password-compat": "~1.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php55\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14T01:06:16+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "13ce343935f0f91ca89605a2f6ca6f5c2f3faac2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/13ce343935f0f91ca89605a2f6ca6f5c2f3faac2", + "reference": "13ce343935f0f91ca89605a2f6ca6f5c2f3faac2", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14T01:06:16+00:00" + }, + { + "name": "symfony/polyfill-xml", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-xml.git", + "reference": "64b6a864f18ab4fddad49f5025f805f6781dfabd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-xml/zipball/64b6a864f18ab4fddad49f5025f805f6781dfabd", + "reference": "64b6a864f18ab4fddad49f5025f805f6781dfabd", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-xml": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Xml\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for xml's utf8_encode and utf8_decode functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14T01:06:16+00:00" + }, + { + "name": "symfony/process", + "version": "v3.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0", + "reference": "999c2cf5061e627e6cd551dc9ebf90dd1d11d9f0", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2017-04-12T14:13:17+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v3.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "5a0105afb670dbd38f521105c444de1b8e10cfe3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a0105afb670dbd38f521105c444de1b8e10cfe3", + "reference": "5a0105afb670dbd38f521105c444de1b8e10cfe3", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "https://symfony.com", + "time": "2017-04-12T14:13:17+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } ], - "time": "2017-03-01T22:17:45+00:00" + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2017-04-07T12:08:54+00:00" }, { "name": "webmozart/assert", @@ -3222,5 +4117,8 @@ "platform": { "php": "^7.0" }, - "platform-dev": [] + "platform-dev": [], + "platform-overrides": { + "php": "7.0" + } } From 9f9ba181c7b30f575974596a3c0663be02be036b Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Sun, 21 May 2017 19:14:29 +0200 Subject: [PATCH 16/28] codestyle fixes --- src/AsyncClient.php | 75 +++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index 9432481..369445b 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -19,11 +19,6 @@ final class AsyncClient protected $noActivityTimeout = self::NO_ACTIVITY_TIMEOUT; - /** - * @var LoopInterface - */ - protected $loop; - /** * @var Observable\RefCountObservable */ @@ -49,39 +44,11 @@ final class AsyncClient */ protected $delay = 200; - /** - * @param LoopInterface $loop - * @param string $app Application ID - * @param Resolver $resolver Optional DNS resolver - * @return AsyncClient - */ - public static function create(LoopInterface $loop, string $app, Resolver $resolver = null): AsyncClient - { - try { - Scheduler::setAsyncFactory(function () use ($loop) { - return new Scheduler\EventLoopScheduler($loop); - }); - } catch (Throwable $t) { - } - - return new self( - $loop, - new WebsocketClient( - ApiSettings::createUrl($app), - false, - [], - $loop, - $resolver - ) - ); - } - /** * @internal */ - public function __construct(LoopInterface $loop, WebsocketClient $client) + public function __construct(Observable $client) { - $this->loop = $loop; $this->messages = $client // Save this subject for sending stuff ->do(function (MessageSubject $ms) { @@ -144,9 +111,35 @@ public function __construct(LoopInterface $loop, WebsocketClient $client) } /** - * Listen on a channel + * @param LoopInterface $loop + * @param string $app Application ID + * @param Resolver $resolver Optional DNS resolver + * @return AsyncClient + */ + public static function create(LoopInterface $loop, string $app, Resolver $resolver = null): AsyncClient + { + try { + Scheduler::setAsyncFactory(function () use ($loop) { + return new Scheduler\EventLoopScheduler($loop); + }); + } catch (Throwable $t) { + } + + return new self( + new WebsocketClient( + ApiSettings::createUrl($app), + false, + [], + $loop, + $resolver + ) + ); + } + + /** + * Listen on a channel. * - * @param string $channel Channel to listen on + * @param string $channel Channel to listen on * @return Observable */ public function channel(string $channel): Observable @@ -186,16 +179,17 @@ public function channel(string $channel): Observable // Share stream amount subscribers to this channel $this->channels[$channel] = $events->share(); + return $this->channels[$channel]; } /** - * Send a message through the client + * Send a message through the client. * * @param array $message Message to send, will be json encoded * * @return A bool indicating whether or not the connection was active - * and the given message has been pass onto the connection. + * and the given message has been pass onto the connection. */ public function send(array $message): bool { @@ -205,12 +199,14 @@ public function send(array $message): bool } $this->sendSubject->onNext(json_encode($message)); + return true; } private function handleLowLevelError(Throwable $throwable) { $this->delay *= 2; + return Observable::timer($this->delay); } @@ -222,9 +218,8 @@ private function subscribeOnChannel(string $channel) $this->send(['event' => 'pusher:subscribe', 'data' => ['channel' => $channel]]); } - /** - * Get connection activity timeout from connection established event + * Get connection activity timeout from connection established event. * * @param Event $event */ From e39bfb82598a02a0e14bcf7290854a69b59cea4c Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 22 May 2017 19:41:31 +0200 Subject: [PATCH 17/28] Codestyle fixes --- src/ApiSettings.php | 8 ++++---- src/Client.php | 2 +- .../Handler/SharedAppClientHandler.php | 2 +- src/Event.php | 20 +++++++++---------- src/Service/SharedAppClientService.php | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/ApiSettings.php b/src/ApiSettings.php index e702cd6..e1eea20 100644 --- a/src/ApiSettings.php +++ b/src/ApiSettings.php @@ -7,9 +7,9 @@ final class ApiSettings { /** - * Create Pusher compatible version + * Create Pusher compatible version. * - * @param string $version + * @param string $version * @return string */ public static function getVersion(string $version = ''): string @@ -28,9 +28,9 @@ public static function getVersion(string $version = ''): string } /** - * Create WebSocket URL for given App ID + * Create WebSocket URL for given App ID. * - * @param string $appId + * @param string $appId * @return string */ public static function createUrl(string $appId): string diff --git a/src/Client.php b/src/Client.php index 4785f0f..092f900 100644 --- a/src/Client.php +++ b/src/Client.php @@ -31,7 +31,7 @@ public function __construct(string $app) } /** - * @param string $channel Channel to listen on + * @param string $channel Channel to listen on * @param callable $listener Listener to call on new messages */ public function channel(string $channel, callable $listener) diff --git a/src/CommandBus/Handler/SharedAppClientHandler.php b/src/CommandBus/Handler/SharedAppClientHandler.php index 19263e5..b20f61c 100644 --- a/src/CommandBus/Handler/SharedAppClientHandler.php +++ b/src/CommandBus/Handler/SharedAppClientHandler.php @@ -24,7 +24,7 @@ public function __construct(SharedAppClientService $service) } /** - * @param SharedAppClientCommand $command + * @param SharedAppClientCommand $command * @return PromiseInterface */ public function handle(SharedAppClientCommand $command): PromiseInterface diff --git a/src/Event.php b/src/Event.php index 4bad7e8..679ce72 100644 --- a/src/Event.php +++ b/src/Event.php @@ -19,18 +19,9 @@ final class Event */ private $data; - public static function createFromMessage(array $message): self - { - return new self( - $message['event'], - json_decode($message['data'], true), - isset($message['channel']) ? $message['channel'] : '' - ); - } - /** * @param string $event - * @param array $data + * @param array $data * @param string $channel */ public function __construct(string $event, array $data, string $channel = '') @@ -40,6 +31,15 @@ public function __construct(string $event, array $data, string $channel = '') $this->channel = $channel; } + public static function createFromMessage(array $message): self + { + return new self( + $message['event'], + json_decode($message['data'], true), + isset($message['channel']) ? $message['channel'] : '' + ); + } + /** * @return string */ diff --git a/src/Service/SharedAppClientService.php b/src/Service/SharedAppClientService.php index 497525a..2bc088f 100644 --- a/src/Service/SharedAppClientService.php +++ b/src/Service/SharedAppClientService.php @@ -3,7 +3,6 @@ namespace ApiClients\Client\Pusher\Service; use ApiClients\Client\Pusher\AsyncClient; -use ApiClients\Foundation\Service\ServiceInterface; use React\EventLoop\LoopInterface; use React\Promise\CancellablePromiseInterface; use function React\Promise\resolve; @@ -30,7 +29,7 @@ public function __construct(LoopInterface $loop) } /** - * @param string|null $appId + * @param string|null $appId * @return CancellablePromiseInterface */ public function share(string $appId): CancellablePromiseInterface @@ -40,6 +39,7 @@ public function share(string $appId): CancellablePromiseInterface } $this->apps[$appId] = AsyncClient::create($this->loop, $appId); + return resolve($this->apps[$appId]); } } From 72be089fc8b9aaa262963287b3c2cb53942424bd Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 22 May 2017 21:15:03 +0200 Subject: [PATCH 18/28] Reset delay on each processed message --- src/AsyncClient.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index 369445b..91d5a4c 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -14,6 +14,7 @@ final class AsyncClient { + const DEFAULT_DELAY = 200; const NO_ACTIVITY_TIMEOUT = 120; const NO_PING_RESPONSE_TIMEOUT = 30; @@ -42,7 +43,7 @@ final class AsyncClient /** * @var int */ - protected $delay = 200; + protected $delay = self::DEFAULT_DELAY; /** * @internal @@ -99,6 +100,8 @@ public function __construct(Observable $client) // Deal with connection established messages ->map(function (array $message) { + $this->delay = self::DEFAULT_DELAY; + $event = Event::createFromMessage($message); if ($event->getEvent() === 'pusher:connection_established') { From f78dea5fc2cf6831537dea125063529d51dd88ad Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 22 May 2017 22:19:40 +0200 Subject: [PATCH 19/28] Event can now handle both array and string data --- src/Event.php | 2 +- tests/EventTest.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Event.php b/src/Event.php index 679ce72..68f9b3f 100644 --- a/src/Event.php +++ b/src/Event.php @@ -35,7 +35,7 @@ public static function createFromMessage(array $message): self { return new self( $message['event'], - json_decode($message['data'], true), + is_array($message['data']) ? $message['data'] : json_decode($message['data'], true), isset($message['channel']) ? $message['channel'] : '' ); } diff --git a/tests/EventTest.php b/tests/EventTest.php index ab04510..e22667b 100644 --- a/tests/EventTest.php +++ b/tests/EventTest.php @@ -45,6 +45,21 @@ public function eventsProvider() $data, ]; + $data = [ + 'time' => time(), + 'pid' => getmypid(), + ]; + yield [ + [ + 'event' => 'event:name', + 'channel' => 'foo-bar', + 'data' => $data, + ], + 'event:name', + 'foo-bar', + $data, + ]; + } /** From 8488242c218b78e3a7416442e2f1fca27f1b37ef Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 22 May 2017 22:20:32 +0200 Subject: [PATCH 20/28] PusherErrorException --- src/PusherErrorException.php | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/PusherErrorException.php diff --git a/src/PusherErrorException.php b/src/PusherErrorException.php new file mode 100644 index 0000000..20781d0 --- /dev/null +++ b/src/PusherErrorException.php @@ -0,0 +1,9 @@ + Date: Mon, 22 May 2017 22:24:20 +0200 Subject: [PATCH 21/28] Handle errors coming from Pusher --- src/AsyncClient.php | 55 ++++++++++++++++++++++++++++++++------- tests/AsyncClientTest.php | 36 +++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index 91d5a4c..9840816 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -4,13 +4,17 @@ use React\Dns\Resolver\Resolver; use React\EventLoop\LoopInterface; +use RuntimeException; use Rx\Disposable\CallbackDisposable; use Rx\Observable; use Rx\ObserverInterface; use Rx\Scheduler; use Rx\Websocket\Client as WebsocketClient; use Rx\Websocket\MessageSubject; +use Rx\Websocket\WebsocketErrorException; use Throwable; +use function React\Promise\reject; +use function React\Promise\resolve; final class AsyncClient { @@ -88,29 +92,35 @@ public function __construct(Observable $client) ->startWith($x); }) - // Handle connection level errors - ->retryWhen(function (Observable $errors) { - return $errors->flatMap(function (Throwable $throwable) { - return $this->handleLowLevelError($throwable); - }); - }) - // Decode JSON ->_ApiClients_jsonDecode() // Deal with connection established messages - ->map(function (array $message) { + ->flatMap(function (array $message) { $this->delay = self::DEFAULT_DELAY; $event = Event::createFromMessage($message); + if ($event->getEvent() === 'pusher:error') { + return Observable::fromPromise(reject(new PusherErrorException($event->getData()['message'], $event->getData()['code']))); + } + if ($event->getEvent() === 'pusher:connection_established') { $this->setActivityTimeout($event); } - return $event; + return Observable::fromPromise(resolve($event)); + }) + + // Handle connection level errors + ->retryWhen(function (Observable $errors) { + return $errors->flatMap(function (Throwable $throwable) { + return $this->handleLowLevelError($throwable); + }); }) - ->share(); + + // Share client + ->share(); } /** @@ -206,8 +216,33 @@ public function send(array $message): bool return true; } + /** + * Handle errors as described at https://pusher.com/docs/pusher_protocol#error-codes. + */ private function handleLowLevelError(Throwable $throwable) { + if (!($throwable instanceof WebsocketErrorException) && !($throwable instanceof RuntimeException) && !($throwable instanceof PusherErrorException)) { + return Observable::fromPromise(reject($throwable)); + } + + $code = $throwable->getCode(); + $pusherError = ($throwable instanceof WebsocketErrorException || $throwable instanceof PusherErrorException); + + // Errors 4000-4099, don't retry connecting + if ($pusherError && $code >= 4000 && $code <= 4099) { + return Observable::fromPromise(reject($throwable)); + } + + // Errors 4100-4199 reconnect after 1 or more seconds, we do it after 1.001 second + if ($pusherError && $code >= 4100 && $code <= 4199) { + return Observable::timer(1001); + } + + // Errors 4200-4299 connection closed by Pusher, reconnect immediately, we wait 0.001 second + if ($pusherError && $code >= 4200 && $code <= 4299) { + return Observable::timer(1); + } + $this->delay *= 2; return Observable::timer($this->delay); diff --git a/tests/AsyncClientTest.php b/tests/AsyncClientTest.php index 7542acb..8bb5538 100644 --- a/tests/AsyncClientTest.php +++ b/tests/AsyncClientTest.php @@ -1,11 +1,16 @@ -channel('test')->subscribe( + function () {}, + function ($e) use (&$capturedException) { + $capturedException = $e; + } + ); + self::assertNull($capturedException); + } + + public function testConnectionRetry() + { + $loop = Factory::create(); + $error = new RuntimeException('', 4199); + $resolver = $this->prophesize(Resolver::class); + $resolver->resolve('ws.pusherapp.com')->shouldBeCalled()->willReturn(reject($error)); + $client = AsyncClient::create($loop, 'abc', $resolver->reveal()); + $client->channel('test')->subscribe(); + $loop->addTimer(1, function () {}); + $loop->run(); + } } From b09a3a2ba98346d86c50b5eb5eb36da87726c5fe Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 22 May 2017 22:29:29 +0200 Subject: [PATCH 22/28] Run tests through CS fixes as well --- .php_cs | 10 +++++++--- tests/AsyncClientTest.php | 13 +++++++------ tests/EventTest.php | 5 ++--- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.php_cs b/.php_cs index 181fd22..521f92d 100644 --- a/.php_cs +++ b/.php_cs @@ -4,13 +4,17 @@ use ApiClients\Tools\TestUtilities\PhpCsFixerConfig; return (function () { - $path = __DIR__ . DIRECTORY_SEPARATOR . 'src'; + $path = __DIR__ . DIRECTORY_SEPARATOR; + $paths = [ + $path . 'src', + $path . 'tests', + ]; return PhpCsFixerConfig::create() ->setFinder( PhpCsFixer\Finder::create() - ->in($path) - ->append([$path]) + ->in($paths) + ->append($paths) ) ->setUsingCache(false) ; diff --git a/tests/AsyncClientTest.php b/tests/AsyncClientTest.php index 8bb5538..9c3144e 100644 --- a/tests/AsyncClientTest.php +++ b/tests/AsyncClientTest.php @@ -2,15 +2,14 @@ namespace ApiClients\Tests\Client\Pusher; -use React\Dns\Resolver\Resolver; -use function React\Promise\reject; -use RuntimeException; use ApiClients\Client\Pusher\AsyncClient; use ApiClients\Tools\TestUtilities\TestCase; +use React\Dns\Resolver\Resolver; use React\EventLoop\Factory; +use RuntimeException; use Rx\Observable; use Rx\Scheduler\ImmediateScheduler; -use Rx\Websocket\Client; +use function React\Promise\reject; final class AsyncClientTest extends TestCase { @@ -28,7 +27,8 @@ public function testConnectionError() $observable = Observable::error($error, new ImmediateScheduler()); $client = new AsyncClient($observable); $client->channel('test')->subscribe( - function () {}, + function () { + }, function ($e) use (&$capturedException) { $capturedException = $e; } @@ -44,7 +44,8 @@ public function testConnectionRetry() $resolver->resolve('ws.pusherapp.com')->shouldBeCalled()->willReturn(reject($error)); $client = AsyncClient::create($loop, 'abc', $resolver->reveal()); $client->channel('test')->subscribe(); - $loop->addTimer(1, function () {}); + $loop->addTimer(1, function () { + }); $loop->run(); } } diff --git a/tests/EventTest.php b/tests/EventTest.php index e22667b..204068f 100644 --- a/tests/EventTest.php +++ b/tests/EventTest.php @@ -59,14 +59,13 @@ public function eventsProvider() 'foo-bar', $data, ]; - } /** - * @param array $input + * @param array $input * @param string $event * @param string $channel - * @param array $data + * @param array $data * * @dataProvider eventsProvider */ From 773d8b88d0464e482a4091c24233429bdd147565 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 22 May 2017 22:43:15 +0200 Subject: [PATCH 23/28] Fixed line lengths --- src/AsyncClient.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index 9840816..d6f5fc1 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -102,7 +102,9 @@ public function __construct(Observable $client) $event = Event::createFromMessage($message); if ($event->getEvent() === 'pusher:error') { - return Observable::fromPromise(reject(new PusherErrorException($event->getData()['message'], $event->getData()['code']))); + return Observable::fromPromise(reject( + new PusherErrorException($event->getData()['message'], $event->getData()['code']) + )); } if ($event->getEvent() === 'pusher:connection_established') { @@ -221,7 +223,11 @@ public function send(array $message): bool */ private function handleLowLevelError(Throwable $throwable) { - if (!($throwable instanceof WebsocketErrorException) && !($throwable instanceof RuntimeException) && !($throwable instanceof PusherErrorException)) { + if ( + !($throwable instanceof WebsocketErrorException) && + !($throwable instanceof RuntimeException) && + !($throwable instanceof PusherErrorException) + ) { return Observable::fromPromise(reject($throwable)); } From 51ed05c3c19c7786382e30e6818b46c1e2eac802 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 22 May 2017 23:01:21 +0200 Subject: [PATCH 24/28] Code style fixes from @stickler-ci --- src/AsyncClient.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index d6f5fc1..b5a007f 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -223,8 +223,7 @@ public function send(array $message): bool */ private function handleLowLevelError(Throwable $throwable) { - if ( - !($throwable instanceof WebsocketErrorException) && + if (!($throwable instanceof WebsocketErrorException) && !($throwable instanceof RuntimeException) && !($throwable instanceof PusherErrorException) ) { From b08c4928d337cd0cfbeb519b766e8c753b5c8439 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 23 May 2017 07:52:37 +0200 Subject: [PATCH 25/28] Required react/dns to ensure everything works in all installations --- composer.json | 1 + composer.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b6a2563..7926f9b 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "api-clients/rx-operators": "^2.0", "clue/block-react": "^1.1", "ocramius/package-versions": "^1.1", + "react/dns": "^0.4.9", "reactivex/rxphp": "^2.0", "rx/websocket": "^1.0" }, diff --git a/composer.lock b/composer.lock index e62cef1..e0c6d47 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "028cd434720eb35d748a6aaf01d68047", + "content-hash": "933530bb893c1fb94d5c6161cb6aad6b", "packages": [ { "name": "api-clients/command-bus", From 302f06c63e3804b92eea8bfc77ef03095ca865a9 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 23 May 2017 08:12:57 +0200 Subject: [PATCH 26/28] Added a note about low level error handling also handlers Pusher errors --- src/AsyncClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AsyncClient.php b/src/AsyncClient.php index b5a007f..01d6b61 100644 --- a/src/AsyncClient.php +++ b/src/AsyncClient.php @@ -114,7 +114,7 @@ public function __construct(Observable $client) return Observable::fromPromise(resolve($event)); }) - // Handle connection level errors + // Handle connection level and Pusher procotol errors ->retryWhen(function (Observable $errors) { return $errors->flatMap(function (Throwable $throwable) { return $this->handleLowLevelError($throwable); From f2176d20e836c96f3a8383ca0c1324337ea36885 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 23 May 2017 22:09:08 +0200 Subject: [PATCH 27/28] Require voryx/event-loop:^2.0.2 to ensure everything works correctly for all installs --- composer.json | 3 ++- composer.lock | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 7926f9b..36198cb 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ "ocramius/package-versions": "^1.1", "react/dns": "^0.4.9", "reactivex/rxphp": "^2.0", - "rx/websocket": "^1.0" + "rx/websocket": "^1.0", + "voryx/event-loop": "^2.0.2" }, "require-dev": { "api-clients/test-utilities": "^4.1" diff --git a/composer.lock b/composer.lock index e0c6d47..d600ff1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "933530bb893c1fb94d5c6161cb6aad6b", + "content-hash": "f4cb67615dd0c45db128bd721e8b16d4", "packages": [ { "name": "api-clients/command-bus", @@ -2896,16 +2896,16 @@ }, { "name": "sebastian/diff", - "version": "1.4.2", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3c7d21999e815cdfac70c6c7d79d3a9cb1bc7bc2" + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3c7d21999e815cdfac70c6c7d79d3a9cb1bc7bc2", - "reference": "3c7d21999e815cdfac70c6c7d79d3a9cb1bc7bc2", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", "shasum": "" }, "require": { @@ -2944,7 +2944,7 @@ "keywords": [ "diff" ], - "time": "2017-05-18T13:44:30+00:00" + "time": "2017-05-22T07:24:03+00:00" }, { "name": "sebastian/environment", From 6a1c7fa4a55b61d069cb57cf3c258730b0c691f2 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 23 May 2017 22:31:14 +0200 Subject: [PATCH 28/28] Require the http-client as well to ensure we use a working version --- composer.json | 1 + composer.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 36198cb..1d77caf 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "clue/block-react": "^1.1", "ocramius/package-versions": "^1.1", "react/dns": "^0.4.9", + "react/http-client": "^0.4.17", "reactivex/rxphp": "^2.0", "rx/websocket": "^1.0", "voryx/event-loop": "^2.0.2" diff --git a/composer.lock b/composer.lock index d600ff1..87f6042 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "f4cb67615dd0c45db128bd721e8b16d4", + "content-hash": "de8feac65999ba4543a43797634ec7e5", "packages": [ { "name": "api-clients/command-bus",