Passed
Push — master ( e350b2...49e282 )
by Shahrad
09:52
created
src/Traits/WSConnectionTrait.php 1 patch
Indentation   +247 added lines, -247 removed lines patch added patch discarded remove patch
@@ -18,252 +18,252 @@
 block discarded – undo
18 18
 trait WSConnectionTrait
19 19
 {
20 20
 
21
-	/**
22
-	 * @var callable|null
23
-	 */
24
-	public $onOpen = null;
25
-
26
-	/**
27
-	 * @var callable|null
28
-	 */
29
-	public $onClose = null;
30
-
31
-	/**
32
-	 * @var callable|null
33
-	 */
34
-	public $onError = null;
35
-
36
-	/**
37
-	 * @var callable|null
38
-	 */
39
-	public $onMessage = null;
40
-
41
-	/**
42
-	 * @var callable|null
43
-	 */
44
-	public $onWhile = null;
45
-
46
-	/**
47
-	 * @var bool
48
-	 */
49
-	private bool $isConnected = false;
50
-
51
-	/**
52
-	 * @var bool
53
-	 */
54
-	private bool $isClosing = false;
55
-
56
-	/**
57
-	 * Default headers
58
-	 *
59
-	 * @var array
60
-	 */
61
-	private array $defaultHeaders = [
62
-		'Connection' => 'Upgrade',
63
-		'Upgrade' => 'WebSocket',
64
-		'Sec-Websocket-Version' => '13',
65
-	];
66
-
67
-	/**
68
-	 * @param string $socketUrl string that represents the URL of the Web Socket server. e.g. ws://localhost:1337 or wss://localhost:1337
69
-	 * @param ?WebSocketConfig $config The configuration for the Web Socket client
70
-	 */
71
-	public function connect(string $socketUrl, ?WebSocketConfig $config = null): void
72
-	{
73
-		try {
74
-			$this->config = $config ?? new WebSocketConfig();
75
-			$this->socketUrl = $socketUrl;
76
-			$urlParts = parse_url($this->socketUrl);
77
-
78
-			$this->config->setScheme($urlParts['scheme']);
79
-			$this->config->setHost($urlParts['host']);
80
-			$this->config->setUser($urlParts);
81
-			$this->config->setPassword($urlParts);
82
-			$this->config->setPort($urlParts);
83
-
84
-			$pathWithQuery = $this->getPathWithQuery($urlParts);
85
-			$hostUri = $this->getHostUri($this->config);
86
-
87
-			$context = $this->getStreamContext();
88
-			if ($this->config->hasProxy()) {
89
-				$this->socket = $this->proxy();
90
-			} else {
91
-				$this->socket = @stream_socket_client(
92
-					$hostUri . ':' . $this->config->getPort(),
93
-					$errno,
94
-					$errstr,
95
-					$this->config->getTimeout(),
96
-					STREAM_CLIENT_CONNECT,
97
-					$context
98
-				);
99
-			}
100
-
101
-			if ($this->socket === false) {
102
-				throw new ConnectionException(
103
-					"Could not open socket to \"{$this->config->getHost()}:{$this->config->getPort()}\": $errstr ($errno).",
104
-					CommonsContract::CLIENT_COULD_NOT_OPEN_SOCKET
105
-				);
106
-			}
107
-
108
-			stream_set_timeout($this->socket, $this->config->getTimeout());
109
-
110
-			$key = $this->generateKey();
111
-			$headers = array_merge($this->defaultHeaders, [
112
-				'Host' => $this->config->getHost() . ':' . $this->config->getPort(),
113
-				'User-Agent' => 'Easy-Http/' . self::VERSION . ' (PHP/' . PHP_VERSION . ')',
114
-				'Sec-WebSocket-Key' => $key,
115
-			]);
116
-
117
-			if ($this->config->getUser() || $this->config->getPassword()) {
118
-				$headers['authorization'] = 'Basic ' . base64_encode($this->config->getUser() . ':' . $this->config->getPassword()) . "\r\n";
119
-			}
120
-
121
-			if (!empty($this->config->getHeaders())) {
122
-				$headers = array_merge($headers, $this->config->getHeaders());
123
-			}
124
-
125
-			$header = $this->getHeaders($pathWithQuery, $headers);
126
-
127
-			$this->write($header);
128
-
129
-			$this->validateResponse($this->config, $pathWithQuery, $key);
130
-			$this->isConnected = true;
131
-			$this->whileIsConnected();
132
-
133
-		} catch (\Exception $e) {
134
-			$this->safeCall($this->onError, $this, new WebSocketException(
135
-				$e->getMessage(),
136
-				$e->getCode(),
137
-				$e->getPrevious()
138
-			));
139
-		}
140
-	}
141
-
142
-	/**
143
-	 * Reconnect to the Web Socket server
144
-	 *
145
-	 * @return void
146
-	 * @throws \Exception
147
-	 */
148
-	public function reconnect(): void
149
-	{
150
-		if ($this->isConnected) {
151
-			$this->close();
152
-		}
153
-
154
-		$this->connect($this->socketUrl, $this->config);
155
-	}
156
-
157
-	/**
158
-	 * @return void
159
-	 * @throws WebSocketException|\Exception
160
-	 */
161
-	private function whileIsConnected(): void
162
-	{
163
-		$this->safeCall($this->onOpen, $this);
164
-
165
-		while ($this->isConnected() && $this->isClosing === false) {
166
-			$this->safeCall($this->onWhile, $this);
167
-
168
-			if (is_string(($message = $this->receive()))) {
169
-				$this->safeCall($this->onMessage, $this, $message);
170
-			}
171
-		}
172
-
173
-		$this->safeCall($this->onClose, $this, $this->closeStatus);
174
-	}
175
-
176
-	/**
177
-	 * Execute events with safety of exceptions
178
-	 *
179
-	 * @param callable|null $callback
180
-	 * @param mixed ...$args
181
-	 * @return void
182
-	 */
183
-	private function safeCall(?callable $callback, ...$args): void
184
-	{
185
-		if (is_callable($callback) && $callback) {
186
-			call_user_func($callback, ...$args);
187
-		}
188
-	}
189
-
190
-	/**
191
-	 * Sends message to opened socket connection client->server
192
-	 *
193
-	 * @param $payload
194
-	 * @param string $opcode
195
-	 * @throws \Exception
196
-	 */
197
-	public function send($payload, string $opcode = CommonsContract::EVENT_TYPE_TEXT): void
198
-	{
199
-		if (!$this->isConnected) {
200
-			throw new \Exception(
201
-				"Can't send message. Connection is not established.",
202
-				CommonsContract::CLIENT_CONNECTION_NOT_ESTABLISHED
203
-			);
204
-		}
205
-
206
-		if (array_key_exists($opcode, self::$opcodes) === false) {
207
-			throw new BadOpcodeException(
208
-				sprintf("Bad opcode '%s'.  Try 'text' or 'binary'.", $opcode),
209
-				CommonsContract::CLIENT_BAD_OPCODE
210
-			);
211
-		}
212
-
213
-		$payloadLength = strlen($payload);
214
-		$fragmentCursor = 0;
215
-
216
-		while ($payloadLength > $fragmentCursor) {
217
-			$subPayload = substr($payload, $fragmentCursor, $this->config->getFragmentSize());
218
-			$fragmentCursor += $this->config->getFragmentSize();
219
-			$final = $payloadLength <= $fragmentCursor;
220
-			$this->sendFragment($final, $subPayload, $opcode, true);
221
-			$opcode = 'continuation';
222
-		}
223
-	}
224
-
225
-	/**
226
-	 * Receives message client<-server
227
-	 *
228
-	 * @return string|null
229
-	 * @throws \Exception
230
-	 */
231
-	public function receive(): string|null
232
-	{
233
-		if (!$this->isConnected && $this->isClosing === false) {
234
-			throw new WebSocketException(
235
-				"Your unexpectedly disconnected from the server",
236
-				CommonsContract::CLIENT_CONNECTION_NOT_ESTABLISHED
237
-			);
238
-		}
239
-
240
-		$this->hugePayload = '';
241
-
242
-		return $this->receiveFragment();
243
-	}
244
-
245
-	/**
246
-	 * Tell the socket to close.
247
-	 *
248
-	 * @param integer $status https://github.com/Luka967/websocket-close-codes
249
-	 * @param string $message A closing message, max 125 bytes.
250
-	 * @return bool|null|string
251
-	 * @throws \Exception
252
-	 */
253
-	public function close(int $status = 1000, string $message = 'ttfn'): bool|null|string
254
-	{
255
-		$statusBin = sprintf('%016b', $status);
256
-		$statusStr = '';
257
-
258
-		foreach (str_split($statusBin, 8) as $binstr) {
259
-			$statusStr .= chr(bindec($binstr));
260
-		}
261
-
262
-		$this->send($statusStr . $message, CommonsContract::EVENT_TYPE_CLOSE);
263
-		$this->closeStatus = $status;
264
-		$this->isClosing = true;
265
-
266
-		return $this->receive(); // Receiving a close frame will close the socket now.
267
-	}
21
+    /**
22
+     * @var callable|null
23
+     */
24
+    public $onOpen = null;
25
+
26
+    /**
27
+     * @var callable|null
28
+     */
29
+    public $onClose = null;
30
+
31
+    /**
32
+     * @var callable|null
33
+     */
34
+    public $onError = null;
35
+
36
+    /**
37
+     * @var callable|null
38
+     */
39
+    public $onMessage = null;
40
+
41
+    /**
42
+     * @var callable|null
43
+     */
44
+    public $onWhile = null;
45
+
46
+    /**
47
+     * @var bool
48
+     */
49
+    private bool $isConnected = false;
50
+
51
+    /**
52
+     * @var bool
53
+     */
54
+    private bool $isClosing = false;
55
+
56
+    /**
57
+     * Default headers
58
+     *
59
+     * @var array
60
+     */
61
+    private array $defaultHeaders = [
62
+        'Connection' => 'Upgrade',
63
+        'Upgrade' => 'WebSocket',
64
+        'Sec-Websocket-Version' => '13',
65
+    ];
66
+
67
+    /**
68
+     * @param string $socketUrl string that represents the URL of the Web Socket server. e.g. ws://localhost:1337 or wss://localhost:1337
69
+     * @param ?WebSocketConfig $config The configuration for the Web Socket client
70
+     */
71
+    public function connect(string $socketUrl, ?WebSocketConfig $config = null): void
72
+    {
73
+        try {
74
+            $this->config = $config ?? new WebSocketConfig();
75
+            $this->socketUrl = $socketUrl;
76
+            $urlParts = parse_url($this->socketUrl);
77
+
78
+            $this->config->setScheme($urlParts['scheme']);
79
+            $this->config->setHost($urlParts['host']);
80
+            $this->config->setUser($urlParts);
81
+            $this->config->setPassword($urlParts);
82
+            $this->config->setPort($urlParts);
83
+
84
+            $pathWithQuery = $this->getPathWithQuery($urlParts);
85
+            $hostUri = $this->getHostUri($this->config);
86
+
87
+            $context = $this->getStreamContext();
88
+            if ($this->config->hasProxy()) {
89
+                $this->socket = $this->proxy();
90
+            } else {
91
+                $this->socket = @stream_socket_client(
92
+                    $hostUri . ':' . $this->config->getPort(),
93
+                    $errno,
94
+                    $errstr,
95
+                    $this->config->getTimeout(),
96
+                    STREAM_CLIENT_CONNECT,
97
+                    $context
98
+                );
99
+            }
100
+
101
+            if ($this->socket === false) {
102
+                throw new ConnectionException(
103
+                    "Could not open socket to \"{$this->config->getHost()}:{$this->config->getPort()}\": $errstr ($errno).",
104
+                    CommonsContract::CLIENT_COULD_NOT_OPEN_SOCKET
105
+                );
106
+            }
107
+
108
+            stream_set_timeout($this->socket, $this->config->getTimeout());
109
+
110
+            $key = $this->generateKey();
111
+            $headers = array_merge($this->defaultHeaders, [
112
+                'Host' => $this->config->getHost() . ':' . $this->config->getPort(),
113
+                'User-Agent' => 'Easy-Http/' . self::VERSION . ' (PHP/' . PHP_VERSION . ')',
114
+                'Sec-WebSocket-Key' => $key,
115
+            ]);
116
+
117
+            if ($this->config->getUser() || $this->config->getPassword()) {
118
+                $headers['authorization'] = 'Basic ' . base64_encode($this->config->getUser() . ':' . $this->config->getPassword()) . "\r\n";
119
+            }
120
+
121
+            if (!empty($this->config->getHeaders())) {
122
+                $headers = array_merge($headers, $this->config->getHeaders());
123
+            }
124
+
125
+            $header = $this->getHeaders($pathWithQuery, $headers);
126
+
127
+            $this->write($header);
128
+
129
+            $this->validateResponse($this->config, $pathWithQuery, $key);
130
+            $this->isConnected = true;
131
+            $this->whileIsConnected();
132
+
133
+        } catch (\Exception $e) {
134
+            $this->safeCall($this->onError, $this, new WebSocketException(
135
+                $e->getMessage(),
136
+                $e->getCode(),
137
+                $e->getPrevious()
138
+            ));
139
+        }
140
+    }
141
+
142
+    /**
143
+     * Reconnect to the Web Socket server
144
+     *
145
+     * @return void
146
+     * @throws \Exception
147
+     */
148
+    public function reconnect(): void
149
+    {
150
+        if ($this->isConnected) {
151
+            $this->close();
152
+        }
153
+
154
+        $this->connect($this->socketUrl, $this->config);
155
+    }
156
+
157
+    /**
158
+     * @return void
159
+     * @throws WebSocketException|\Exception
160
+     */
161
+    private function whileIsConnected(): void
162
+    {
163
+        $this->safeCall($this->onOpen, $this);
164
+
165
+        while ($this->isConnected() && $this->isClosing === false) {
166
+            $this->safeCall($this->onWhile, $this);
167
+
168
+            if (is_string(($message = $this->receive()))) {
169
+                $this->safeCall($this->onMessage, $this, $message);
170
+            }
171
+        }
172
+
173
+        $this->safeCall($this->onClose, $this, $this->closeStatus);
174
+    }
175
+
176
+    /**
177
+     * Execute events with safety of exceptions
178
+     *
179
+     * @param callable|null $callback
180
+     * @param mixed ...$args
181
+     * @return void
182
+     */
183
+    private function safeCall(?callable $callback, ...$args): void
184
+    {
185
+        if (is_callable($callback) && $callback) {
186
+            call_user_func($callback, ...$args);
187
+        }
188
+    }
189
+
190
+    /**
191
+     * Sends message to opened socket connection client->server
192
+     *
193
+     * @param $payload
194
+     * @param string $opcode
195
+     * @throws \Exception
196
+     */
197
+    public function send($payload, string $opcode = CommonsContract::EVENT_TYPE_TEXT): void
198
+    {
199
+        if (!$this->isConnected) {
200
+            throw new \Exception(
201
+                "Can't send message. Connection is not established.",
202
+                CommonsContract::CLIENT_CONNECTION_NOT_ESTABLISHED
203
+            );
204
+        }
205
+
206
+        if (array_key_exists($opcode, self::$opcodes) === false) {
207
+            throw new BadOpcodeException(
208
+                sprintf("Bad opcode '%s'.  Try 'text' or 'binary'.", $opcode),
209
+                CommonsContract::CLIENT_BAD_OPCODE
210
+            );
211
+        }
212
+
213
+        $payloadLength = strlen($payload);
214
+        $fragmentCursor = 0;
215
+
216
+        while ($payloadLength > $fragmentCursor) {
217
+            $subPayload = substr($payload, $fragmentCursor, $this->config->getFragmentSize());
218
+            $fragmentCursor += $this->config->getFragmentSize();
219
+            $final = $payloadLength <= $fragmentCursor;
220
+            $this->sendFragment($final, $subPayload, $opcode, true);
221
+            $opcode = 'continuation';
222
+        }
223
+    }
224
+
225
+    /**
226
+     * Receives message client<-server
227
+     *
228
+     * @return string|null
229
+     * @throws \Exception
230
+     */
231
+    public function receive(): string|null
232
+    {
233
+        if (!$this->isConnected && $this->isClosing === false) {
234
+            throw new WebSocketException(
235
+                "Your unexpectedly disconnected from the server",
236
+                CommonsContract::CLIENT_CONNECTION_NOT_ESTABLISHED
237
+            );
238
+        }
239
+
240
+        $this->hugePayload = '';
241
+
242
+        return $this->receiveFragment();
243
+    }
244
+
245
+    /**
246
+     * Tell the socket to close.
247
+     *
248
+     * @param integer $status https://github.com/Luka967/websocket-close-codes
249
+     * @param string $message A closing message, max 125 bytes.
250
+     * @return bool|null|string
251
+     * @throws \Exception
252
+     */
253
+    public function close(int $status = 1000, string $message = 'ttfn'): bool|null|string
254
+    {
255
+        $statusBin = sprintf('%016b', $status);
256
+        $statusStr = '';
257
+
258
+        foreach (str_split($statusBin, 8) as $binstr) {
259
+            $statusStr .= chr(bindec($binstr));
260
+        }
261
+
262
+        $this->send($statusStr . $message, CommonsContract::EVENT_TYPE_CLOSE);
263
+        $this->closeStatus = $status;
264
+        $this->isClosing = true;
265
+
266
+        return $this->receive(); // Receiving a close frame will close the socket now.
267
+    }
268 268
 
269 269
 }
270 270
\ No newline at end of file
Please login to merge, or discard this patch.