Complex classes like Client 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 Client, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
27 | class Client extends AbstractClient implements ClientInterface, LoggerAwareInterface |
||
28 | { |
||
29 | use LoggerAwareTrait; |
||
30 | |||
31 | protected $socket; |
||
32 | protected $chunk_size; |
||
33 | protected $verify_peer_name; |
||
34 | |||
35 | public function __construct(array $config, ObjectSpec $objectSpec = null) |
||
36 | { |
||
37 | parent::__construct($config, $objectSpec); |
||
38 | |||
39 | if (!empty($config['chunk_size'])) { |
||
40 | $this->chunk_size = (int) $config['chunk_size']; |
||
41 | } else { |
||
42 | $this->chunk_size = 1024; |
||
43 | } |
||
44 | |||
45 | if (isset($config['verify_peer_name'])) { |
||
46 | $this->verify_peer_name = (bool) $config['verify_peer_name']; |
||
47 | } else { |
||
48 | $this->verify_peer_name = true; |
||
49 | } |
||
50 | |||
51 | if ($this->port === false) { |
||
52 | // if not set, default port is 700 |
||
53 | $this->port = 700; |
||
54 | } |
||
55 | } |
||
56 | |||
57 | public function __destruct() |
||
58 | { |
||
59 | $this->close(); |
||
60 | } |
||
61 | |||
62 | /** |
||
63 | * Setup context in case of ssl connection |
||
64 | * |
||
65 | * @return resource|null |
||
66 | */ |
||
67 | private function setupContext() |
||
68 | { |
||
69 | if (!$this->ssl) { |
||
70 | return null; |
||
71 | } |
||
72 | |||
73 | $context = stream_context_create(); |
||
74 | |||
75 | $options_array = [ |
||
76 | 'verify_peer' => false, |
||
77 | 'verify_peer_name' => $this->verify_peer_name, |
||
78 | 'allow_self_signed' => true, |
||
79 | 'local_cert' => $this->local_cert, |
||
80 | 'passphrase' => $this->passphrase, |
||
81 | 'cafile' => $this->ca_cert, |
||
82 | 'local_pk' => $this->pk_cert, |
||
83 | ]; |
||
84 | |||
85 | // filter out empty user provided values |
||
86 | $options_array = array_filter( |
||
87 | $options_array, |
||
88 | function ($var) { |
||
89 | return !is_null($var); |
||
90 | } |
||
91 | ); |
||
92 | |||
93 | $options = ['ssl' => $options_array]; |
||
94 | |||
95 | // stream_context_set_option accepts array of options in form of $arr['wrapper']['option'] = value |
||
96 | stream_context_set_option($context, $options); |
||
97 | |||
98 | return $context; |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * Setup connection socket |
||
103 | * |
||
104 | * @param resource|null $context SSL context or null in case of tcp connection |
||
105 | * |
||
106 | * @throws Exception on socket errors |
||
107 | */ |
||
108 | private function setupSocket($context = null) |
||
109 | { |
||
110 | $proto = $this->ssl ? 'ssl' : 'tcp'; |
||
111 | $target = sprintf('%s://%s:%d', $proto, $this->host, $this->port); |
||
112 | |||
113 | $errno = 0; |
||
114 | $errstr = ''; |
||
115 | |||
116 | $this->socket = @stream_socket_client($target, $errno, $errstr, $this->connect_timeout, STREAM_CLIENT_CONNECT, $context); |
||
117 | |||
118 | if ($this->socket === false) { |
||
119 | // Socket initialization may fail, before system call connect() |
||
120 | // so the $errno is 0 and $errstr isn't populated . |
||
121 | // see https://www.php.net/manual/en/function.stream-socket-client.php#refsect1-function.stream-socket-client-errors |
||
122 | throw new Exception(sprintf('problem initializing socket: %s code: [%d]', $errstr, $errno), $errno); |
||
123 | } |
||
124 | |||
125 | // set stream time out |
||
126 | if (!stream_set_timeout($this->socket, $this->timeout)) { |
||
127 | throw new Exception('unable to set stream timeout'); |
||
128 | } |
||
129 | |||
130 | // set to non-blocking |
||
131 | if (!stream_set_blocking($this->socket, 0)) { |
||
132 | throw new Exception('unable to set blocking'); |
||
133 | } |
||
134 | } |
||
135 | |||
136 | /** |
||
137 | * {@inheritdoc} |
||
138 | * |
||
139 | * @see \AfriCC\EPP\ClientInterface::connect() |
||
140 | */ |
||
141 | public function connect($newPassword = false) |
||
142 | { |
||
143 | $context = $this->setupContext(); |
||
144 | $this->setupSocket($context); |
||
145 | |||
146 | // get greeting |
||
147 | $greeting = $this->getFrame(); |
||
148 | |||
149 | // login |
||
150 | $this->login($newPassword); |
||
151 | |||
152 | // return greeting |
||
153 | return $greeting; |
||
154 | } |
||
155 | |||
156 | /** |
||
157 | * Closes a previously opened EPP connection |
||
158 | */ |
||
159 | public function close() |
||
160 | { |
||
161 | if ($this->active()) { |
||
162 | // send logout frame |
||
163 | $this->request(new LogoutCommand($this->objectSpec)); |
||
164 | |||
165 | return fclose($this->socket); |
||
166 | } |
||
167 | |||
168 | return false; |
||
169 | } |
||
170 | |||
171 | public function getFrame() |
||
172 | { |
||
173 | $hard_time_limit = time() + $this->timeout + 2; |
||
174 | do { |
||
175 | $header = $this->recv(4); |
||
176 | } while (empty($header) && (time() < $hard_time_limit)); |
||
177 | |||
178 | if (time() >= $hard_time_limit) { |
||
179 | throw new Exception('Timeout while reading header from EPP Server'); |
||
180 | } |
||
181 | |||
182 | // Unpack first 4 bytes which is our length |
||
183 | $unpacked = unpack('N', $header); |
||
184 | $length = $unpacked[1]; |
||
185 | |||
186 | if ($length < 5) { |
||
187 | throw new Exception(sprintf('Got a bad frame header length of %d bytes from peer', $length)); |
||
188 | } else { |
||
189 | $length -= 4; |
||
190 | |||
191 | return $this->recv($length); |
||
192 | } |
||
193 | } |
||
194 | |||
195 | public function sendFrame(FrameInterface $frame) |
||
196 | { |
||
197 | $buffer = (string) $frame; |
||
198 | $header = pack('N', mb_strlen($buffer, 'ASCII') + 4); |
||
199 | |||
200 | return $this->send($header . $buffer); |
||
201 | } |
||
202 | |||
203 | protected function debugLog($message, $color = '0;32') |
||
204 | { |
||
205 | if ($message === '' || !$this->debug) { |
||
206 | return; |
||
207 | } |
||
208 | echo sprintf("\033[%sm%s\033[0m", $color, $message); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * check if socket is still active |
||
213 | * |
||
214 | * @return bool |
||
215 | */ |
||
216 | private function active() |
||
220 | |||
221 | /** |
||
222 | * receive socket data |
||
223 | * |
||
224 | * @param int $length |
||
225 | * |
||
226 | * @throws Exception |
||
227 | * |
||
228 | * @return string |
||
229 | */ |
||
230 | private function recv($length) |
||
269 | |||
270 | /** |
||
271 | * send data to socket |
||
272 | * |
||
273 | * @param string $buffer |
||
274 | */ |
||
275 | private function send($buffer) |
||
327 | } |
||
328 |