Complex classes like WscMain often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WscMain, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | class WscMain implements WscCommonsContract |
||
19 | { |
||
20 | use WSClientTrait; |
||
21 | |||
22 | private $socket; |
||
23 | private $isConnected = false; |
||
24 | private $isClosing = false; |
||
25 | private $lastOpcode; |
||
26 | private $closeStatus; |
||
27 | private $hugePayload; |
||
28 | |||
29 | private static $opcodes = [ |
||
30 | CommonsContract::EVENT_TYPE_CONTINUATION => 0, |
||
31 | CommonsContract::EVENT_TYPE_TEXT => 1, |
||
32 | CommonsContract::EVENT_TYPE_BINARY => 2, |
||
33 | CommonsContract::EVENT_TYPE_CLOSE => 8, |
||
34 | CommonsContract::EVENT_TYPE_PING => 9, |
||
35 | CommonsContract::EVENT_TYPE_PONG => 10, |
||
36 | ]; |
||
37 | |||
38 | protected $socketUrl = ''; |
||
39 | protected $config; |
||
40 | |||
41 | /** |
||
42 | * @throws \InvalidArgumentException |
||
43 | * @throws BadUriException |
||
44 | * @throws ConnectionException |
||
45 | * @throws \Exception |
||
46 | */ |
||
47 | protected function connect() |
||
48 | { |
||
49 | $urlParts = parse_url($this->socketUrl); |
||
50 | |||
51 | $this->config->setScheme($urlParts['scheme']); |
||
52 | $this->config->setHost($urlParts['host']); |
||
53 | $this->config->setUser($urlParts); |
||
|
|||
54 | $this->config->setPassword($urlParts); |
||
55 | $this->config->setPort($urlParts); |
||
56 | |||
57 | $pathWithQuery = $this->getPathWithQuery($urlParts); |
||
58 | $hostUri = $this->getHostUri($this->config); |
||
59 | |||
60 | // Set the stream context options if they're already set in the config |
||
61 | $context = $this->getStreamContext(); |
||
62 | |||
63 | |||
64 | if ($this->config->hasProxy()) { |
||
65 | $this->socket = $this->proxy($this->config); |
||
66 | } else { |
||
67 | $this->socket = @stream_socket_client( |
||
68 | $hostUri . ':' . $this->config->getPort(), |
||
69 | $errno, |
||
70 | $errstr, |
||
71 | $this->config->getTimeout(), |
||
72 | STREAM_CLIENT_CONNECT, |
||
73 | $context |
||
74 | ); |
||
75 | } |
||
76 | |||
77 | if ($this->socket === false) { |
||
78 | throw new ConnectionException( |
||
79 | "Could not open socket to \"{$this->config->getHost()}:{$this->config->getPort()}\": $errstr ($errno).", |
||
80 | CommonsContract::CLIENT_COULD_NOT_OPEN_SOCKET |
||
81 | ); |
||
82 | } |
||
83 | |||
84 | // Set timeout on the stream as well. |
||
85 | stream_set_timeout($this->socket, $this->config->getTimeout()); |
||
86 | |||
87 | // Generate the WebSocket key. |
||
88 | $key = $this->generateKey(); |
||
89 | $headers = [ |
||
90 | 'Host' => $this->config->getHost() . ':' . $this->config->getPort(), |
||
91 | 'User-Agent' => 'websocket-client-php', |
||
92 | 'Connection' => 'Upgrade', |
||
93 | 'Upgrade' => 'WebSocket', |
||
94 | 'Sec-WebSocket-Key' => $key, |
||
95 | 'Sec-Websocket-Version' => '13', |
||
96 | ]; |
||
97 | |||
98 | // Handle basic authentication. |
||
99 | if ($this->config->getUser() || $this->config->getPassword()) { |
||
100 | $headers['authorization'] = 'Basic ' . base64_encode($this->config->getUser() . ':' . $this->config->getPassword()) . "\r\n"; |
||
101 | } |
||
102 | |||
103 | // Add and override with headers from options. |
||
104 | if (!empty($this->config->getHeaders())) { |
||
105 | $headers = array_merge($headers, $this->config->getHeaders()); |
||
106 | } |
||
107 | |||
108 | $header = $this->getHeaders($pathWithQuery, $headers); |
||
109 | |||
110 | // Send headers. |
||
111 | $this->write($header); |
||
112 | |||
113 | // Get server response header |
||
114 | // @todo Handle version switching |
||
115 | $this->validateResponse($this->config, $pathWithQuery, $key); |
||
116 | $this->isConnected = true; |
||
117 | } |
||
118 | |||
119 | |||
120 | /** |
||
121 | * Init a proxy connection |
||
122 | * |
||
123 | * @param ClientConfig $config |
||
124 | * @return bool|resource |
||
125 | * @throws \InvalidArgumentException |
||
126 | * @throws \WSSC\Exceptions\ConnectionException |
||
127 | */ |
||
128 | private function proxy(ClientConfig $config) |
||
129 | { |
||
130 | $sock = @stream_socket_client( |
||
131 | WscCommonsContract::TCP_SCHEME . $config->getProxyIp() . ':' . $config->getProxyPort(), |
||
132 | $errno, |
||
133 | $errstr, |
||
134 | $this->config->getTimeout(), |
||
135 | STREAM_CLIENT_CONNECT, |
||
136 | $this->getStreamContext() |
||
137 | ); |
||
138 | |||
139 | $write = "CONNECT {$config->getHost()} HTTP/1.1\r\n"; |
||
140 | $auth = $config->getProxyAuth(); |
||
141 | if ($auth !== NULL) { |
||
142 | $write .= "Proxy-Authorization: Basic {$auth}\r\n"; |
||
143 | } |
||
144 | $write .= "\r\n"; |
||
145 | fwrite($sock, $write); |
||
146 | $resp = fread($sock, 1024); |
||
147 | |||
148 | if (preg_match('/^HTTP\/\d\.\d 200/', $resp) === 1) { |
||
149 | return $sock; |
||
150 | } |
||
151 | |||
152 | throw new ConnectionException('Failed to connect to the host via proxy'); |
||
153 | } |
||
154 | |||
155 | |||
156 | /** |
||
157 | * @return mixed|resource |
||
158 | * @throws \InvalidArgumentException |
||
159 | */ |
||
160 | private function getStreamContext() |
||
176 | |||
177 | /** |
||
178 | * @param mixed $urlParts |
||
179 | * @return string |
||
180 | */ |
||
181 | private function getPathWithQuery($urlParts): string |
||
196 | |||
197 | /** |
||
198 | * @param string $pathWithQuery |
||
199 | * @param array $headers |
||
200 | * @return string |
||
201 | */ |
||
202 | private function getHeaders(string $pathWithQuery, array $headers): string |
||
217 | |||
218 | /** |
||
219 | * @return string |
||
220 | */ |
||
221 | public function getLastOpcode(): string |
||
225 | |||
226 | /** |
||
227 | * @return int |
||
228 | */ |
||
229 | public function getCloseStatus(): int |
||
233 | |||
234 | /** |
||
235 | * @return bool |
||
236 | */ |
||
237 | public function isConnected(): bool |
||
241 | |||
242 | /** |
||
243 | * @param int $timeout |
||
244 | * @param null $microSecs |
||
245 | * @return WscMain |
||
246 | */ |
||
247 | public function setTimeout(int $timeout, $microSecs = null): WscMain |
||
256 | |||
257 | /** |
||
258 | * Sends message to opened socket connection client->server |
||
259 | * |
||
260 | * @param $payload |
||
261 | * @param string $opcode |
||
262 | * @throws \InvalidArgumentException |
||
263 | * @throws BadOpcodeException |
||
264 | * @throws BadUriException |
||
265 | * @throws ConnectionException |
||
266 | * @throws \Exception |
||
267 | */ |
||
268 | public function send($payload, $opcode = CommonsContract::EVENT_TYPE_TEXT) |
||
301 | |||
302 | /** |
||
303 | * Receives message client<-server |
||
304 | * |
||
305 | * @return null|string |
||
306 | * @throws \InvalidArgumentException |
||
307 | * @throws BadOpcodeException |
||
308 | * @throws BadUriException |
||
309 | * @throws ConnectionException |
||
310 | * @throws \Exception |
||
311 | */ |
||
312 | public function receive() |
||
326 | |||
327 | /** |
||
328 | * Tell the socket to close. |
||
329 | * |
||
330 | * @param integer $status http://tools.ietf.org/html/rfc6455#section-7.4 |
||
331 | * @param string $message A closing message, max 125 bytes. |
||
332 | * @return bool|null|string |
||
333 | * @throws \InvalidArgumentException |
||
334 | * @throws BadOpcodeException |
||
335 | * @throws BadUriException |
||
336 | * @throws ConnectionException |
||
337 | * @throws \Exception |
||
338 | */ |
||
339 | public function close(int $status = 1000, string $message = 'ttfn') |
||
353 | |||
354 | /** |
||
355 | * @param $data |
||
356 | * @throws ConnectionException |
||
357 | */ |
||
358 | protected function write(string $data) |
||
369 | |||
370 | /** |
||
371 | * @param int $len |
||
372 | * @return string |
||
373 | * @throws ConnectionException |
||
374 | */ |
||
375 | protected function read(int $len): string |
||
403 | |||
404 | /** |
||
405 | * Helper to convert a binary to a string of '0' and '1'. |
||
406 | * |
||
407 | * @param $string |
||
408 | * @return string |
||
409 | */ |
||
410 | protected static function sprintB(string $string): string |
||
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 |
||
438 | } |
||
439 |
This check looks for type mismatches where the missing type is
false
. This is usually indicative of an error condtion.Consider the follow example
This function either returns a new
DateTime
object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returnedfalse
before passing on the value to another function or method that may not be able to handle afalse
.