Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Connection 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 Connection, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
9 | class Connection extends \PHPDaemon\Network\Connection { |
||
10 | use \PHPDaemon\Traits\DeferredEventHandlers; |
||
11 | use \PHPDaemon\Traits\Sessions; |
||
12 | |||
13 | /** |
||
14 | * @var integer Timeout |
||
15 | */ |
||
16 | protected $timeout = 120; |
||
17 | |||
18 | protected $handshaked = false; |
||
19 | protected $route; |
||
20 | protected $writeReady = true; |
||
21 | protected $extensions = []; |
||
22 | protected $extensionsCleanRegex = '/(?:^|\W)x-webkit-/iS'; |
||
23 | |||
24 | protected $headers = []; |
||
25 | protected $headers_sent = false; |
||
26 | |||
27 | /** |
||
28 | * @var array _SERVER |
||
29 | */ |
||
30 | public $server = []; |
||
31 | |||
32 | /** |
||
33 | * @var array _COOKIE |
||
34 | */ |
||
35 | public $cookie = []; |
||
36 | |||
37 | /** |
||
38 | * @var array _GET |
||
39 | */ |
||
40 | public $get = []; |
||
41 | |||
42 | |||
43 | protected $policyReqNotFound = false; |
||
44 | protected $currentHeader; |
||
45 | protected $EOL = "\r\n"; |
||
46 | |||
47 | /** |
||
48 | * @var boolean Is this connection running right now? |
||
49 | */ |
||
50 | protected $running = false; |
||
51 | |||
52 | /** |
||
53 | * State: first line |
||
54 | */ |
||
55 | const STATE_FIRSTLINE = 1; |
||
56 | |||
57 | /** |
||
58 | * State: headers |
||
59 | */ |
||
60 | const STATE_HEADERS = 2; |
||
61 | |||
62 | /** |
||
63 | * State: content |
||
64 | */ |
||
65 | const STATE_CONTENT = 3; |
||
66 | |||
67 | /** |
||
68 | * State: prehandshake |
||
69 | */ |
||
70 | const STATE_PREHANDSHAKE = 5; |
||
71 | |||
72 | /** |
||
73 | * State: handshaked |
||
74 | */ |
||
75 | const STATE_HANDSHAKED = 6; |
||
76 | |||
77 | const STRING = NULL; |
||
78 | |||
79 | const BINARY = NULL; |
||
80 | |||
81 | /** |
||
82 | * @var integer Content length from header() method |
||
83 | */ |
||
84 | protected $contentLength; |
||
85 | |||
86 | /** |
||
87 | * @var integer Number of outgoing cookie-headers |
||
88 | */ |
||
89 | protected $cookieNum = 0; |
||
90 | |||
91 | /** |
||
92 | * @var array Replacement pairs for processing some header values in parse_str() |
||
93 | */ |
||
94 | public static $hvaltr = ['; ' => '&', ';' => '&', ' ' => '%20']; |
||
95 | |||
96 | /** |
||
97 | * Called when the stream is handshaked (at low-level), and peer is ready to recv. data |
||
98 | * @return void |
||
99 | */ |
||
100 | public function onReady() { |
||
103 | |||
104 | /** |
||
105 | * Get real frame type identificator |
||
106 | * @param $type |
||
107 | * @return integer |
||
108 | */ |
||
109 | public function getFrameType($type) { |
||
122 | |||
123 | |||
124 | /** |
||
125 | * Called when connection is inherited from HTTP request |
||
126 | * @param object $req |
||
127 | * @return void |
||
128 | */ |
||
129 | View Code Duplication | public function onInheritanceFromRequest($req) { |
|
137 | |||
138 | /** |
||
139 | * Sends a frame. |
||
140 | * @param string $data Frame's data. |
||
141 | * @param string $type Frame's type. ("STRING" OR "BINARY") |
||
142 | * @param callable $cb Optional. Callback called when the frame is received by client. |
||
143 | * @callback $cb ( ) |
||
144 | * @return boolean Success. |
||
145 | */ |
||
146 | public function sendFrame($data, $type = null, $cb = null) { |
||
149 | |||
150 | /** |
||
151 | * Event of Connection. |
||
152 | * @return void |
||
153 | */ |
||
154 | public function onFinish() { |
||
163 | |||
164 | /** |
||
165 | * Uncaught exception handler |
||
166 | * @param Exception $e |
||
167 | * @return boolean Handled? |
||
168 | */ |
||
169 | public function handleException($e) { |
||
175 | |||
176 | /** |
||
177 | * Called when new frame received. |
||
178 | * @param string $data Frame's data. |
||
179 | * @param string $type Frame's type ("STRING" OR "BINARY"). |
||
180 | * @return boolean Success. |
||
181 | */ |
||
182 | public function onFrame($data, $type) { |
||
195 | |||
196 | /** |
||
197 | * Called when the worker is going to shutdown. |
||
198 | * @return boolean Ready to shutdown ? |
||
199 | */ |
||
200 | public function gracefulShutdown() { |
||
207 | |||
208 | |||
209 | /** |
||
210 | * Called when we're going to handshake. |
||
211 | * @return boolean Handshake status |
||
212 | */ |
||
213 | public function handshake() { |
||
241 | |||
242 | protected function handshakeAfter($extraHeaders = '') { |
||
259 | |||
260 | /** |
||
261 | * Send Bad request |
||
262 | * @return void |
||
263 | */ |
||
264 | public function badRequest() { |
||
269 | |||
270 | /** |
||
271 | * Read first line of HTTP request |
||
272 | * @return boolean|null Success |
||
273 | */ |
||
274 | protected function httpReadFirstline() { |
||
304 | |||
305 | /** |
||
306 | * Read headers line-by-line |
||
307 | * @return boolean|null Success |
||
308 | */ |
||
309 | protected function httpReadHeaders() { |
||
331 | |||
332 | /** |
||
333 | * Called when new data received. |
||
334 | * @return void |
||
335 | */ |
||
336 | protected function onRead() { |
||
381 | |||
382 | /** |
||
383 | * Process headers |
||
384 | * @return bool |
||
385 | */ |
||
386 | protected function httpProcessHeaders() { |
||
434 | |||
435 | protected function switchToProtocol($proto) { |
||
444 | |||
445 | View Code Duplication | public function onInheritance($conn) { |
|
453 | |||
454 | |||
455 | /** |
||
456 | * Send HTTP-status |
||
457 | * @throws RequestHeadersAlreadySent |
||
458 | * @param integer $code Code |
||
459 | * @return boolean Success |
||
460 | */ |
||
461 | public function status($code = 200) { |
||
464 | |||
465 | /** |
||
466 | * Send the header |
||
467 | * @param string $s Header. Example: 'Location: http://php.net/' |
||
468 | * @param boolean $replace Optional. Replace? |
||
469 | * @param boolean $code Optional. HTTP response code |
||
470 | * @throws \PHPDaemon\Request\RequestHeadersAlreadySent |
||
471 | * @return boolean Success |
||
472 | */ |
||
473 | View Code Duplication | public function header($s, $replace = true, $code = false) { |
|
530 | } |
||
531 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.