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:
1 | <?php |
||
13 | class AttachWebsocketStream |
||
14 | { |
||
15 | /** @var resource The underlying socket */ |
||
16 | private $socket; |
||
17 | |||
18 | 1 | public function __construct(StreamInterface $stream) |
|
22 | |||
23 | /** |
||
24 | * Send input to the container |
||
25 | * |
||
26 | * @param string $data Data to send |
||
27 | */ |
||
28 | 1 | public function write($data) |
|
29 | { |
||
30 | 1 | $rand = rand(0, 28); |
|
31 | $frame = [ |
||
32 | 1 | 'fin' => 1, |
|
33 | 1 | 'rsv1' => 0, |
|
34 | 1 | 'rsv2' => 0, |
|
35 | 1 | 'rsv3' => 0, |
|
36 | 1 | 'opcode' => 1, // We always send text |
|
37 | 1 | 'mask' => 1, |
|
38 | 1 | 'len' => strlen($data), |
|
39 | 1 | 'mask_key' => substr(md5(uniqid()), $rand, 4), |
|
40 | 1 | 'data' => $data, |
|
41 | 1 | ]; |
|
42 | |||
43 | 1 | View Code Duplication | if ($frame['mask'] == 1) { |
|
|||
44 | 1 | for ($i = 0; $i < $frame['len']; $i++) { |
|
45 | 1 | $frame['data']{$i} |
|
46 | 1 | = chr(ord($frame['data']{$i}) ^ ord($frame['mask_key']{$i % 4})); |
|
47 | 1 | } |
|
48 | 1 | } |
|
49 | |||
50 | 1 | if ($frame['len'] > pow(2, 16)) { |
|
51 | $len = 127; |
||
52 | 1 | } elseif ($frame['len'] > 125) { |
|
53 | $len = 126; |
||
54 | } else { |
||
55 | 1 | $len = $frame['len']; |
|
56 | } |
||
57 | |||
58 | 1 | $firstByte = ($frame['fin'] << 7) | (($frame['rsv1'] << 7) >> 1) | (($frame['rsv2'] << 7) >> 2) | (($frame['rsv3'] << 7) >> 3) | (($frame['opcode'] << 4) >> 4); |
|
59 | 1 | $secondByte = ($frame['mask'] << 7) | (($len << 1) >> 1); |
|
60 | |||
61 | 1 | $this->socketWrite(chr($firstByte)); |
|
62 | 1 | $this->socketWrite(chr($secondByte)); |
|
63 | |||
64 | 1 | if ($len == 126) { |
|
65 | $this->socketWrite(pack('n', $frame['len'])); |
||
66 | 1 | } elseif ($len == 127) { |
|
67 | $higher = $frame['len'] >> 32; |
||
68 | $lower = ($frame['len'] << 32) >> 32; |
||
69 | $this->socketWrite(pack('N', $higher)); |
||
70 | $this->socketWrite(pack('N', $lower)); |
||
71 | } |
||
72 | |||
73 | 1 | if ($frame['mask'] == 1) { |
|
74 | 1 | $this->socketWrite($frame['mask_key']); |
|
75 | 1 | } |
|
76 | |||
77 | 1 | $this->socketWrite($frame['data']); |
|
78 | 1 | } |
|
79 | |||
80 | /** |
||
81 | * Block until it receive a frame from websocket or return null if no more connexion |
||
82 | * |
||
83 | * @param int $waitTime Time to wait in seconds before return false |
||
84 | * @param int $waitMicroTime Time to wait in microseconds before return false |
||
85 | * @param boolean $getFrame Whether to return the frame of websocket or only the data |
||
86 | * |
||
87 | * @return null|false|string|array Null for socket not available, false for no message, string for the last message and the frame array if $getFrame is set to true |
||
88 | */ |
||
89 | 1 | public function read($waitTime = 0, $waitMicroTime = 200000, $getFrame = false) |
|
90 | { |
||
91 | 1 | if (!is_resource($this->socket) || feof($this->socket)) { |
|
92 | return null; |
||
93 | } |
||
94 | |||
95 | 1 | $read = [$this->socket]; |
|
96 | 1 | $write = null; |
|
97 | 1 | $expect = null; |
|
98 | |||
99 | 1 | if (stream_select($read, $write, $expect, $waitTime, $waitMicroTime) == 0) { |
|
100 | 1 | return false; |
|
101 | } |
||
102 | |||
103 | 1 | $firstByte = $this->socketRead(1); |
|
104 | 1 | $frame = []; |
|
105 | 1 | $firstByte = ord($firstByte); |
|
106 | 1 | $secondByte = ord($this->socketRead(1)); |
|
107 | |||
108 | // First byte decoding |
||
109 | 1 | $frame['fin'] = ($firstByte & 128) >> 7; |
|
110 | 1 | $frame['rsv1'] = ($firstByte & 64) >> 6; |
|
111 | 1 | $frame['rsv2'] = ($firstByte & 32) >> 5; |
|
112 | 1 | $frame['rsv3'] = ($firstByte & 16) >> 4; |
|
113 | 1 | $frame['opcode'] = ($firstByte & 15); |
|
114 | |||
115 | // Second byte decoding |
||
116 | 1 | $frame['mask'] = ($secondByte & 128) >> 7; |
|
117 | 1 | $frame['len'] = ($secondByte & 127); |
|
118 | |||
119 | // Get length of the frame |
||
120 | 1 | if ($frame['len'] == 126) { |
|
121 | $frame['len'] = unpack('n', $this->socketRead(2))[1]; |
||
122 | 1 | } elseif ($frame['len'] == 127) { |
|
123 | list($higher, $lower) = array_values(unpack('N2', $this->socketRead(8))); |
||
124 | $frame['len'] = ($higher << 32) | $lower; |
||
125 | } |
||
126 | |||
127 | // Get the mask key if needed |
||
128 | 1 | if ($frame['mask'] == 1) { |
|
129 | $frame['mask_key'] = $this->socketRead(4); |
||
130 | } |
||
131 | |||
132 | 1 | $frame['data'] = $this->socketRead($frame['len']); |
|
133 | |||
134 | // Decode data if needed |
||
135 | 1 | View Code Duplication | if ($frame['mask'] == 1) { |
136 | for ($i = 0; $i < $frame['len']; $i++) { |
||
137 | $frame['data']{$i} = chr(ord($frame['data']{$i}) ^ ord($frame['mask_key']{$i % 4})); |
||
138 | } |
||
139 | } |
||
140 | |||
141 | 1 | if ($getFrame) { |
|
142 | return $frame; |
||
143 | } |
||
144 | |||
145 | 1 | return (string)$frame['data']; |
|
146 | } |
||
147 | |||
148 | /** |
||
149 | * Force to have something of the expected size (block) |
||
150 | * |
||
151 | * @param $length |
||
152 | * |
||
153 | * @return string |
||
154 | */ |
||
155 | 1 | private function socketRead($length) |
|
156 | { |
||
157 | 1 | $read = ""; |
|
158 | |||
159 | do { |
||
160 | 1 | $read .= fread($this->socket, $length - strlen($read)); |
|
161 | 1 | } while (strlen($read) < $length && !feof($this->socket)); |
|
162 | |||
163 | 1 | return $read; |
|
164 | } |
||
165 | |||
166 | /** |
||
167 | * Write to the socket |
||
168 | * |
||
169 | * @param $data |
||
170 | * |
||
171 | * @return int |
||
172 | */ |
||
173 | 1 | private function socketWrite($data) |
|
177 | } |
||
178 |
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.