Complex classes like Frame 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 Frame, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class Frame |
||
29 | { |
||
30 | const OP_CONTINUE = 0; |
||
31 | const OP_TEXT = 1; |
||
32 | const OP_BINARY = 2; |
||
33 | const OP_CLOSE = 8; |
||
34 | const OP_PING = 9; |
||
35 | const OP_PONG = 10; |
||
36 | |||
37 | // To understand codes, please refer to RFC: |
||
38 | // https://tools.ietf.org/html/rfc6455#section-7.4 |
||
39 | const CLOSE_NORMAL = 1000; |
||
40 | const CLOSE_GOING_AWAY = 1001; |
||
41 | const CLOSE_PROTOCOL_ERROR = 1002; |
||
42 | const CLOSE_WRONG_DATA = 1003; |
||
43 | // 1004-1006 are reserved |
||
44 | const CLOSE_INCOHERENT_DATA = 1007; |
||
45 | const CLOSE_POLICY_VIOLATION = 1008; |
||
46 | const CLOSE_TOO_BIG_TO_PROCESS = 1009; |
||
47 | const CLOSE_MISSING_EXTENSION = 1010; // In this case you should precise a reason |
||
48 | const CLOSE_UNEXPECTING_CONDITION = 1011; |
||
49 | // 1015 is reserved |
||
50 | |||
51 | /** |
||
52 | * @see https://tools.ietf.org/html/rfc6455#section-5.5 |
||
53 | */ |
||
54 | const MAX_CONTROL_FRAME_SIZE = 125; |
||
55 | |||
56 | /** |
||
57 | * The payload size can be specified on 64b unsigned int according to the RFC. That means that maximum data |
||
58 | * inside the payload is 0b1111111111111111111111111111111111111111111111111111111111111111 bits. In |
||
59 | * decimal and GB, that means 2147483647 GB. As this is a bit too much for the memory of your computer or |
||
60 | * server, we specified a max size to. |
||
61 | * |
||
62 | * Notice that to support larger transfer we need to implemente a cache strategy on the harddrive. It also suggest |
||
63 | * to have a threaded environment as the task of retrieving the data and treat it will be long. |
||
64 | * |
||
65 | * This value is in bytes. Here we allow 0.5 MiB. |
||
66 | * |
||
67 | * @var int |
||
68 | */ |
||
69 | private static $defaultMaxPayloadSize = 524288; |
||
70 | |||
71 | /** |
||
72 | * Complete string representing data collected from socket |
||
73 | * |
||
74 | * @var string |
||
75 | */ |
||
76 | private $rawData; |
||
77 | |||
78 | /** |
||
79 | * @var int |
||
80 | */ |
||
81 | private $frameSize; |
||
82 | |||
83 | // In case of enter request the following data is cache. |
||
84 | // Otherwise it's data used to generate "rawData". |
||
85 | |||
86 | /** |
||
87 | * @var int |
||
88 | */ |
||
89 | private $firstByte; |
||
90 | |||
91 | /** |
||
92 | * @var int |
||
93 | */ |
||
94 | private $secondByte; |
||
95 | |||
96 | /** |
||
97 | * @var bool |
||
98 | */ |
||
99 | private $final; |
||
100 | |||
101 | /** |
||
102 | * @var int |
||
103 | */ |
||
104 | private $payloadLen; |
||
105 | |||
106 | /** |
||
107 | * Number of bits representing the payload length in the current frame. |
||
108 | * |
||
109 | * @var int |
||
110 | */ |
||
111 | private $payloadLenSize; |
||
112 | |||
113 | /** |
||
114 | * Cache variable for the payload. |
||
115 | * |
||
116 | * @var string |
||
117 | */ |
||
118 | private $payload; |
||
119 | |||
120 | /** |
||
121 | * @var |
||
122 | */ |
||
123 | private $mask; |
||
124 | |||
125 | /** |
||
126 | * @var int |
||
127 | */ |
||
128 | private $opcode; |
||
129 | |||
130 | /** |
||
131 | * @var int |
||
132 | */ |
||
133 | private $infoBytesLen; |
||
134 | |||
135 | /** |
||
136 | * @see Frame::setConfig() for full default configuration. |
||
137 | * |
||
138 | * @var array |
||
139 | */ |
||
140 | private $config; |
||
141 | |||
142 | /** |
||
143 | * You should construct this object by using the FrameFactory class instead of this direct constructor. |
||
144 | * |
||
145 | * @param string|null $data |
||
146 | * @param array $config |
||
147 | * @internal |
||
148 | */ |
||
149 | 82 | public function __construct(string $data = null, array $config = []) |
|
156 | |||
157 | /** |
||
158 | * It also run checks on data. |
||
159 | * |
||
160 | * @param string|int $rawData Probably more likely a string than an int, but well... why not. |
||
161 | * @return self |
||
162 | * @throws IncompleteFrameException |
||
163 | */ |
||
164 | 74 | public function setRawData(string $rawData) |
|
185 | |||
186 | /** |
||
187 | * Return cached raw data or generate it from data (payload/frame type). |
||
188 | * |
||
189 | * @return string |
||
190 | * @throws InvalidFrameException |
||
191 | */ |
||
192 | 23 | public function getRawData() : string |
|
232 | |||
233 | /** |
||
234 | * As a message is composed by many frames, the frame have the information of "last" or not. |
||
235 | * The frame is final if the first bit is 0. |
||
236 | */ |
||
237 | 62 | public function isFinal() : bool |
|
241 | |||
242 | /** |
||
243 | * @param bool $final |
||
244 | * @return Frame |
||
245 | */ |
||
246 | 3 | public function setFinal(bool $final) : Frame |
|
252 | |||
253 | /** |
||
254 | * @return boolean |
||
255 | */ |
||
256 | 10 | public function getRsv1() : bool |
|
260 | |||
261 | /** |
||
262 | * @return boolean |
||
263 | */ |
||
264 | 9 | public function getRsv2() : bool |
|
268 | |||
269 | /** |
||
270 | * @return boolean |
||
271 | */ |
||
272 | 9 | public function getRsv3() : bool |
|
276 | |||
277 | /** |
||
278 | * @return int |
||
279 | */ |
||
280 | 71 | public function getOpcode() : int |
|
284 | |||
285 | /** |
||
286 | * Set the opcode of the frame. |
||
287 | * |
||
288 | * @param int $opcode |
||
289 | * @return Frame |
||
290 | */ |
||
291 | 9 | public function setOpcode(int $opcode) : Frame |
|
302 | |||
303 | /** |
||
304 | * Set the masking key of the frame. As a consequence the frame is now considered as masked. |
||
305 | * |
||
306 | * @param string $mask |
||
307 | * @return Frame |
||
308 | */ |
||
309 | 1 | public function setMaskingKey(string $mask) : Frame |
|
319 | |||
320 | /** |
||
321 | * Get the masking key (from cache if possible). |
||
322 | * |
||
323 | * @return string |
||
324 | */ |
||
325 | 10 | public function getMaskingKey() : string |
|
345 | |||
346 | /** |
||
347 | * Get payload from cache or generate it from raw data. |
||
348 | * |
||
349 | * @return string |
||
350 | */ |
||
351 | 37 | public function getPayload() |
|
368 | |||
369 | /** |
||
370 | * Returns the content and not potential metadata of the body. |
||
371 | * If you want to get the real body you will prefer using `getPayload` |
||
372 | * |
||
373 | * @return string |
||
374 | */ |
||
375 | 26 | public function getContent() |
|
389 | |||
390 | /** |
||
391 | * Get length of meta data of the frame. |
||
392 | * Metadata contains type of frame, length, masking key and rsv data. |
||
393 | * |
||
394 | * @return int |
||
395 | */ |
||
396 | 72 | public function getInfoBytesLen() |
|
406 | |||
407 | /** |
||
408 | * Set the payload. |
||
409 | * The raw data is reset. |
||
410 | * |
||
411 | * @param string $payload |
||
412 | * @return Frame |
||
413 | */ |
||
414 | 9 | public function setPayload(string $payload) : Frame |
|
429 | |||
430 | /** |
||
431 | * @return int |
||
432 | * @throws TooBigFrameException |
||
433 | */ |
||
434 | 73 | public function getPayloadLength() : int |
|
468 | |||
469 | /** |
||
470 | * If there is a mask in the raw data or if the mask was set, the frame is masked and this method gets the result |
||
471 | * for you. |
||
472 | * |
||
473 | * @return bool |
||
474 | */ |
||
475 | 78 | public function isMasked() : bool |
|
487 | |||
488 | /** |
||
489 | * This method works for mask and unmask (it's the same operation) |
||
490 | * |
||
491 | * @return string |
||
492 | */ |
||
493 | 10 | public function applyMask() : string |
|
505 | |||
506 | /** |
||
507 | * Fill metadata of the frame from the raw data. |
||
508 | */ |
||
509 | 73 | private function getInformationFromRawData() |
|
517 | |||
518 | /** |
||
519 | * Check if the frame have the good size based on payload size. |
||
520 | * |
||
521 | * @throws IncompleteFrameException |
||
522 | * @throws TooBigFrameException |
||
523 | */ |
||
524 | 72 | public function checkFrameSize() |
|
544 | |||
545 | |||
546 | /** |
||
547 | * Validate a frame with RFC criteria |
||
548 | * |
||
549 | * @param Frame $frame |
||
550 | * |
||
551 | * @throws ControlFrameException |
||
552 | * @throws TooBigControlFrameException |
||
553 | */ |
||
554 | 70 | public static function checkFrame(Frame $frame) |
|
566 | |||
567 | /** |
||
568 | * You can call this method to be sure your frame is valid before trying to get the raw data. |
||
569 | * |
||
570 | * @return bool |
||
571 | */ |
||
572 | 8 | public function isValid() : bool |
|
576 | |||
577 | /** |
||
578 | * The Control Frame is a pong, ping, close frame or a reserved frame between 0xB-0xF. |
||
579 | * @see https://tools.ietf.org/html/rfc6455#section-5.5 |
||
580 | * |
||
581 | * @return bool |
||
582 | */ |
||
583 | 70 | public function isControlFrame() |
|
587 | |||
588 | /** |
||
589 | * @param array $config |
||
590 | * @return Frame |
||
591 | */ |
||
592 | 82 | public function setConfig(array $config = []) |
|
600 | } |
||
601 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.