Passed
Push — master ( beaa4b...da3f2a )
by Shahrad
02:02
created
examples/websoket/coinmarketcap-ticker.php 2 patches
Indentation   +29 added lines, -29 removed lines patch added patch discarded remove patch
@@ -13,41 +13,41 @@
 block discarded – undo
13 13
  */
14 14
 function startTicker(array $ids, callable $callback): void
15 15
 {
16
-	try {
17
-
18
-		$close_time = time() + 10;
19
-		$ClientConfig = (new WSConfig())->setFragmentSize(8096)->setTimeout(15);
20
-		$WebSocketClient = new WebSocket('wss://stream.coinmarketcap.com/price/latest', $ClientConfig);
21
-
22
-		$WebSocketClient->send(json_encode([
23
-			'method' => 'subscribe',
24
-			'id' => 'price',
25
-			'data' => [
26
-				'cryptoIds' => $ids,
27
-				'index' => 'detail'
28
-			]
29
-		]));
30
-
31
-		while ($close_time > time()) {
32
-			if (($message = $WebSocketClient->receive()) != "") {
33
-				$json_response = json_decode($message, true);
34
-
35
-				if ($json_response['id'] == "price") {
36
-					$callback($json_response);
37
-				}
38
-			}
39
-		}
40
-
41
-	} catch (Exception $e) {
42
-		echo "<b>Error</b>: " . $e->getMessage();
43
-	}
16
+    try {
17
+
18
+        $close_time = time() + 10;
19
+        $ClientConfig = (new WSConfig())->setFragmentSize(8096)->setTimeout(15);
20
+        $WebSocketClient = new WebSocket('wss://stream.coinmarketcap.com/price/latest', $ClientConfig);
21
+
22
+        $WebSocketClient->send(json_encode([
23
+            'method' => 'subscribe',
24
+            'id' => 'price',
25
+            'data' => [
26
+                'cryptoIds' => $ids,
27
+                'index' => 'detail'
28
+            ]
29
+        ]));
30
+
31
+        while ($close_time > time()) {
32
+            if (($message = $WebSocketClient->receive()) != "") {
33
+                $json_response = json_decode($message, true);
34
+
35
+                if ($json_response['id'] == "price") {
36
+                    $callback($json_response);
37
+                }
38
+            }
39
+        }
40
+
41
+    } catch (Exception $e) {
42
+        echo "<b>Error</b>: " . $e->getMessage();
43
+    }
44 44
 }
45 45
 
46 46
 echo "<pre>Start Tome: " . date('Y-m-d H:i:s') . "</pre><br/>";
47 47
 
48 48
 $responses = [];
49 49
 startTicker([1, 1027, 825, 3408, 1839, 4687, 52, 2010, 5426], function ($data) use (&$responses) {
50
-	$responses[] = $data;
50
+    $responses[] = $data;
51 51
 });
52 52
 
53 53
 echo "<pre>" . json_encode($responses, JSON_PRETTY_PRINT) . "</pre><br/>";
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -46,7 +46,7 @@
 block discarded – undo
46 46
 echo "<pre>Start Tome: " . date('Y-m-d H:i:s') . "</pre><br/>";
47 47
 
48 48
 $responses = [];
