├── composer.json ├── LICENSE ├── index.php ├── class.php ├── javascript-websocket-example.html ├── README.md └── composer.lock /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "minimum-stability": "dev", 3 | "prefer-stable": true, 4 | "require": { 5 | "react/socket": "^1.5", 6 | "ratchet/pawl": "^0.3.5" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 executium 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | prepareConnect($host); 17 | $subscribeData = $ws->getSubscribeServer($host, $sid); 18 | 19 | function orderbook_pushed($match) 20 | { 21 | $data = json_decode($match[2], true); 22 | 23 | if($data[0] == 'dp') 24 | { 25 | $d = explode(",", trim($data[1]['d'], "[]")); 26 | $price = $d[0]; 27 | $quantity = $d[1]; 28 | $ago = $d[2]; 29 | 30 | echo "n: {$data[1]['n']}, price: {$price}, quantity: {$quantity}, time: {$ago}" . PHP_EOL; 31 | } 32 | } 33 | 34 | 35 | $loop = React\EventLoop\Factory::create(); 36 | $reactConnector = new React\Socket\Connector($loop, ['tls' => ['verify_peer'=> false, 'verify_peer_name' => false,]]); 37 | 38 | $connector = new \Ratchet\Client\Connector($loop, $reactConnector); 39 | $connector($ws->createUrl($host, $sid, ['type' => 'wss']), ['Origin' => 'https://' . $host]) 40 | ->then(function (Ratchet\Client\WebSocket $conn) use ($ws) { 41 | $conn->on('message', function ($msg) use ($conn, $ws) { 42 | $ws->debug("Received: {$msg}", 99); 43 | switch ($msg) { 44 | case '3probe': 45 | $conn->send('5'); 46 | break; 47 | case '3': 48 | $conn->send('2'); 49 | break; 50 | default: 51 | $conn->close(); 52 | } 53 | }); 54 | 55 | $conn->on('close', function ($code = null, $reason = null) use ($ws) { 56 | $ws->debug("Connection closed ({$code} - {$reason})", 99); 57 | }); 58 | 59 | $conn->send('2probe'); 60 | }, function (\Exception $e) use ($loop) { 61 | echo "Could not connect: {$e->getMessage()}\n"; 62 | $loop->stop(); 63 | }); 64 | 65 | if (empty($subscribeData) || ($subscribeData[0] ?? '') !== 'obreq') { 66 | exit("Incorrect subscribe data."); 67 | } 68 | 69 | $subscribeHost = $subscribeData[1]['n']; 70 | $subscribeSid = $ws->prepareSubscribe($subscribeHost); 71 | 72 | $subscribeConnector = new \Ratchet\Client\Connector($loop, $reactConnector); 73 | $subscribeConnector( 74 | $ws->createUrl($subscribeHost, $subscribeSid, ['type' => 'wss']), 75 | ['Origin' => 'https://' . $host] 76 | ) 77 | ->then(function (Ratchet\Client\WebSocket $conn) use ($ws, $loop, $subscribeData) { 78 | $conn->on('message', function ($msg) use ($conn, $ws) { 79 | $ws->debug("Subscribe Received: {$msg}", 99); 80 | switch ($msg) { 81 | case '3probe': 82 | $conn->send('5'); 83 | break; 84 | } 85 | 86 | if (preg_match("|(\d+)(.*)|", $msg, $match) && !empty($match[2])) 87 | { 88 | orderbook_pushed($match); 89 | } 90 | }); 91 | 92 | $conn->on('close', function ($code = null, $reason = null) use ($ws, $loop, $subscribeData) 93 | { 94 | $ws->debug("Subscribe Connection closed ({$code} - {$reason})", 99); 95 | }); 96 | 97 | $loop->addPeriodicTimer(5, function () use ($conn) { 98 | $conn->send(2); 99 | }); 100 | 101 | $conn->send('2probe'); 102 | }, function (\Exception $e) use ($loop) { 103 | echo "Could not connect: {$e->getMessage()}\n"; 104 | $loop->stop(); 105 | }); 106 | 107 | $loop->run(); 108 | -------------------------------------------------------------------------------- /class.php: -------------------------------------------------------------------------------- 1 | $this->debugLevel) { 20 | return; 21 | } 22 | if (is_array($message)) { 23 | print_r($message); 24 | 25 | return; 26 | } 27 | echo $message . "\n"; 28 | } 29 | 30 | private function requestSession($host, $sid = '', $updateCookie = false) 31 | { 32 | if ($updateCookie) { 33 | $this->cookieHeader = null; 34 | } 35 | [$header, $body] = $this->request($this->createUrl($host, $sid)); 36 | $pattern = "#Set-Cookie:\\s+(?[^=]+=[^;]+)#m"; 37 | preg_match_all($pattern, $header, $matches); 38 | $cookie = "Cookie: " . implode("; ", $matches['cookie']); 39 | if (!$this->cookieHeader) { 40 | $this->cookieHeader = $cookie; 41 | $this->debug($cookie); 42 | } 43 | 44 | return $body; 45 | } 46 | 47 | public function createUrl($host, $sid = '', $options = []) 48 | { 49 | $type = $options['type'] ?? 'https'; 50 | if ($type === 'wss') { 51 | $transport = 'websocket'; 52 | } else { 53 | $transport = 'polling'; 54 | } 55 | $url = $type . "://" . $host . ":" . $this->port . "/socket.io/?EIO=3&transport=" . $transport; 56 | if ($sid) { 57 | $url .= '&sid=' . $sid; 58 | } 59 | 60 | return $url; 61 | } 62 | 63 | private function request($url, $method = 'GET', $options = []) 64 | { 65 | $this->debug($method . ' request to ' . $url); 66 | $postData = $options['data'] ?? ''; 67 | $curlOptions = [ 68 | CURLOPT_URL => $url, 69 | CURLOPT_HEADER => true, 70 | CURLOPT_RETURNTRANSFER => true, 71 | CURLOPT_ENCODING => "", 72 | CURLOPT_MAXREDIRS => 10, 73 | CURLOPT_TIMEOUT => 0, 74 | CURLOPT_FOLLOWLOCATION => true, 75 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 76 | CURLOPT_CUSTOMREQUEST => $method, 77 | CURLOPT_USERAGENT, 78 | $this->useragent, 79 | CURLOPT_HTTPHEADER => [ 80 | $this->cookieHeader, 81 | ], 82 | ]; 83 | if ($postData) { 84 | $curlOptions[CURLOPT_POSTFIELDS] = $postData; 85 | } 86 | $curl = curl_init(); 87 | curl_setopt_array($curl, $curlOptions); 88 | $response = curl_exec($curl); 89 | curl_close($curl); 90 | 91 | return explode("\r\n\r\n", $response, 2); 92 | } 93 | 94 | private function requestPayload($host, $sid, $options) 95 | { 96 | $offset = $options['offset']; 97 | $addr = $options['address']; 98 | $data = $options['payload']; 99 | 100 | $payload = ['message', $data]; 101 | $payloadStr = $this->encode(json_encode($payload), $offset, $addr); 102 | [$header, $body] = $this->request($this->createUrl($host, $sid), "POST", ['data' => $payloadStr]); 103 | 104 | return $body; 105 | } 106 | 107 | public function encode($data, $offset, $address) 108 | { 109 | $this->debug('trying encode: ' . $data); 110 | $data = ($address + $offset) . $data; 111 | $length = strlen($data); 112 | 113 | return $length . ':' . $data; 114 | } 115 | 116 | public function decode($data) 117 | { 118 | $this->debug('trying decode: ' . $data); 119 | if (preg_match("|(\d+):(\d+)(.*)|", $data, $match)) { 120 | $length = $match[1] - 1; 121 | $str = substr($match[3], 0, $length); 122 | $offset = 0; 123 | $address = 0; 124 | if (preg_match("|(\d+):(\d+)|", substr($match[3], $length), $match)) { 125 | $offset = $match[1]; 126 | $address = $match[2]; 127 | } 128 | 129 | return [$str, $offset, $address]; 130 | } 131 | 132 | return false; 133 | } 134 | 135 | public function prepareConnect($host) 136 | { 137 | $sid = $this->requestSession($host); 138 | 139 | [$sidStr, $offset, $address] = $this->decode($sid); 140 | try { 141 | $decodedSid = json_decode($sidStr, true); 142 | } catch (Exception $ex) { 143 | $decodedSid = false; 144 | } 145 | $output = "{$this->side}-{$this->exchange}-{$this->symbol}"; 146 | $payload = [ 147 | 'req' => $this->exchange, 148 | 's' => $this->symbol, 149 | 'o' => $output, 150 | ]; 151 | if ($decodedSid) { 152 | $result = $this->requestPayload($host, $decodedSid['sid'], compact('offset', 'address', 'payload')); 153 | if ($result == 'ok') { 154 | return $decodedSid['sid']; 155 | } 156 | } 157 | 158 | return false; 159 | } 160 | 161 | public function getSubscribeServer($host, $sid) 162 | { 163 | $sid = $this->requestSession($host, $sid); 164 | [$sidStr] = $this->decode($sid); 165 | try { 166 | return json_decode($sidStr, true); 167 | } catch (Exception $ex) { 168 | } 169 | 170 | return []; 171 | } 172 | 173 | public function prepareSubscribe($host) 174 | { 175 | $sid = $this->requestSession($host, ''); 176 | [$sidStr, $offset, $address] = $this->decode($sid); 177 | try { 178 | $decodedSid = json_decode($sidStr, true); 179 | } catch (Exception $ex) { 180 | $decodedSid = false; 181 | } 182 | $payload = ["subscribe" => "{$this->side}/{$this->exchange}-{$this->symbol}-{$this->orderbook_level}"]; 183 | if ($decodedSid) { 184 | $result = $this->requestPayload($host, $decodedSid['sid'], compact('offset', 'address', 'payload')); 185 | if ($result == 'ok') { 186 | return $decodedSid['sid']; 187 | } 188 | 189 | return $decodedSid['sid']; 190 | } 191 | 192 | return false; 193 | } 194 | 195 | public function __construct($port,$side,$symbol,$exchange,$orderbook_level) 196 | { 197 | $this->port = $port; 198 | $this->side = $side; 199 | $this->symbol = $symbol; 200 | $this->exchange = $exchange; 201 | $this->orderbook_level = $orderbook_level; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /javascript-websocket-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 258 | 259 | 260 | 261 | 268 | 269 | 270 |
271 |
272 |
273 | 274 | 275 | 294 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Cryptocurrency Websocket 2 | ![crypto websocket](https://i.imgur.com/VGeP4EG.png) 3 | This PHP cryptocurrency websocket connects you to the executium cryptocurrency websocket network. This repository can be used for an array of projects where you require a live market feed. 4 | 5 | - [How to install](#how-to-install) 6 | - Variables 7 | - [Host and Port](#host-and-port) 8 | - [Side](#side) 9 | - [Symbol](#symbol) 10 | - [Exchange](#exchange) 11 | - [Orderbook Level](#orderbook-level) 12 | - [How to find out what is supported](#how-to-find-out-what-is-supported) 13 | - [Dealing with the output](#dealing-with-the-output) 14 | - [Online example](#online-example) 15 | - [Fair usage](https://github.com/executium/real-time-cryptocurrency-market-prices-websocket/blob/master/FAIR-USAGE.md) 16 | - [License](#license-related-to-code) 17 | 18 | ## How to install 19 | 1. Clone this repository 20 | 21 | ``` 22 | git clone https://github.com/executium/php-cryptocurrency-websocket.git 23 | ``` 24 | 25 | 2. The `cd` into the directory and install the dependencies using composer. 26 | ``` 27 | cd php-cryptocurrency-websocket/ 28 | composer install 29 | ``` 30 | 31 | 3. Then finally run the script 32 | ``` 33 | php index.php 34 | ``` 35 | 36 | Finally, you should see an output similar to the following: 37 | ``` 38 | GET request to https://wss-public.executium.com:2083/socket.io/?EIO=3&transport=polling 39 | Cookie: __cfduid=d344885355ebe04675bc7d57b5f84ad8e1597399433; io=YUMWpgTk5CwoXI7fAAKr 40 | trying decode: 96:0{"sid":"YUMWpgTk5CwoXI7fAAKr","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}2:40 41 | trying encode: ["message",{"req":"binance","s":"btcusdt","o":"asks-binance-btcusdt"}] 42 | POST request to https://wss-public.executium.com:2083/socket.io/?EIO=3&transport=polling&sid=YUMWpgTk5CwoXI7fAAKr 43 | GET request to https://wss-public.executium.com:2083/socket.io/?EIO=3&transport=polling&sid=YUMWpgTk5CwoXI7fAAKr 44 | trying decode: 91:42["obreq",{"n":"8e8e16d7.executium.net","s":"binance-btcusdt","o":"asks-binance-btcusdt"}] 45 | GET request to https://8e8e16d7.executium.net:2083/socket.io/?EIO=3&transport=polling 46 | trying decode: 96:0{"sid":"N6AXZMacovQWFgsXAApl","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}2:40 47 | trying encode: ["message",{"subscribe":"asks\/binance-btcusdt-1"}] 48 | POST request to https://8e8e16d7.executium.net:2083/socket.io/?EIO=3&transport=polling&sid=N6AXZMacovQWFgsXAApl 49 | n: asks/binance-btcusdt-1, price: 11694.23, quantity: 2.411781, time: 1597399435591 50 | n: asks/binance-btcusdt-1, price: 11693.5, quantity: 2.794828, time: 1597399436682 51 | n: asks/binance-btcusdt-1, price: 11693.5, quantity: 2.734814, time: 1597399437675 52 | n: asks/binance-btcusdt-1, price: 11693.5, quantity: 2.704761, time: 1597399438632 53 | n: asks/binance-btcusdt-1, price: 11693.39, quantity: 3.107546, time: 1597399439674 54 | n: asks/binance-btcusdt-1, price: 11693.39, quantity: 2.955887, time: 1597399440660 55 | ``` 56 | 57 | 58 | NOTE: This script uses `php-curl`, to install use the following command 59 | ``` 60 | apt install php-curl 61 | ``` 62 | 63 | ## How to use 64 | The folling variables are highlighted for easy customization. 65 | 66 | ```php 67 | $port = 2083; 68 | $host = 'wss-public.executium.com'; 69 | $side = "asks"; 70 | $symbol = "btcusdt"; 71 | $exchange = "binance"; 72 | $orderbook_level=1; 73 | ``` 74 | 75 | ## Host and Port 76 | As default the host for the cryptocurrency websocket is `wss-public.executium.com` on port `2083`. Cloudflare is utilized for protection. 77 | 78 | ## Side 79 | You can select `bids` or `asks` to indicate what side of the orderboook you want back. 80 | 81 | ## Symbol 82 | You can select from a range of symbols, on every exchange supported, executium will list all `tradable` symbols. 83 | 84 | ## Exchange 85 | Over `25` exchanges are supported by executium, the list and code is as follows: 86 | 87 | Exchange | Executium Code |Active | Symbols Count 88 | ------------ | ------------ | ------------ | ------------ 89 | Binance|binance|Yes|677 90 | Bitfinex|bitfinex|Yes|310 91 | Bitflyer|bitflyer|Yes|11 92 | Bithumb|bithumb|Yes|106 93 | Bitmart|bitmart|Yes|5 94 | Bitmex|bitmex|Yes|15 95 | Bitstamp|bitstamp|Yes|32 96 | Bittrex|bittrex|Yes|490 97 | Bybit|bybit|Yes|5 98 | Coinbase|coinbase|Yes|169 99 | Coinbasepro|coinbasepro|Yes|78 100 | Coincheck|coincheck|Yes|1 101 | Deribit|deribit|Yes|8 102 | Ftx|ftx|Yes|368 103 | Gateio|gateio|Yes|484 104 | Hbdm|hbdm|Yes|44 105 | Huobipro|huobipro|Yes|596 106 | Indodax|indodax|Yes|68 107 | Itbit|itbit|Yes|6 108 | Kraken|kraken|Yes|155 109 | Krakenfutures|krakenfutures|Yes|19 110 | Kucoin|kucoin|Yes|458 111 | Liquid|liquid|Yes|157 112 | Okex|okex|Yes|400 113 | Poloniex|poloniex|Yes|215 114 | Upbit|upbit|Yes|263 115 | Zb|zb|Yes|154 116 | 117 | ## Orderbook level 118 | Executium supports level 1 to 10 of the orderbook, set the `$orderbook_level` to correspond to the orderbook level you want to return. 119 | 120 | ## How to find out what is supported? 121 | You can find out what is supported by using the `symbol` endpoint as follows: 122 | 123 | ``` 124 | https://marketdata.executium.com/api/v2/system/symbols 125 | ``` 126 | 127 | If you want to break it down to exchange then you can add a `GET` parameter as follows: 128 | 129 | ``` 130 | https://marketdata.executium.com/api/v2/system/symbols?exchange=bitfinex 131 | ``` 132 | 133 | ## Dealing with the output 134 | If you are looking to capture and manipulate the output then the following passage is recommended: 135 | 136 | ```php 137 | function orderbook_pushed($match) 138 | { 139 | $data = json_decode($match[2], true); 140 | 141 | if($data[0] == 'dp') 142 | { 143 | $d = explode(",", trim($data[1]['d'], "[]")); 144 | $price = $d[0]; 145 | $quantity = $d[1]; 146 | $ago = $d[2]; 147 | echo "n: {$data[1]['n']}, price: {$price}, quantity: {$quantity}, time: {$ago}" . PHP_EOL; 148 | } 149 | } 150 | 151 | ``` 152 | 153 | When `dp` is passed this will contain the desired data. For your developments it is recommended this is the area in which you modify. Within the `orderbook_pushed()` function you can manipulate the output as you see fit. 154 | 155 | ## Online Example 156 | You can view an online example of the javascript websocket at [https://marketdata.executium.com/realtime-cryptocurrency-market-prices-websockets/](https://marketdata.executium.com/realtime-cryptocurrency-market-prices-websockets/). The code related to the javascript can be found [in this repository](https://github.com/executium/real-time-cryptocurrency-market-prices-websocket). 157 | 158 | ## License related to code 159 | 160 | MIT License 161 | 162 | Copyright (c) 2020 executium ltd 163 | 164 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 165 | 166 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 167 | 168 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 169 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "0f0897a8be03be5b7e40d50e21558bcb", 8 | "packages": [ 9 | { 10 | "name": "evenement/evenement", 11 | "version": "v3.0.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/igorw/evenement.git", 15 | "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", 20 | "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=7.0" 25 | }, 26 | "require-dev": { 27 | "phpunit/phpunit": "^6.0" 28 | }, 29 | "type": "library", 30 | "autoload": { 31 | "psr-0": { 32 | "Evenement": "src" 33 | } 34 | }, 35 | "notification-url": "https://packagist.org/downloads/", 36 | "license": [ 37 | "MIT" 38 | ], 39 | "authors": [ 40 | { 41 | "name": "Igor Wiedler", 42 | "email": "igor@wiedler.ch" 43 | } 44 | ], 45 | "description": "Événement is a very simple event dispatching library for PHP", 46 | "keywords": [ 47 | "event-dispatcher", 48 | "event-emitter" 49 | ], 50 | "time": "2017-07-23T21:35:13+00:00" 51 | }, 52 | { 53 | "name": "guzzlehttp/psr7", 54 | "version": "1.6.1", 55 | "source": { 56 | "type": "git", 57 | "url": "https://github.com/guzzle/psr7.git", 58 | "reference": "239400de7a173fe9901b9ac7c06497751f00727a" 59 | }, 60 | "dist": { 61 | "type": "zip", 62 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", 63 | "reference": "239400de7a173fe9901b9ac7c06497751f00727a", 64 | "shasum": "" 65 | }, 66 | "require": { 67 | "php": ">=5.4.0", 68 | "psr/http-message": "~1.0", 69 | "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" 70 | }, 71 | "provide": { 72 | "psr/http-message-implementation": "1.0" 73 | }, 74 | "require-dev": { 75 | "ext-zlib": "*", 76 | "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" 77 | }, 78 | "suggest": { 79 | "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" 80 | }, 81 | "type": "library", 82 | "extra": { 83 | "branch-alias": { 84 | "dev-master": "1.6-dev" 85 | } 86 | }, 87 | "autoload": { 88 | "psr-4": { 89 | "GuzzleHttp\\Psr7\\": "src/" 90 | }, 91 | "files": [ 92 | "src/functions_include.php" 93 | ] 94 | }, 95 | "notification-url": "https://packagist.org/downloads/", 96 | "license": [ 97 | "MIT" 98 | ], 99 | "authors": [ 100 | { 101 | "name": "Michael Dowling", 102 | "email": "mtdowling@gmail.com", 103 | "homepage": "https://github.com/mtdowling" 104 | }, 105 | { 106 | "name": "Tobias Schultze", 107 | "homepage": "https://github.com/Tobion" 108 | } 109 | ], 110 | "description": "PSR-7 message implementation that also provides common utility methods", 111 | "keywords": [ 112 | "http", 113 | "message", 114 | "psr-7", 115 | "request", 116 | "response", 117 | "stream", 118 | "uri", 119 | "url" 120 | ], 121 | "time": "2019-07-01T23:21:34+00:00" 122 | }, 123 | { 124 | "name": "psr/http-message", 125 | "version": "1.0.1", 126 | "source": { 127 | "type": "git", 128 | "url": "https://github.com/php-fig/http-message.git", 129 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 130 | }, 131 | "dist": { 132 | "type": "zip", 133 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 134 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 135 | "shasum": "" 136 | }, 137 | "require": { 138 | "php": ">=5.3.0" 139 | }, 140 | "type": "library", 141 | "extra": { 142 | "branch-alias": { 143 | "dev-master": "1.0.x-dev" 144 | } 145 | }, 146 | "autoload": { 147 | "psr-4": { 148 | "Psr\\Http\\Message\\": "src/" 149 | } 150 | }, 151 | "notification-url": "https://packagist.org/downloads/", 152 | "license": [ 153 | "MIT" 154 | ], 155 | "authors": [ 156 | { 157 | "name": "PHP-FIG", 158 | "homepage": "http://www.php-fig.org/" 159 | } 160 | ], 161 | "description": "Common interface for HTTP messages", 162 | "homepage": "https://github.com/php-fig/http-message", 163 | "keywords": [ 164 | "http", 165 | "http-message", 166 | "psr", 167 | "psr-7", 168 | "request", 169 | "response" 170 | ], 171 | "time": "2016-08-06T14:39:51+00:00" 172 | }, 173 | { 174 | "name": "ralouphie/getallheaders", 175 | "version": "3.0.3", 176 | "source": { 177 | "type": "git", 178 | "url": "https://github.com/ralouphie/getallheaders.git", 179 | "reference": "120b605dfeb996808c31b6477290a714d356e822" 180 | }, 181 | "dist": { 182 | "type": "zip", 183 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", 184 | "reference": "120b605dfeb996808c31b6477290a714d356e822", 185 | "shasum": "" 186 | }, 187 | "require": { 188 | "php": ">=5.6" 189 | }, 190 | "require-dev": { 191 | "php-coveralls/php-coveralls": "^2.1", 192 | "phpunit/phpunit": "^5 || ^6.5" 193 | }, 194 | "type": "library", 195 | "autoload": { 196 | "files": [ 197 | "src/getallheaders.php" 198 | ] 199 | }, 200 | "notification-url": "https://packagist.org/downloads/", 201 | "license": [ 202 | "MIT" 203 | ], 204 | "authors": [ 205 | { 206 | "name": "Ralph Khattar", 207 | "email": "ralph.khattar@gmail.com" 208 | } 209 | ], 210 | "description": "A polyfill for getallheaders.", 211 | "time": "2019-03-08T08:55:37+00:00" 212 | }, 213 | { 214 | "name": "ratchet/pawl", 215 | "version": "v0.3.5", 216 | "source": { 217 | "type": "git", 218 | "url": "https://github.com/ratchetphp/Pawl.git", 219 | "reference": "89ec703c76dc893484a2a0ed44b48a37d445abd5" 220 | }, 221 | "dist": { 222 | "type": "zip", 223 | "url": "https://api.github.com/repos/ratchetphp/Pawl/zipball/89ec703c76dc893484a2a0ed44b48a37d445abd5", 224 | "reference": "89ec703c76dc893484a2a0ed44b48a37d445abd5", 225 | "shasum": "" 226 | }, 227 | "require": { 228 | "evenement/evenement": "^3.0 || ^2.0", 229 | "php": ">=5.4", 230 | "ratchet/rfc6455": "^0.3", 231 | "react/socket": "^1.0 || ^0.8 || ^0.7" 232 | }, 233 | "require-dev": { 234 | "phpunit/phpunit": "~4.8" 235 | }, 236 | "suggest": { 237 | "reactivex/rxphp": "~2.0" 238 | }, 239 | "type": "library", 240 | "autoload": { 241 | "psr-4": { 242 | "Ratchet\\Client\\": "src" 243 | }, 244 | "files": [ 245 | "src/functions_include.php" 246 | ] 247 | }, 248 | "notification-url": "https://packagist.org/downloads/", 249 | "license": [ 250 | "MIT" 251 | ], 252 | "description": "Asynchronous WebSocket client", 253 | "keywords": [ 254 | "Ratchet", 255 | "async", 256 | "client", 257 | "websocket", 258 | "websocket client" 259 | ], 260 | "time": "2020-07-17T15:32:47+00:00" 261 | }, 262 | { 263 | "name": "ratchet/rfc6455", 264 | "version": "v0.3", 265 | "source": { 266 | "type": "git", 267 | "url": "https://github.com/ratchetphp/RFC6455.git", 268 | "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341" 269 | }, 270 | "dist": { 271 | "type": "zip", 272 | "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/c8651c7938651c2d55f5d8c2422ac5e57a183341", 273 | "reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341", 274 | "shasum": "" 275 | }, 276 | "require": { 277 | "guzzlehttp/psr7": "^1.0", 278 | "php": ">=5.4.2" 279 | }, 280 | "require-dev": { 281 | "phpunit/phpunit": "5.7.*", 282 | "react/socket": "^1.3" 283 | }, 284 | "type": "library", 285 | "autoload": { 286 | "psr-4": { 287 | "Ratchet\\RFC6455\\": "src" 288 | } 289 | }, 290 | "notification-url": "https://packagist.org/downloads/", 291 | "license": [ 292 | "MIT" 293 | ], 294 | "authors": [ 295 | { 296 | "name": "Chris Boden", 297 | "email": "cboden@gmail.com", 298 | "role": "Developer" 299 | }, 300 | { 301 | "name": "Matt Bonneau", 302 | "role": "Developer" 303 | } 304 | ], 305 | "description": "RFC6455 WebSocket protocol handler", 306 | "homepage": "http://socketo.me", 307 | "keywords": [ 308 | "WebSockets", 309 | "rfc6455", 310 | "websocket" 311 | ], 312 | "time": "2020-05-15T18:31:24+00:00" 313 | }, 314 | { 315 | "name": "react/cache", 316 | "version": "v1.0.0", 317 | "source": { 318 | "type": "git", 319 | "url": "https://github.com/reactphp/cache.git", 320 | "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466" 321 | }, 322 | "dist": { 323 | "type": "zip", 324 | "url": "https://api.github.com/repos/reactphp/cache/zipball/aa10d63a1b40a36a486bdf527f28bac607ee6466", 325 | "reference": "aa10d63a1b40a36a486bdf527f28bac607ee6466", 326 | "shasum": "" 327 | }, 328 | "require": { 329 | "php": ">=5.3.0", 330 | "react/promise": "~2.0|~1.1" 331 | }, 332 | "require-dev": { 333 | "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" 334 | }, 335 | "type": "library", 336 | "autoload": { 337 | "psr-4": { 338 | "React\\Cache\\": "src/" 339 | } 340 | }, 341 | "notification-url": "https://packagist.org/downloads/", 342 | "license": [ 343 | "MIT" 344 | ], 345 | "description": "Async, Promise-based cache interface for ReactPHP", 346 | "keywords": [ 347 | "cache", 348 | "caching", 349 | "promise", 350 | "reactphp" 351 | ], 352 | "time": "2019-07-11T13:45:28+00:00" 353 | }, 354 | { 355 | "name": "react/dns", 356 | "version": "v1.3.0", 357 | "source": { 358 | "type": "git", 359 | "url": "https://github.com/reactphp/dns.git", 360 | "reference": "89d83794e959ef3e0f1ab792f070b0157de1abf2" 361 | }, 362 | "dist": { 363 | "type": "zip", 364 | "url": "https://api.github.com/repos/reactphp/dns/zipball/89d83794e959ef3e0f1ab792f070b0157de1abf2", 365 | "reference": "89d83794e959ef3e0f1ab792f070b0157de1abf2", 366 | "shasum": "" 367 | }, 368 | "require": { 369 | "php": ">=5.3.0", 370 | "react/cache": "^1.0 || ^0.6 || ^0.5", 371 | "react/event-loop": "^1.0 || ^0.5", 372 | "react/promise": "^3.0 || ^2.7 || ^1.2.1", 373 | "react/promise-timer": "^1.2" 374 | }, 375 | "require-dev": { 376 | "clue/block-react": "^1.2", 377 | "phpunit/phpunit": "^9.0 || ^4.8.35" 378 | }, 379 | "type": "library", 380 | "autoload": { 381 | "psr-4": { 382 | "React\\Dns\\": "src" 383 | } 384 | }, 385 | "notification-url": "https://packagist.org/downloads/", 386 | "license": [ 387 | "MIT" 388 | ], 389 | "description": "Async DNS resolver for ReactPHP", 390 | "keywords": [ 391 | "async", 392 | "dns", 393 | "dns-resolver", 394 | "reactphp" 395 | ], 396 | "time": "2020-07-10T12:12:50+00:00" 397 | }, 398 | { 399 | "name": "react/event-loop", 400 | "version": "v1.1.1", 401 | "source": { 402 | "type": "git", 403 | "url": "https://github.com/reactphp/event-loop.git", 404 | "reference": "6d24de090cd59cfc830263cfba965be77b563c13" 405 | }, 406 | "dist": { 407 | "type": "zip", 408 | "url": "https://api.github.com/repos/reactphp/event-loop/zipball/6d24de090cd59cfc830263cfba965be77b563c13", 409 | "reference": "6d24de090cd59cfc830263cfba965be77b563c13", 410 | "shasum": "" 411 | }, 412 | "require": { 413 | "php": ">=5.3.0" 414 | }, 415 | "require-dev": { 416 | "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" 417 | }, 418 | "suggest": { 419 | "ext-event": "~1.0 for ExtEventLoop", 420 | "ext-pcntl": "For signal handling support when using the StreamSelectLoop", 421 | "ext-uv": "* for ExtUvLoop" 422 | }, 423 | "type": "library", 424 | "autoload": { 425 | "psr-4": { 426 | "React\\EventLoop\\": "src" 427 | } 428 | }, 429 | "notification-url": "https://packagist.org/downloads/", 430 | "license": [ 431 | "MIT" 432 | ], 433 | "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", 434 | "keywords": [ 435 | "asynchronous", 436 | "event-loop" 437 | ], 438 | "time": "2020-01-01T18:39:52+00:00" 439 | }, 440 | { 441 | "name": "react/promise", 442 | "version": "v2.8.0", 443 | "source": { 444 | "type": "git", 445 | "url": "https://github.com/reactphp/promise.git", 446 | "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4" 447 | }, 448 | "dist": { 449 | "type": "zip", 450 | "url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4", 451 | "reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4", 452 | "shasum": "" 453 | }, 454 | "require": { 455 | "php": ">=5.4.0" 456 | }, 457 | "require-dev": { 458 | "phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36" 459 | }, 460 | "type": "library", 461 | "autoload": { 462 | "psr-4": { 463 | "React\\Promise\\": "src/" 464 | }, 465 | "files": [ 466 | "src/functions_include.php" 467 | ] 468 | }, 469 | "notification-url": "https://packagist.org/downloads/", 470 | "license": [ 471 | "MIT" 472 | ], 473 | "authors": [ 474 | { 475 | "name": "Jan Sorgalla", 476 | "email": "jsorgalla@gmail.com" 477 | } 478 | ], 479 | "description": "A lightweight implementation of CommonJS Promises/A for PHP", 480 | "keywords": [ 481 | "promise", 482 | "promises" 483 | ], 484 | "time": "2020-05-12T15:16:56+00:00" 485 | }, 486 | { 487 | "name": "react/promise-timer", 488 | "version": "v1.6.0", 489 | "source": { 490 | "type": "git", 491 | "url": "https://github.com/reactphp/promise-timer.git", 492 | "reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6" 493 | }, 494 | "dist": { 495 | "type": "zip", 496 | "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/daee9baf6ef30c43ea4c86399f828bb5f558f6e6", 497 | "reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6", 498 | "shasum": "" 499 | }, 500 | "require": { 501 | "php": ">=5.3", 502 | "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", 503 | "react/promise": "^3.0 || ^2.7.0 || ^1.2.1" 504 | }, 505 | "require-dev": { 506 | "phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35" 507 | }, 508 | "type": "library", 509 | "autoload": { 510 | "psr-4": { 511 | "React\\Promise\\Timer\\": "src/" 512 | }, 513 | "files": [ 514 | "src/functions_include.php" 515 | ] 516 | }, 517 | "notification-url": "https://packagist.org/downloads/", 518 | "license": [ 519 | "MIT" 520 | ], 521 | "authors": [ 522 | { 523 | "name": "Christian Lück", 524 | "email": "christian@lueck.tv" 525 | } 526 | ], 527 | "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", 528 | "homepage": "https://github.com/reactphp/promise-timer", 529 | "keywords": [ 530 | "async", 531 | "event-loop", 532 | "promise", 533 | "reactphp", 534 | "timeout", 535 | "timer" 536 | ], 537 | "time": "2020-07-10T12:18:06+00:00" 538 | }, 539 | { 540 | "name": "react/socket", 541 | "version": "v1.5.0", 542 | "source": { 543 | "type": "git", 544 | "url": "https://github.com/reactphp/socket.git", 545 | "reference": "842dcd71df86671ee9491734035b3d2cf4a80ece" 546 | }, 547 | "dist": { 548 | "type": "zip", 549 | "url": "https://api.github.com/repos/reactphp/socket/zipball/842dcd71df86671ee9491734035b3d2cf4a80ece", 550 | "reference": "842dcd71df86671ee9491734035b3d2cf4a80ece", 551 | "shasum": "" 552 | }, 553 | "require": { 554 | "evenement/evenement": "^3.0 || ^2.0 || ^1.0", 555 | "php": ">=5.3.0", 556 | "react/dns": "^1.1", 557 | "react/event-loop": "^1.0 || ^0.5", 558 | "react/promise": "^2.6.0 || ^1.2.1", 559 | "react/promise-timer": "^1.4.0", 560 | "react/stream": "^1.1" 561 | }, 562 | "require-dev": { 563 | "clue/block-react": "^1.2", 564 | "phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35", 565 | "react/promise-stream": "^1.2" 566 | }, 567 | "type": "library", 568 | "autoload": { 569 | "psr-4": { 570 | "React\\Socket\\": "src" 571 | } 572 | }, 573 | "notification-url": "https://packagist.org/downloads/", 574 | "license": [ 575 | "MIT" 576 | ], 577 | "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", 578 | "keywords": [ 579 | "Connection", 580 | "Socket", 581 | "async", 582 | "reactphp", 583 | "stream" 584 | ], 585 | "time": "2020-07-01T12:50:00+00:00" 586 | }, 587 | { 588 | "name": "react/stream", 589 | "version": "v1.1.1", 590 | "source": { 591 | "type": "git", 592 | "url": "https://github.com/reactphp/stream.git", 593 | "reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a" 594 | }, 595 | "dist": { 596 | "type": "zip", 597 | "url": "https://api.github.com/repos/reactphp/stream/zipball/7c02b510ee3f582c810aeccd3a197b9c2f52ff1a", 598 | "reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a", 599 | "shasum": "" 600 | }, 601 | "require": { 602 | "evenement/evenement": "^3.0 || ^2.0 || ^1.0", 603 | "php": ">=5.3.8", 604 | "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5" 605 | }, 606 | "require-dev": { 607 | "clue/stream-filter": "~1.2", 608 | "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" 609 | }, 610 | "type": "library", 611 | "autoload": { 612 | "psr-4": { 613 | "React\\Stream\\": "src" 614 | } 615 | }, 616 | "notification-url": "https://packagist.org/downloads/", 617 | "license": [ 618 | "MIT" 619 | ], 620 | "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", 621 | "keywords": [ 622 | "event-driven", 623 | "io", 624 | "non-blocking", 625 | "pipe", 626 | "reactphp", 627 | "readable", 628 | "stream", 629 | "writable" 630 | ], 631 | "time": "2020-05-04T10:17:57+00:00" 632 | } 633 | ], 634 | "packages-dev": [], 635 | "aliases": [], 636 | "minimum-stability": "dev", 637 | "stability-flags": [], 638 | "prefer-stable": true, 639 | "prefer-lowest": false, 640 | "platform": [], 641 | "platform-dev": [], 642 | "plugin-api-version": "1.1.0" 643 | } 644 | --------------------------------------------------------------------------------