49
-startTicker([1, 1027, 825, 3408, 1839, 4687, 52, 2010, 5426], function ($data) use (&$responses) {
49
+startTicker([1, 1027, 825, 3408, 1839, 4687, 52, 2010, 5426], function($data) use (&$responses) {
50 50
 	$responses[] = $data;
51 51
 });
52 52
 
Please login to merge, or discard this patch.
src/Contracts/CommonsContract.php 1 patch
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -12,57 +12,57 @@
 block discarded – undo
12 12
 interface CommonsContract
13 13
 {
14 14
 
15
-	// DADA types
16
-	public const EVENT_TYPE_PING = 'ping';
17
-	public const EVENT_TYPE_PONG = 'pong';
18
-	public const EVENT_TYPE_TEXT = 'text';
19
-	public const EVENT_TYPE_CLOSE = 'close';
20
-	public const EVENT_TYPE_BINARY = 'binary';
21
-	public const EVENT_TYPE_CONTINUATION = 'continuation';
15
+    // DADA types
16
+    public const EVENT_TYPE_PING = 'ping';
17
+    public const EVENT_TYPE_PONG = 'pong';
18
+    public const EVENT_TYPE_TEXT = 'text';
19
+    public const EVENT_TYPE_CLOSE = 'close';
20
+    public const EVENT_TYPE_BINARY = 'binary';
21
+    public const EVENT_TYPE_CONTINUATION = 'continuation';
22 22
 
23
-	public const MAP_EVENT_TYPE_TO_METHODS = [
24
-		self::EVENT_TYPE_TEXT => 'onMessage',
25
-		self::EVENT_TYPE_PING => 'onPing',
26
-		self::EVENT_TYPE_PONG => 'onPong',
27
-	];
23
+    public const MAP_EVENT_TYPE_TO_METHODS = [
24
+        self::EVENT_TYPE_TEXT => 'onMessage',
25
+        self::EVENT_TYPE_PING => 'onPing',
26
+        self::EVENT_TYPE_PONG => 'onPong',
27
+    ];
28 28
 
29
-	// DECODE FRAMES
30
-	public const DECODE_TEXT = 1;
31
-	public const DECODE_BINARY = 2;
32
-	public const DECODE_CLOSE = 8;
33
-	public const DECODE_PING = 9;
34
-	public const DECODE_PONG = 10;
29
+    // DECODE FRAMES
30
+    public const DECODE_TEXT = 1;
31
+    public const DECODE_BINARY = 2;
32
+    public const DECODE_CLOSE = 8;
33
+    public const DECODE_PING = 9;
34
+    public const DECODE_PONG = 10;
35 35
 
36
-	// ENCODE FRAMES
37
-	public const ENCODE_TEXT = 129;
38
-	public const ENCODE_CLOSE = 136;
39
-	public const ENCODE_PING = 137;
40
-	public const ENCODE_PONG = 138;
36
+    // ENCODE FRAMES
37
+    public const ENCODE_TEXT = 129;
38
+    public const ENCODE_CLOSE = 136;
39
+    public const ENCODE_PING = 137;
40
+    public const ENCODE_PONG = 138;
41 41
 
42
-	// MASKS
43
-	public const MASK_125 = 125;
44
-	public const MASK_126 = 126;
45
-	public const MASK_127 = 127;
46
-	public const MASK_128 = 128;
47
-	public const MASK_254 = 254;
48
-	public const MASK_255 = 255;
42
+    // MASKS
43
+    public const MASK_125 = 125;
44
+    public const MASK_126 = 126;
45
+    public const MASK_127 = 127;
46
+    public const MASK_128 = 128;
47
+    public const MASK_254 = 254;
48
+    public const MASK_255 = 255;
49 49
 
50
-	// PAYLOADS
51
-	public const PAYLOAD_CHUNK = 8;
52
-	public const PAYLOAD_MAX_BITS = 65535;
50
+    // PAYLOADS
51
+    public const PAYLOAD_CHUNK = 8;
52
+    public const PAYLOAD_MAX_BITS = 65535;
53 53
 
54
-	// transfer protocol-level errors
55
-	public const SERVER_COULD_NOT_BIND_TO_SOCKET = 101;
56
-	public const SERVER_SELECT_ERROR = 102;
57
-	public const SERVER_HEADERS_NOT_SET = 103;
58
-	public const CLIENT_COULD_NOT_OPEN_SOCKET = 104;
59
-	public const CLIENT_INCORRECT_SCHEME = 105;
60
-	public const CLIENT_INVALID_UPGRADE_RESPONSE = 106;
61
-	public const CLIENT_INVALID_STREAM_CONTEXT = 107;
62
-	public const CLIENT_BAD_OPCODE = 108;
63
-	public const CLIENT_COULD_ONLY_WRITE_LESS = 109;
64
-	public const CLIENT_BROKEN_FRAME = 110;
65
-	public const CLIENT_EMPTY_READ = 111;
66
-	public const SERVER_INVALID_STREAM_CONTEXT = 112;
54
+    // transfer protocol-level errors
55
+    public const SERVER_COULD_NOT_BIND_TO_SOCKET = 101;
56
+    public const SERVER_SELECT_ERROR = 102;
57
+    public const SERVER_HEADERS_NOT_SET = 103;
58
+    public const CLIENT_COULD_NOT_OPEN_SOCKET = 104;
59
+    public const CLIENT_INCORRECT_SCHEME = 105;
60
+    public const CLIENT_INVALID_UPGRADE_RESPONSE = 106;
61
+    public const CLIENT_INVALID_STREAM_CONTEXT = 107;
62
+    public const CLIENT_BAD_OPCODE = 108;
63
+    public const CLIENT_COULD_ONLY_WRITE_LESS = 109;
64
+    public const CLIENT_BROKEN_FRAME = 110;
65
+    public const CLIENT_EMPTY_READ = 111;
66
+    public const SERVER_INVALID_STREAM_CONTEXT = 112;
67 67
 
68 68
 }
69 69
\ No newline at end of file
Please login to merge, or discard this patch.
src/Contracts/ConnectionContract.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -12,16 +12,16 @@
 block discarded – undo
12 12
 interface ConnectionContract
13 13
 {
14 14
 
15
-	public function send(string $data): void;
15
+    public function send(string $data): void;
16 16
 
17
-	public function close(): void;
17
+    public function close(): void;
18 18
 
19
-	public function getUniqueSocketId(): int;
19
+    public function getUniqueSocketId(): int;
20 20
 
21
-	public function getPeerName(): string;
21
+    public function getPeerName(): string;
22 22
 
23
-	public function broadCast(string $data): void;
23
+    public function broadCast(string $data): void;
24 24
 
25
-	public function broadCastMany(array $data, int $delay): void;
25
+    public function broadCastMany(array $data, int $delay): void;
26 26
 
27 27
 }
Please login to merge, or discard this patch.
src/Contracts/WscCommonsContract.php 1 patch
Indentation   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -12,24 +12,24 @@
 block discarded – undo
12 12
 interface WscCommonsContract
13 13
 {
14 14
 
15
-	public const TCP_SCHEME = 'tcp://';
15
+    public const TCP_SCHEME = 'tcp://';
16 16
 
17
-	public const MAX_BYTES_READ = 65535;
18
-	public const DEFAULT_TIMEOUT = 5;
19
-	public const DEFAULT_FRAGMENT_SIZE = 4096;
20
-	public const DEFAULT_RESPONSE_HEADER = 8192;
21
-	public const SEC_WEBSOCKET_ACCEPT_PTTRN = '/Sec-WebSocket-Accept:\s(.*)$/mUi';
22
-	public const SERVER_KEY_ACCEPT = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
23
-	public const PROXY_MATCH_RESP = '/^HTTP\/\d\.\d 200/';
17
+    public const MAX_BYTES_READ = 65535;
18
+    public const DEFAULT_TIMEOUT = 5;
19
+    public const DEFAULT_FRAGMENT_SIZE = 4096;
20
+    public const DEFAULT_RESPONSE_HEADER = 8192;
21
+    public const SEC_WEBSOCKET_ACCEPT_PTTRN = '/Sec-WebSocket-Accept:\s(.*)$/mUi';
22
+    public const SERVER_KEY_ACCEPT = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
23
+    public const PROXY_MATCH_RESP = '/^HTTP\/\d\.\d 200/';
24 24
 
25
-	// MASKS
26
-	public const MASK_125 = 125;
27
-	public const MASK_126 = 126;
28
-	public const MASK_127 = 127;
29
-	public const MASK_128 = 128;
30
-	public const MASK_254 = 254;
31
-	public const MASK_255 = 255;
25
+    // MASKS
26
+    public const MASK_125 = 125;
27
+    public const MASK_126 = 126;
28
+    public const MASK_127 = 127;
29
+    public const MASK_128 = 128;
30
+    public const MASK_254 = 254;
31
+    public const MASK_255 = 255;
32 32
 
33
-	public const KEY_GEN_LENGTH = 16;
33
+    public const KEY_GEN_LENGTH = 16;
34 34
 
35 35
 }
Please login to merge, or discard this patch.
src/Contracts/MessageContract.php 1 patch
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -14,12 +14,12 @@
 block discarded – undo
14 14
 interface MessageContract
15 15
 {
16 16
 
17
-	/**
18
-	 * @param ConnectionContract $recv
19
-	 * @param mixed $msg
20
-	 * @return mixed
21
-	 * @throws WebSocketException
22
-	 */
23
-	public function onMessage(ConnectionContract $recv, mixed $msg): mixed;
17
+    /**
18
+     * @param ConnectionContract $recv
19
+     * @param mixed $msg
20
+     * @return mixed
21
+     * @throws WebSocketException
22
+     */
23
+    public function onMessage(ConnectionContract $recv, mixed $msg): mixed;
24 24
 
25 25
 }
Please login to merge, or discard this patch.
src/Contracts/WebSocketContract.php 1 patch
Indentation   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -14,24 +14,24 @@
 block discarded – undo
14 14
 interface WebSocketContract
15 15
 {
16 16
 
17
-	/**
18
-	 * @param ConnectionContract $conn
19
-	 * @return mixed
20
-	 */
21
-	public function onOpen(ConnectionContract $conn): mixed;
17
+    /**
18
+     * @param ConnectionContract $conn
19
+     * @return mixed
20
+     */
21
+    public function onOpen(ConnectionContract $conn): mixed;
22 22
 
23
-	/**
24
-	 * @param ConnectionContract $conn
25
-	 * @return mixed
26
-	 * @throws WebSocketException
27
-	 */
28
-	public function onClose(ConnectionContract $conn): mixed;
23
+    /**
24
+     * @param ConnectionContract $conn
25
+     * @return mixed
26
+     * @throws WebSocketException
27
+     */
28
+    public function onClose(ConnectionContract $conn): mixed;
29 29
 
30
-	/**
31
-	 * @param ConnectionContract $conn
32
-	 * @param WebSocketException $ex
33
-	 * @return mixed
34
-	 */
35
-	public function onError(ConnectionContract $conn, WebSocketException $ex): mixed;
30
+    /**
31
+     * @param ConnectionContract $conn
32
+     * @param WebSocketException $ex
33
+     * @return mixed
34
+     */
35
+    public function onError(ConnectionContract $conn, WebSocketException $ex): mixed;
36 36
 
37 37
 }
Please login to merge, or discard this patch.
src/WebSocket.php 1 patch
Indentation   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -16,17 +16,17 @@
 block discarded – undo
16 16
 class WebSocket extends WscMain
17 17
 {
18 18
 
19
-	/**
20
-	 * Sets parameters for Web Socket Client intercommunication
21
-	 *
22
-	 * @param string $url string representation of a socket utf, ex.: tcp://www.example.com:8000 or udp://example.com:13
23
-	 * @param WSConfig $config Client configuration settings e.g.: connection - timeout, ssl options, fragment message size to send etc.
24
-	 * @throws Exception
25
-	 */
26
-	public function __construct(string $url, WSConfig $config)
27
-	{
28
-		$this->socketUrl = $url;
29
-		$this->connect($config);
30
-	}
19
+    /**
20
+     * Sets parameters for Web Socket Client intercommunication
21
+     *
22
+     * @param string $url string representation of a socket utf, ex.: tcp://www.example.com:8000 or udp://example.com:13
23
+     * @param WSConfig $config Client configuration settings e.g.: connection - timeout, ssl options, fragment message size to send etc.
24
+     * @throws Exception
25
+     */
26
+    public function __construct(string $url, WSConfig $config)
27
+    {
28
+        $this->socketUrl = $url;
29
+        $this->connect($config);
30
+    }
31 31
 
32 32
 }
Please login to merge, or discard this patch.
src/Utils/WSConfig.php 1 patch
Indentation   +308 added lines, -308 removed lines patch added patch discarded remove patch
@@ -14,313 +14,313 @@
 block discarded – undo
14 14
 class WSConfig
15 15
 {
16 16
 
17
-	/**
18
-	 * @var string
19
-	 */
20
-	private string $scheme;
21
-
22
-	/**
23
-	 * @var string
24
-	 */
25
-	private string $host;
26
-
27
-	/**
28
-	 * @var string
29
-	 */
30
-	private string $user;
31
-
32
-	/**
33
-	 * @var string
34
-	 */
35
-	private string $password;
36
-
37
-	/**
38
-	 * @var string
39
-	 */
40
-	private string $port;
41
-
42
-	/**
43
-	 * @var int
44
-	 */
45
-	private int $timeout = WscCommonsContract::DEFAULT_TIMEOUT;
46
-
47
-	/**
48
-	 * @var array
49
-	 */
50
-	private array $headers = [];
51
-
52
-	/**
53
-	 * @var int
54
-	 */
55
-	private int $fragmentSize = WscCommonsContract::DEFAULT_FRAGMENT_SIZE;
56
-
57
-	/**
58
-	 * @var null|resource
59
-	 */
60
-	private $context;
61
-
62
-	/**
63
-	 * @var bool
64
-	 */
65
-	private bool $hasProxy = false;
66
-
67
-	/**
68
-	 * @var string
69
-	 */
70
-	private string $proxyIp;
71
-
72
-	/**
73
-	 * @var string
74
-	 */
75
-	private string $proxyPort;
76
-
77
-	/**
78
-	 * @var string|null
79
-	 */
80
-	private ?string $proxyAuth;
81
-
82
-	/**
83
-	 * @var array
84
-	 */
85
-	private array $contextOptions = [];
86
-
87
-	/**
88
-	 * @return int
89
-	 */
90
-	public function getTimeout(): int
91
-	{
92
-		return $this->timeout;
93
-	}
94
-
95
-	/**
96
-	 * @param int $timeout
97
-	 * @return WSConfig
98
-	 */
99
-	public function setTimeout(int $timeout): WSConfig
100
-	{
101
-		$this->timeout = $timeout;
102
-		return $this;
103
-	}
104
-
105
-	/**
106
-	 * @return array
107
-	 */
108
-	public function getHeaders(): array
109
-	{
110
-		return $this->headers;
111
-	}
112
-
113
-	/**
114
-	 * @param array $headers
115
-	 * @return WSConfig
116
-	 */
117
-	public function setHeaders(array $headers): WSConfig
118
-	{
119
-		$this->headers = $headers;
120
-		return $this;
121
-	}
122
-
123
-	/**
124
-	 * @return int
125
-	 */
126
-	public function getFragmentSize(): int
127
-	{
128
-		return $this->fragmentSize;
129
-	}
130
-
131
-	/**
132
-	 * @param int $fragmentSize
133
-	 * @return WSConfig
134
-	 */
135
-	public function setFragmentSize(int $fragmentSize): WSConfig
136
-	{
137
-		$this->fragmentSize = $fragmentSize;
138
-		return $this;
139
-	}
140
-
141
-	/**
142
-	 * @return mixed
143
-	 */
144
-	public function getContext(): mixed
145
-	{
146
-		return $this->context;
147
-	}
148
-
149
-	/**
150
-	 * @param mixed $context
151
-	 * @return WSConfig
152
-	 */
153
-	public function setContext(mixed $context): WSConfig
154
-	{
155
-		$this->context = $context;
156
-		return $this;
157
-	}
158
-
159
-	/**
160
-	 * @return mixed
161
-	 */
162
-	public function getScheme(): string
163
-	{
164
-		return $this->scheme;
165
-	}
166
-
167
-	/**
168
-	 * @param string $scheme
169
-	 * @return WSConfig
170
-	 */
171
-	public function setScheme(string $scheme): WSConfig
172
-	{
173
-		$this->scheme = $scheme;
174
-		return $this;
175
-	}
176
-
177
-	/**
178
-	 * @return string
179
-	 */
180
-	public function getHost(): string
181
-	{
182
-		return $this->host;
183
-	}
184
-
185
-	/**
186
-	 * @param string $host
187
-	 * @return WSConfig
188
-	 */
189
-	public function setHost(string $host): WSConfig
190
-	{
191
-		$this->host = $host;
192
-		return $this;
193
-	}
194
-
195
-	/**
196
-	 * @return string
197
-	 */
198
-	public function getUser(): string
199
-	{
200
-		return $this->user;
201
-	}
202
-
203
-	/**
204
-	 * @param array $urlParts
205
-	 * @return WSConfig
206
-	 */
207
-	public function setUser(array $urlParts): WSConfig
208
-	{
209
-		$this->user = $urlParts['user'] ?? '';
210
-		return $this;
211
-	}
212
-
213
-	/**
214
-	 * @return string
215
-	 */
216
-	public function getPassword(): string
217
-	{
218
-		return $this->password;
219
-	}
220
-
221
-	/**
222
-	 * @param array $urlParts
223
-	 * @return WSConfig
224
-	 */
225
-	public function setPassword(array $urlParts): WSConfig
226
-	{
227
-		$this->password = $urlParts['pass'] ?? '';
228
-		return $this;
229
-	}
230
-
231
-	/**
232
-	 * @return string
233
-	 */
234
-	public function getPort(): string
235
-	{
236
-		return $this->port;
237
-	}
238
-
239
-	/**
240
-	 * @param array $urlParts
241
-	 * @return WSConfig
242
-	 */
243
-	public function setPort(array $urlParts): WSConfig
244
-	{
245
-		$this->port = $urlParts['port'] ?? ($this->scheme === 'wss' ? '443' : '80');
246
-		return $this;
247
-	}
248
-
249
-	/**
250
-	 * @return array
251
-	 */
252
-	public function getContextOptions(): array
253
-	{
254
-		return $this->contextOptions;
255
-	}
256
-
257
-	/**
258
-	 * @param array $contextOptions
259
-	 * @return WSConfig
260
-	 */
261
-	public function setContextOptions(array $contextOptions): WSConfig
262
-	{
263
-		$this->contextOptions = $contextOptions;
264
-		return $this;
265
-	}
266
-
267
-	/**
268
-	 * @param string $ip
269
-	 * @param string $port
270
-	 * @return WSConfig
271
-	 */
272
-	public function setProxy(string $ip, string $port): WSConfig
273
-	{
274
-		$this->hasProxy = true;
275
-		$this->proxyIp = $ip;
276
-		$this->proxyPort = $port;
277
-
278
-		return $this;
279
-	}
280
-
281
-	/**
282
-	 * Sets auth for proxy
283
-	 *
284
-	 * @param string $userName
285
-	 * @param string $password
286
-	 * @return WSConfig
287
-	 */
288
-	public function setProxyAuth(string $userName, string $password): WSConfig
289
-	{
290
-		$this->proxyAuth = (empty($userName) === false && empty($password) === false) ? base64_encode($userName . ':' . $password) : null;
291
-		return $this;
292
-	}
293
-
294
-	/**
295
-	 * @return bool
296
-	 */
297
-	public function hasProxy(): bool
298
-	{
299
-		return $this->hasProxy;
300
-	}
301
-
302
-	/**
303
-	 * @return string|null
304
-	 */
305
-	public function getProxyIp(): ?string
306
-	{
307
-		return $this->proxyIp;
308
-	}
309
-
310
-	/**
311
-	 * @return string|null
312
-	 */
313
-	public function getProxyPort(): ?string
314
-	{
315
-		return $this->proxyPort;
316
-	}
317
-
318
-	/**
319
-	 * @return string|null
320
-	 */
321
-	public function getProxyAuth(): ?string
322
-	{
323
-		return $this->proxyAuth;
324
-	}
17
+    /**
18
+     * @var string
19
+     */
20
+    private string $scheme;
21
+
22
+    /**
23
+     * @var string
24
+     */
25
+    private string $host;
26
+
27
+    /**
28
+     * @var string
29
+     */
30
+    private string $user;
31
+
32
+    /**
33
+     * @var string
34
+     */
35
+    private string $password;
36
+
37
+    /**
38
+     * @var string
39
+     */
40
+    private string $port;
41
+
42
+    /**
43
+     * @var int
44
+     */
45
+    private int $timeout = WscCommonsContract::DEFAULT_TIMEOUT;
46
+
47
+    /**
48
+     * @var array
49
+     */
50
+    private array $headers = [];
51
+
52
+    /**
53
+     * @var int
54
+     */
55
+    private int $fragmentSize = WscCommonsContract::DEFAULT_FRAGMENT_SIZE;
56
+
57
+    /**
58
+     * @var null|resource
59
+     */
60
+    private $context;
61
+
62
+    /**
63
+     * @var bool
64
+     */
65
+    private bool $hasProxy = false;
66
+
67
+    /**
68
+     * @var string
69
+     */
70
+    private string $proxyIp;
71
+
72
+    /**
73
+     * @var string
74
+     */
75
+    private string $proxyPort;
76
+
77
+    /**
78
+     * @var string|null
79
+     */
80
+    private ?string $proxyAuth;
81
+
82
+    /**
83
+     * @var array
84
+     */
85
+    private array $contextOptions = [];
86
+
87
+    /**
88
+     * @return int
89
+     */
90
+    public function getTimeout(): int
91
+    {
92
+        return $this->timeout;
93
+    }
94
+
95
+    /**
96
+     * @param int $timeout
97
+     * @return WSConfig
98
+     */
99
+    public function setTimeout(int $timeout): WSConfig
100
+    {
101
+        $this->timeout = $timeout;
102
+        return $this;
103
+    }
104
+
105
+    /**
106
+     * @return array
107
+     */
108
+    public function getHeaders(): array
109
+    {
110
+        return $this->headers;
111
+    }
112
+
113
+    /**
114
+     * @param array $headers
115
+     * @return WSConfig
116
+     */
117
+    public function setHeaders(array $headers): WSConfig
118
+    {
119
+        $this->headers = $headers;
120
+        return $this;
121
+    }
122
+
123
+    /**
124
+     * @return int
125
+     */
126
+    public function getFragmentSize(): int
127
+    {
128
+        return $this->fragmentSize;
129
+    }
130
+
131
+    /**
132
+     * @param int $fragmentSize
133
+     * @return WSConfig
134
+     */
135
+    public function setFragmentSize(int $fragmentSize): WSConfig
136
+    {
137
+        $this->fragmentSize = $fragmentSize;
138
+        return $this;
139
+    }
140
+
141
+    /**
142
+     * @return mixed
143
+     */
144
+    public function getContext(): mixed
145
+    {
146
+        return $this->context;
147
+    }
148
+
149
+    /**
150
+     * @param mixed $context
151
+     * @return WSConfig
152
+     */
153
+    public function setContext(mixed $context): WSConfig
154
+    {
155
+        $this->context = $context;
156
+        return $this;
157
+    }
158
+
159
+    /**
160
+     * @return mixed
161
+     */
162
+    public function getScheme(): string
163
+    {
164
+        return $this->scheme;
165
+    }
166
+
167
+    /**
168
+     * @param string $scheme
169
+     * @return WSConfig
170
+     */
171
+    public function setScheme(string $scheme): WSConfig
172
+    {
173
+        $this->scheme = $scheme;
174
+        return $this;
175
+    }
176
+
177
+    /**
178
+     * @return string
179
+     */
180
+    public function getHost(): string
181
+    {
182
+        return $this->host;
183
+    }
184
+
185
+    /**
186
+     * @param string $host
187
+     * @return WSConfig
188
+     */
189
+    public function setHost(string $host): WSConfig
190
+    {
191
+        $this->host = $host;
192
+        return $this;
193
+    }
194
+
195
+    /**
196
+     * @return string
197
+     */
198
+    public function getUser(): string
199
+    {
200
+        return $this->user;
201
+    }
202
+
203
+    /**
204
+     * @param array $urlParts
205
+     * @return WSConfig
206
+     */
207
+    public function setUser(array $urlParts): WSConfig
208
+    {
209
+        $this->user = $urlParts['user'] ?? '';
210
+        return $this;
211
+    }
212
+
213
+    /**
214
+     * @return string
215
+     */
216
+    public function getPassword(): string
217
+    {
218
+        return $this->password;
219
+    }
220
+
221
+    /**
222
+     * @param array $urlParts
223
+     * @return WSConfig
224
+     */
225
+    public function setPassword(array $urlParts): WSConfig
226
+    {
227
+        $this->password = $urlParts['pass'] ?? '';
228
+        return $this;
229
+    }
230
+
231
+    /**
232
+     * @return string
233
+     */
234
+    public function getPort(): string
235
+    {
236
+        return $this->port;
237
+    }
238
+
239
+    /**
240
+     * @param array $urlParts
241
+     * @return WSConfig
242
+     */
243
+    public function setPort(array $urlParts): WSConfig
244
+    {
245
+        $this->port = $urlParts['port'] ?? ($this->scheme === 'wss' ? '443' : '80');
246
+        return $this;
247
+    }
248
+
249
+    /**
250
+     * @return array
251
+     */
252
+    public function getContextOptions(): array
253
+    {
254
+        return $this->contextOptions;
255
+    }
256
+
257
+    /**
258
+     * @param array $contextOptions
259
+     * @return WSConfig
260
+     */
261
+    public function setContextOptions(array $contextOptions): WSConfig
262
+    {
263
+        $this->contextOptions = $contextOptions;
264
+        return $this;
265
+    }
266
+
267
+    /**
268
+     * @param string $ip
269
+     * @param string $port
270
+     * @return WSConfig
271
+     */
272
+    public function setProxy(string $ip, string $port): WSConfig
273
+    {
274
+        $this->hasProxy = true;
275
+        $this->proxyIp = $ip;
276
+        $this->proxyPort = $port;
277
+
278
+        return $this;
279
+    }
280
+
281
+    /**
282
+     * Sets auth for proxy
283
+     *
284
+     * @param string $userName
285
+     * @param string $password
286
+     * @return WSConfig
287
+     */
288
+    public function setProxyAuth(string $userName, string $password): WSConfig
289
+    {
290
+        $this->proxyAuth = (empty($userName) === false && empty($password) === false) ? base64_encode($userName . ':' . $password) : null;
291
+        return $this;
292
+    }
293
+
294
+    /**
295
+     * @return bool
296
+     */
297
+    public function hasProxy(): bool
298
+    {
299
+        return $this->hasProxy;
300
+    }
301
+
302
+    /**
303
+     * @return string|null
304
+     */
305
+    public function getProxyIp(): ?string
306
+    {
307
+        return $this->proxyIp;
308
+    }
309
+
310
+    /**
311
+     * @return string|null
312
+     */
313
+    public function getProxyPort(): ?string
314
+    {
315
+        return $this->proxyPort;
316
+    }
317
+
318
+    /**
319
+     * @return string|null
320
+     */
321
+    public function getProxyAuth(): ?string
322
+    {
323
+        return $this->proxyAuth;
324
+    }
325 325
 
326 326
 }
Please login to merge, or discard this patch.
src/Utils/WscMain.php 2 patches
Indentation   +416 added lines, -416 removed lines patch added patch discarded remove patch
@@ -19,421 +19,421 @@
 block discarded – undo
19 19
 class WscMain implements WscCommonsContract
20 20
 {
21 21
 
22
-	use WSClientTrait;
23
-
24
-	/**
25
-	 * @var resource|bool
26
-	 */
27
-	private $socket;
28
-
29
-	/**
30
-	 * @var bool
31
-	 */
32
-	private bool $isConnected = false;
33
-
34
-	/**
35
-	 * @var bool
36
-	 */
37
-	private bool $isClosing = false;
38
-
39
-	/**
40
-	 * @var string
41
-	 */
42
-	private string $lastOpcode;
43
-
44
-	/**
45
-	 * @var float|int
46
-	 */
47
-	private float|int $closeStatus;
48
-
49
-	/**
50
-	 * @var string|null
51
-	 */
52
-	private ?string $hugePayload;
53
-
54
-	/**
55
-	 * @var array|int[]
56
-	 */
57
-	private static array $opcodes = [
58
-		CommonsContract::EVENT_TYPE_CONTINUATION => 0,
59
-		CommonsContract::EVENT_TYPE_TEXT => 1,
60
-		CommonsContract::EVENT_TYPE_BINARY => 2,
61
-		CommonsContract::EVENT_TYPE_CLOSE => 8,
62
-		CommonsContract::EVENT_TYPE_PING => 9,
63
-		CommonsContract::EVENT_TYPE_PONG => 10,
64
-	];
65
-
66
-	/**
67
-	 * @var string
68
-	 */
69
-	protected string $socketUrl = '';
70
-
71
-	/**
72
-	 * @var WSConfig
73
-	 */
74
-	protected WSConfig $config;
75
-
76
-	/**
77
-	 * @param WSConfig $config
78
-	 * @throws \Exception
79
-	 */
80
-	protected function connect(WSConfig $config): void
81
-	{
82
-		$this->config = $config;
83
-		$urlParts = parse_url($this->socketUrl);
84
-
85
-		$this->config->setScheme($urlParts['scheme']);
86
-		$this->config->setHost($urlParts['host']);
87
-		$this->config->setUser($urlParts);
88
-		$this->config->setPassword($urlParts);
89
-		$this->config->setPort($urlParts);
90
-
91
-		$pathWithQuery = $this->getPathWithQuery($urlParts);
92
-		$hostUri = $this->getHostUri($this->config);
93
-
94
-		$context = $this->getStreamContext();
95
-		if ($this->config->hasProxy()) {
96
-			$this->socket = $this->proxy();
97
-		} else {
98
-			$this->socket = @stream_socket_client(
99
-				$hostUri . ':' . $this->config->getPort(),
100
-				$errno,
101
-				$errstr,
102
-				$this->config->getTimeout(),
103
-				STREAM_CLIENT_CONNECT,
104
-				$context
105
-			);
106
-		}
107
-
108
-		if ($this->socket === false) {
109
-			throw new ConnectionException(
110
-				"Could not open socket to \"{$this->config->getHost()}:{$this->config->getPort()}\": $errstr ($errno).",
111
-				CommonsContract::CLIENT_COULD_NOT_OPEN_SOCKET
112
-			);
113
-		}
114
-
115
-		stream_set_timeout($this->socket, $this->config->getTimeout());
116
-
117
-		$key = $this->generateKey();
118
-		$headers = [
119
-			'Host' => $this->config->getHost() . ':' . $this->config->getPort(),
120
-			'User-Agent' => 'websocket-client-php',
121
-			'Connection' => 'Upgrade',
122
-			'Upgrade' => 'WebSocket',
123
-			'Sec-WebSocket-Key' => $key,
124
-			'Sec-Websocket-Version' => '13',
125
-		];
126
-
127
-		if ($this->config->getUser() || $this->config->getPassword()) {
128
-			$headers['authorization'] = 'Basic ' . base64_encode($this->config->getUser() . ':' . $this->config->getPassword()) . "\r\n";
129
-		}
130
-
131
-		if (!empty($this->config->getHeaders())) {
132
-			$headers = array_merge($headers, $this->config->getHeaders());
133
-		}
134
-
135
-		$header = $this->getHeaders($pathWithQuery, $headers);
136
-
137
-		$this->write($header);
138
-
139
-		$this->validateResponse($this->config, $pathWithQuery, $key);
140
-		$this->isConnected = true;
141
-	}
142
-
143
-	/**
144
-	 * Init a proxy connection
145
-	 *
146
-	 * @return resource|false
147
-	 * @throws InvalidArgumentException
148
-	 * @throws ConnectionException
149
-	 */
150
-	private function proxy()
151
-	{
152
-		$sock = @stream_socket_client(
153
-			WscCommonsContract::TCP_SCHEME . $this->config->getProxyIp() . ':' . $this->config->getProxyPort(),
154
-			$errno,
155
-			$errstr,
156
-			$this->config->getTimeout(),
157
-			STREAM_CLIENT_CONNECT,
158
-			$this->getStreamContext()
159
-		);
160
-		$write = "CONNECT {$this->config->getProxyIp()}:{$this->config->getProxyPort()} HTTP/1.1\r\n";
161
-		$auth = $this->config->getProxyAuth();
162
-		if ($auth !== NULL) {
163
-			$write .= "Proxy-Authorization: Basic {$auth}\r\n";
164
-		}
165
-		$write .= "\r\n";
166
-		fwrite($sock, $write);
167
-		$resp = fread($sock, 1024);
168
-
169
-		if (preg_match(self::PROXY_MATCH_RESP, $resp) === 1) {
170
-			return $sock;
171
-		}
172
-
173
-		throw new ConnectionException('Failed to connect to the host via proxy');
174
-	}
175
-
176
-	/**
177
-	 * @return mixed
178
-	 * @throws \InvalidArgumentException
179
-	 */
180
-	private function getStreamContext(): mixed
181
-	{
182
-		if ($this->config->getContext() !== null) {
183
-			// Suppress the error since we'll catch it below
184
-			if (@get_resource_type($this->config->getContext()) === 'stream-context') {
185
-				return $this->config->getContext();
186
-			}
187
-
188
-			throw new \InvalidArgumentException(
189
-				'Stream context is invalid',
190
-				CommonsContract::CLIENT_INVALID_STREAM_CONTEXT
191
-			);
192
-		}
193
-
194
-		return stream_context_create($this->config->getContextOptions());
195
-	}
196
-
197
-	/**
198
-	 * @param mixed $urlParts
199
-	 * @return string
200
-	 */
201
-	private function getPathWithQuery(mixed $urlParts): string
202
-	{
203
-		$path = isset($urlParts['path']) ? $urlParts['path'] : '/';
204
-		$query = isset($urlParts['query']) ? $urlParts['query'] : '';
205
-		$fragment = isset($urlParts['fragment']) ? $urlParts['fragment'] : '';
206
-		$pathWithQuery = $path;
207
-		if (!empty($query)) {
208
-			$pathWithQuery .= '?' . $query;
209
-		}
210
-		if (!empty($fragment)) {
211
-			$pathWithQuery .= '#' . $fragment;
212
-		}
213
-
214
-		return $pathWithQuery;
215
-	}
216
-
217
-	/**
218
-	 * @param string $pathWithQuery
219
-	 * @param array $headers
220
-	 * @return string
221
-	 */
222
-	private function getHeaders(string $pathWithQuery, array $headers): string
223
-	{
224
-		return 'GET ' . $pathWithQuery . " HTTP/1.1\r\n"
225
-			. implode(
226
-				"\r\n",
227
-				array_map(
228
-					function ($key, $value) {
229
-						return "$key: $value";
230
-					},
231
-					array_keys($headers),
232
-					$headers
233
-				)
234
-			)
235
-			. "\r\n\r\n";
236
-	}
237
-
238
-	/**
239
-	 * @return string
240
-	 */
241
-	public function getLastOpcode(): string
242
-	{
243
-		return $this->lastOpcode;
244
-	}
245
-
246
-	/**
247
-	 * @return int
248
-	 */
249
-	public function getCloseStatus(): int
250
-	{
251
-		return $this->closeStatus;
252
-	}
253
-
254
-	/**
255
-	 * @return bool
256
-	 */
257
-	public function isConnected(): bool
258
-	{
259
-		return $this->isConnected;
260
-	}
261
-
262
-	/**
263
-	 * @param int $timeout
264
-	 * @param null $microSecs
265
-	 * @return WscMain
266
-	 */
267
-	public function setTimeout(int $timeout, $microSecs = null): WscMain
268
-	{
269
-		$this->config->setTimeout($timeout);
270
-		if ($this->socket && get_resource_type($this->socket) === 'stream') {
271
-			stream_set_timeout($this->socket, $timeout, $microSecs);
272
-		}
273
-
274
-		return $this;
275
-	}
276
-
277
-	/**
278
-	 * Sends message to opened socket connection client->server
279
-	 *
280
-	 * @param $payload
281
-	 * @param string $opcode
282
-	 * @throws \Exception
283
-	 */
284
-	public function send($payload, string $opcode = CommonsContract::EVENT_TYPE_TEXT): void
285
-	{
286
-		if (!$this->isConnected) {
287
-			$this->connect(new WSConfig());
288
-		}
289
-
290
-		if (array_key_exists($opcode, self::$opcodes) === false) {
291
-			throw new BadOpcodeException(
292
-				"Bad opcode '$opcode'.  Try 'text' or 'binary'.",
293
-				CommonsContract::CLIENT_BAD_OPCODE
294
-			);
295
-		}
296
-
297
-		$payloadLength = strlen($payload);
298
-		$fragmentCursor = 0;
299
-
300
-		while ($payloadLength > $fragmentCursor) {
301
-			$subPayload = substr($payload, $fragmentCursor, $this->config->getFragmentSize());
302
-			$fragmentCursor += $this->config->getFragmentSize();
303
-			$final = $payloadLength <= $fragmentCursor;
304
-			$this->sendFragment($final, $subPayload, $opcode, true);
305
-			$opcode = 'continuation';
306
-		}
307
-	}
308
-
309
-	/**
310
-	 * Receives message client<-server
311
-	 *
312
-	 * @return null|string
313
-	 * @throws \Exception
314
-	 */
315
-	public function receive(): ?string
316
-	{
317
-		if (!$this->isConnected) {
318
-			$this->connect(new WSConfig());
319
-		}
320
-
321
-		$this->hugePayload = '';
322
-
323
-		$response = null;
324
-		while ($response === null) {
325
-			$response = $this->receiveFragment();
326
-		}
327
-
328
-		return $response;
329
-	}
330
-
331
-	/**
332
-	 * Tell the socket to close.
333
-	 *
334
-	 * @param integer $status http://tools.ietf.org/html/rfc6455#section-7.4
335
-	 * @param string $message A closing message, max 125 bytes.
336
-	 * @return bool|null|string
337
-	 * @throws \Exception
338
-	 */
339
-	public function close(int $status = 1000, string $message = 'ttfn'): bool|null|string
340
-	{
341
-		$statusBin = sprintf('%016b', $status);
342
-		$statusStr = '';
343
-
344
-		foreach (str_split($statusBin, 8) as $binstr) {
345
-			$statusStr .= chr(bindec($binstr));
346
-		}
347
-
348
-		$this->send($statusStr . $message, CommonsContract::EVENT_TYPE_CLOSE);
349
-		$this->isClosing = true;
350
-
351
-		return $this->receive(); // Receiving a close frame will close the socket now.
352
-	}
353
-
354
-	/**
355
-	 * @param string $data
356
-	 * @throws ConnectionException
357
-	 */
358
-	protected function write(string $data): void
359
-	{
360
-		$written = fwrite($this->socket, $data);
361
-
362
-		if ($written < strlen($data)) {
363
-			throw new ConnectionException(
364
-				"Could only write $written out of " . strlen($data) . ' bytes.',
365
-				CommonsContract::CLIENT_COULD_ONLY_WRITE_LESS
366
-			);
367
-		}
368
-	}
369
-
370
-	/**
371
-	 * @param int $len
372
-	 * @return string
373
-	 * @throws ConnectionException
374
-	 */
375
-	protected function read(int $len): string
376
-	{
377
-		$data = '';
378
-		while (($dataLen = strlen($data)) < $len) {
379
-			$buff = fread($this->socket, $len - $dataLen);
380
-
381
-			if ($buff === false) {
382
-				$metadata = stream_get_meta_data($this->socket);
383
-				throw new ConnectionException(
384
-					'Broken frame, read ' . strlen($data) . ' of stated '
385
-					. $len . ' bytes.  Stream state: '
386
-					. json_encode($metadata),
387
-					CommonsContract::CLIENT_BROKEN_FRAME
388
-				);
389
-			}
390
-
391
-			if ($buff === '') {
392
-				$metadata = stream_get_meta_data($this->socket);
393
-				throw new ConnectionException(
394
-					'Empty read; connection dead?  Stream state: ' . json_encode($metadata),
395
-					CommonsContract::CLIENT_EMPTY_READ
396
-				);
397
-			}
398
-			$data .= $buff;
399
-		}
400
-
401
-		return $data;
402
-	}
403
-
404
-	/**
405
-	 * Helper to convert a binary to a string of '0' and '1'.
406
-	 *
407
-	 * @param string $string
408
-	 * @return string
409
-	 */
410
-	protected static function sprintB(string $string): string
411
-	{
412
-		$return = '';
413
-		$strLen = strlen($string);
414
-		for ($i = 0; $i < $strLen; $i++) {
415
-			$return .= sprintf('%08b', ord($string[$i]));
416
-		}
417
-
418
-		return $return;
419
-	}
420
-
421
-	/**
422
-	 * Sec-WebSocket-Key generator
423
-	 *
424
-	 * @return string   the 16 character length key
425
-	 * @throws \Exception
426
-	 */
427
-	private function generateKey(): string
428
-	{
429
-		$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$&/()=[]{}0123456789';
430
-		$key = '';
431
-		$chLen = strlen($chars);
432
-		for ($i = 0; $i < self::KEY_GEN_LENGTH; $i++) {
433
-			$key .= $chars[random_int(0, $chLen - 1)];
434
-		}
435
-
436
-		return base64_encode($key);
437
-	}
22
+    use WSClientTrait;
23
+
24
+    /**
25
+     * @var resource|bool
26
+     */
27
+    private $socket;
28
+
29
+    /**
30
+     * @var bool
31
+     */
32
+    private bool $isConnected = false;
33
+
34
+    /**
35
+     * @var bool
36
+     */
37
+    private bool $isClosing = false;
38
+
39
+    /**
40
+     * @var string
41
+     */
42
+    private string $lastOpcode;
43
+
44
+    /**
45
+     * @var float|int
46
+     */
47
+    private float|int $closeStatus;
48
+
49
+    /**
50
+     * @var string|null
51
+     */
52
+    private ?string $hugePayload;
53
+
54
+    /**
55
+     * @var array|int[]
56
+     */
57
+    private static array $opcodes = [
58
+        CommonsContract::EVENT_TYPE_CONTINUATION => 0,
59
+        CommonsContract::EVENT_TYPE_TEXT => 1,
60
+        CommonsContract::EVENT_TYPE_BINARY => 2,
61
+        CommonsContract::EVENT_TYPE_CLOSE => 8,
62
+        CommonsContract::EVENT_TYPE_PING => 9,
63
+        CommonsContract::EVENT_TYPE_PONG => 10,
64
+    ];
65
+
66
+    /**
67
+     * @var string
68
+     */
69
+    protected string $socketUrl = '';
70
+
71
+    /**
72
+     * @var WSConfig
73
+     */
74
+    protected WSConfig $config;
75
+
76
+    /**
77
+     * @param WSConfig $config
78
+     * @throws \Exception
79
+     */
80
+    protected function connect(WSConfig $config): void
81
+    {
82
+        $this->config = $config;
83
+        $urlParts = parse_url($this->socketUrl);
84
+
85
+        $this->config->setScheme($urlParts['scheme']);
86
+        $this->config->setHost($urlParts['host']);
87
+        $this->config->setUser($urlParts);
88
+        $this->config->setPassword($urlParts);
89
+        $this->config->setPort($urlParts);
90
+
91
+        $pathWithQuery = $this->getPathWithQuery($urlParts);
92
+        $hostUri = $this->getHostUri($this->config);
93
+
94
+        $context = $this->getStreamContext();
95
+        if ($this->config->hasProxy()) {
96
+            $this->socket = $this->proxy();
97
+        } else {
98
+            $this->socket = @stream_socket_client(
99
+                $hostUri . ':' . $this->config->getPort(),
100
+                $errno,
101
+                $errstr,
102
+                $this->config->getTimeout(),
103
+                STREAM_CLIENT_CONNECT,
104
+                $context
105
+            );
106
+        }
107
+
108
+        if ($this->socket === false) {
109
+            throw new ConnectionException(
110
+                "Could not open socket to \"{$this->config->getHost()}:{$this->config->getPort()}\": $errstr ($errno).",
111
+                CommonsContract::CLIENT_COULD_NOT_OPEN_SOCKET
112
+            );
113
+        }
114
+
115
+        stream_set_timeout($this->socket, $this->config->getTimeout());
116
+
117
+        $key = $this->generateKey();
118
+        $headers = [
119
+            'Host' => $this->config->getHost() . ':' . $this->config->getPort(),
120
+            'User-Agent' => 'websocket-client-php',
121
+            'Connection' => 'Upgrade',
122
+            'Upgrade' => 'WebSocket',
123
+            'Sec-WebSocket-Key' => $key,
124
+            'Sec-Websocket-Version' => '13',
125
+        ];
126
+
127
+        if ($this->config->getUser() || $this->config->getPassword()) {
128
+            $headers['authorization'] = 'Basic ' . base64_encode($this->config->getUser() . ':' . $this->config->getPassword()) . "\r\n";
129
+        }
130
+
131
+        if (!empty($this->config->getHeaders())) {
132
+            $headers = array_merge($headers, $this->config->getHeaders());
133
+        }
134
+
135
+        $header = $this->getHeaders($pathWithQuery, $headers);
136
+
137
+        $this->write($header);
138
+
139
+        $this->validateResponse($this->config, $pathWithQuery, $key);
140
+        $this->isConnected = true;
141
+    }
142
+
143
+    /**
144
+     * Init a proxy connection
145
+     *
146
+     * @return resource|false
147
+     * @throws InvalidArgumentException
148
+     * @throws ConnectionException
149
+     */
150
+    private function proxy()
151
+    {
152
+        $sock = @stream_socket_client(
153
+            WscCommonsContract::TCP_SCHEME . $this->config->getProxyIp() . ':' . $this->config->getProxyPort(),
154
+            $errno,
155
+            $errstr,
156
+            $this->config->getTimeout(),
157
+            STREAM_CLIENT_CONNECT,
158
+            $this->getStreamContext()
159
+        );
160
+        $write = "CONNECT {$this->config->getProxyIp()}:{$this->config->getProxyPort()} HTTP/1.1\r\n";
161
+        $auth = $this->config->getProxyAuth();
162
+        if ($auth !== NULL) {
163
+            $write .= "Proxy-Authorization: Basic {$auth}\r\n";
164
+        }
165
+        $write .= "\r\n";
166
+        fwrite($sock, $write);
167
+        $resp = fread($sock, 1024);
168
+
169
+        if (preg_match(self::PROXY_MATCH_RESP, $resp) === 1) {
170
+            return $sock;
171
+        }
172
+
173
+        throw new ConnectionException('Failed to connect to the host via proxy');
174
+    }
175
+
176
+    /**
177
+     * @return mixed
178
+     * @throws \InvalidArgumentException
179
+     */
180
+    private function getStreamContext(): mixed
181
+    {
182
+        if ($this->config->getContext() !== null) {
183
+            // Suppress the error since we'll catch it below
184
+            if (@get_resource_type($this->config->getContext()) === 'stream-context') {
185
+                return $this->config->getContext();
186
+            }
187
+
188
+            throw new \InvalidArgumentException(
189
+                'Stream context is invalid',
190
+                CommonsContract::CLIENT_INVALID_STREAM_CONTEXT
191
+            );
192
+        }
193
+
194
+        return stream_context_create($this->config->getContextOptions());
195
+    }
196
+
197
+    /**
198
+     * @param mixed $urlParts
199
+     * @return string
200
+     */
201
+    private function getPathWithQuery(mixed $urlParts): string
202
+    {
203
+        $path = isset($urlParts['path']) ? $urlParts['path'] : '/';
204
+        $query = isset($urlParts['query']) ? $urlParts['query'] : '';
205
+        $fragment = isset($urlParts['fragment']) ? $urlParts['fragment'] : '';
206
+        $pathWithQuery = $path;
207
+        if (!empty($query)) {
208
+            $pathWithQuery .= '?' . $query;
209
+        }
210
+        if (!empty($fragment)) {
211
+            $pathWithQuery .= '#' . $fragment;
212
+        }
213
+
214
+        return $pathWithQuery;
215
+    }
216
+
217
+    /**
218
+     * @param string $pathWithQuery
219
+     * @param array $headers
220
+     * @return string
221
+     */
222
+    private function getHeaders(string $pathWithQuery, array $headers): string
223
+    {
224
+        return 'GET ' . $pathWithQuery . " HTTP/1.1\r\n"
225
+            . implode(
226
+                "\r\n",
227
+                array_map(
228
+                    function ($key, $value) {
229
+                        return "$key: $value";
230
+                    },
231
+                    array_keys($headers),
232
+                    $headers
233
+                )
234
+            )
235
+            . "\r\n\r\n";
236
+    }
237
+
238
+    /**
239
+     * @return string
240
+     */
241
+    public function getLastOpcode(): string
242
+    {
243
+        return $this->lastOpcode;
244
+    }
245
+
246
+    /**
247
+     * @return int
248
+     */
249
+    public function getCloseStatus(): int
250
+    {
251
+        return $this->closeStatus;
252
+    }
253
+
254
+    /**
255
+     * @return bool
256
+     */
257
+    public function isConnected(): bool
258
+    {
259
+        return $this->isConnected;
260
+    }
261
+
262
+    /**
263
+     * @param int $timeout
264
+     * @param null $microSecs
265
+     * @return WscMain
266
+     */
267
+    public function setTimeout(int $timeout, $microSecs = null): WscMain
268
+    {
269
+        $this->config->setTimeout($timeout);
270
+        if ($this->socket && get_resource_type($this->socket) === 'stream') {
271
+            stream_set_timeout($this->socket, $timeout, $microSecs);
272
+        }
273
+
274
+        return $this;
275
+    }
276
+
277
+    /**
278
+     * Sends message to opened socket connection client->server
279
+     *
280
+     * @param $payload
281
+     * @param string $opcode
282
+     * @throws \Exception
283
+     */
284
+    public function send($payload, string $opcode = CommonsContract::EVENT_TYPE_TEXT): void
285
+    {
286
+        if (!$this->isConnected) {
287
+            $this->connect(new WSConfig());
288
+        }
289
+
290
+        if (array_key_exists($opcode, self::$opcodes) === false) {
291
+            throw new BadOpcodeException(
292
+                "Bad opcode '$opcode'.  Try 'text' or 'binary'.",
293
+                CommonsContract::CLIENT_BAD_OPCODE
294
+            );
295
+        }
296
+
297
+        $payloadLength = strlen($payload);
298
+        $fragmentCursor = 0;
299
+
300
+        while ($payloadLength > $fragmentCursor) {
301
+            $subPayload = substr($payload, $fragmentCursor, $this->config->getFragmentSize());
302
+            $fragmentCursor += $this->config->getFragmentSize();
303
+            $final = $payloadLength <= $fragmentCursor;
304
+            $this->sendFragment($final, $subPayload, $opcode, true);
305
+            $opcode = 'continuation';
306
+        }
307
+    }
308
+
309
+    /**
310
+     * Receives message client<-server
311
+     *
312
+     * @return null|string
313
+     * @throws \Exception
314
+     */
315
+    public function receive(): ?string
316
+    {
317
+        if (!$this->isConnected) {
318
+            $this->connect(new WSConfig());
319
+        }
320
+
321
+        $this->hugePayload = '';
322
+
323
+        $response = null;
324
+        while ($response === null) {
325
+            $response = $this->receiveFragment();
326
+        }
327
+
328
+        return $response;
329
+    }
330
+
331
+    /**
332
+     * Tell the socket to close.
333
+     *
334
+     * @param integer $status http://tools.ietf.org/html/rfc6455#section-7.4
335
+     * @param string $message A closing message, max 125 bytes.
336
+     * @return bool|null|string
337
+     * @throws \Exception
338
+     */
339
+    public function close(int $status = 1000, string $message = 'ttfn'): bool|null|string
340
+    {
341
+        $statusBin = sprintf('%016b', $status);
342
+        $statusStr = '';
343
+
344
+        foreach (str_split($statusBin, 8) as $binstr) {
345
+            $statusStr .= chr(bindec($binstr));
346
+        }
347
+
348
+        $this->send($statusStr . $message, CommonsContract::EVENT_TYPE_CLOSE);
349
+        $this->isClosing = true;
350
+
351
+        return $this->receive(); // Receiving a close frame will close the socket now.
352
+    }
353
+
354
+    /**
355
+     * @param string $data
356
+     * @throws ConnectionException
357
+     */
358
+    protected function write(string $data): void
359
+    {
360
+        $written = fwrite($this->socket, $data);
361
+
362
+        if ($written < strlen($data)) {
363
+            throw new ConnectionException(
364
+                "Could only write $written out of " . strlen($data) . ' bytes.',
365
+                CommonsContract::CLIENT_COULD_ONLY_WRITE_LESS
366
+            );
367
+        }
368
+    }
369
+
370
+    /**
371
+     * @param int $len
372
+     * @return string
373
+     * @throws ConnectionException
374
+     */
375
+    protected function read(int $len): string
376
+    {
377
+        $data = '';
378
+        while (($dataLen = strlen($data)) < $len) {
379
+            $buff = fread($this->socket, $len - $dataLen);
380
+
381
+            if ($buff === false) {
382
+                $metadata = stream_get_meta_data($this->socket);
383
+                throw new ConnectionException(
384
+                    'Broken frame, read ' . strlen($data) . ' of stated '
385
+                    . $len . ' bytes.  Stream state: '
386
+                    . json_encode($metadata),
387
+                    CommonsContract::CLIENT_BROKEN_FRAME
388
+                );
389
+            }
390
+
391
+            if ($buff === '') {
392
+                $metadata = stream_get_meta_data($this->socket);
393
+                throw new ConnectionException(
394
+                    'Empty read; connection dead?  Stream state: ' . json_encode($metadata),
395
+                    CommonsContract::CLIENT_EMPTY_READ
396
+                );
397
+            }
398
+            $data .= $buff;
399
+        }
400
+
401
+        return $data;
402
+    }
403
+
404
+    /**
405
+     * Helper to convert a binary to a string of '0' and '1'.
406
+     *
407
+     * @param string $string
408
+     * @return string
409
+     */
410
+    protected static function sprintB(string $string): string
411
+    {
412
+        $return = '';
413
+        $strLen = strlen($string);
414
+        for ($i = 0; $i < $strLen; $i++) {
415
+            $return .= sprintf('%08b', ord($string[$i]));
416
+        }
417
+
418
+        return $return;
419
+    }
420
+
421
+    /**
422
+     * Sec-WebSocket-Key generator
423
+     *
424
+     * @return string   the 16 character length key
425
+     * @throws \Exception
426
+     */
427
+    private function generateKey(): string
428
+    {
429
+        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$&/()=[]{}0123456789';
430
+        $key = '';
431
+        $chLen = strlen($chars);
432
+        for ($i = 0; $i < self::KEY_GEN_LENGTH; $i++) {
433
+            $key .= $chars[random_int(0, $chLen - 1)];
434
+        }
435
+
436
+        return base64_encode($key);
437
+    }
438 438
 
439 439
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -225,7 +225,7 @@
 block discarded – undo
225 225
 			. implode(
226 226
 				"\r\n",
227 227
 				array_map(
228
-					function ($key, $value) {
228
+					function($key, $value) {
229 229
 						return "$key: $value";
230 230
 					},
231 231
 					array_keys($headers),
Please login to merge, or discard this patch.