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 |
||
28 | class Request |
||
29 | { |
||
30 | /** |
||
31 | * @var ReactHttpClient |
||
32 | */ |
||
33 | protected $httpClient; |
||
34 | |||
35 | /** |
||
36 | * @var LoopInterface |
||
37 | */ |
||
38 | protected $loop; |
||
39 | |||
40 | /** |
||
41 | * @var HttpResponse |
||
42 | */ |
||
43 | protected $httpResponse; |
||
44 | |||
45 | /** |
||
46 | * @var string |
||
47 | */ |
||
48 | protected $buffer = ''; |
||
49 | |||
50 | /** |
||
51 | * @var Stream |
||
52 | */ |
||
53 | protected $stream; |
||
54 | |||
55 | /** |
||
56 | * @var \Exception |
||
57 | */ |
||
58 | protected $error = ''; |
||
59 | |||
60 | /** |
||
61 | * @var \React\EventLoop\Timer\TimerInterface |
||
62 | */ |
||
63 | protected $connectionTimer; |
||
64 | |||
65 | /** |
||
66 | * @var \React\EventLoop\Timer\TimerInterface |
||
67 | */ |
||
68 | protected $requestTimer; |
||
69 | |||
70 | /** |
||
71 | * @var ProgressInterface |
||
72 | */ |
||
73 | protected $progress; |
||
74 | |||
75 | /** |
||
76 | * @var Deferred |
||
77 | */ |
||
78 | protected $deferred; |
||
79 | |||
80 | /** |
||
81 | * @var array |
||
82 | */ |
||
83 | protected $options; |
||
84 | |||
85 | /** |
||
86 | * @var array |
||
87 | */ |
||
88 | protected $defaultOptions = [ |
||
89 | 'stream' => false, |
||
90 | 'connect_timeout' => 0, |
||
91 | 'timeout' => 0, |
||
92 | 'delay' => 0, |
||
93 | ]; |
||
94 | |||
95 | /** |
||
96 | * @var RequestInterface |
||
97 | */ |
||
98 | protected $request; |
||
99 | |||
100 | /** |
||
101 | * @var bool |
||
102 | */ |
||
103 | protected $connectionTimedOut = false; |
||
104 | |||
105 | /** |
||
106 | * @param RequestInterface $request |
||
107 | * @param array $options |
||
108 | * @param ReactHttpClient $httpClient |
||
109 | * @param LoopInterface $loop |
||
110 | |||
111 | */ |
||
112 | 3 | protected function __construct( |
|
123 | |||
124 | /** |
||
125 | * @param RequestInterface $request |
||
126 | * @param array $options |
||
127 | * @param ReactHttpClient $httpClient |
||
128 | * @param LoopInterface $loop |
||
129 | * @return \React\Promise\Promise |
||
130 | */ |
||
131 | 1 | public static function send( |
|
132 | RequestInterface $request, |
||
133 | array $options, |
||
134 | ReactHttpClient $httpClient, |
||
135 | LoopInterface $loop, |
||
136 | Request $requestObject = null |
||
137 | ) { |
||
138 | 1 | if ($requestObject === null) { |
|
139 | $requestObject = new static($request, $options, $httpClient, $loop); |
||
140 | } |
||
141 | 1 | return $requestObject->perform(); |
|
142 | } |
||
143 | |||
144 | /** |
||
145 | * @return \React\Promise\Promise |
||
146 | */ |
||
147 | 1 | protected function perform() |
|
148 | { |
||
149 | 1 | $this->deferred = new Deferred(); |
|
150 | |||
151 | 1 | $this->loop->addTimer( |
|
152 | 1 | (int)$this->options['delay'] / 1000, |
|
153 | function () { |
||
154 | 1 | $this->tickRequest(); |
|
155 | 1 | } |
|
156 | 1 | ); |
|
157 | |||
158 | 1 | return $this->deferred->promise(); |
|
159 | } |
||
160 | |||
161 | /** |
||
162 | * |
||
163 | */ |
||
164 | 1 | protected function tickRequest() |
|
179 | |||
180 | /** |
||
181 | * @return HttpRequest mixed |
||
182 | */ |
||
183 | 1 | protected function setupRequest() |
|
184 | { |
||
185 | 1 | $headers = []; |
|
186 | 1 | foreach ($this->request->getHeaders() as $key => $values) { |
|
187 | 1 | $headers[$key] = implode(';', $values); |
|
188 | 1 | } |
|
189 | |||
190 | 1 | return $this->httpClient->request( |
|
191 | 1 | $this->request->getMethod(), |
|
192 | 1 | (string)$this->request->getUri(), |
|
193 | 1 | $headers, |
|
194 | 1 | $this->request->getProtocolVersion() |
|
195 | 1 | ); |
|
196 | } |
||
197 | |||
198 | /** |
||
199 | * @param HttpRequest $request |
||
200 | */ |
||
201 | 1 | protected function setupListeners(HttpRequest $request) |
|
202 | { |
||
203 | 1 | $request->on( |
|
204 | 1 | 'headers-written', |
|
205 | function () { |
||
206 | $this->onHeadersWritten(); |
||
207 | } |
||
208 | 1 | ); |
|
209 | 1 | $request->on( |
|
210 | 1 | 'drain', |
|
211 | function () { |
||
212 | $this->progress->onSent(); |
||
213 | } |
||
214 | 1 | ); |
|
215 | 1 | $request->on( |
|
216 | 1 | 'response', |
|
217 | function (HttpResponse $response) use ($request) { |
||
218 | $this->onResponse($response, $request); |
||
219 | } |
||
220 | 1 | ); |
|
221 | 1 | $request->on( |
|
222 | 1 | 'error', |
|
223 | function ($error) { |
||
224 | $this->onError($error); |
||
225 | } |
||
226 | 1 | ); |
|
227 | 1 | $request->on( |
|
228 | 1 | 'end', |
|
229 | function () { |
||
230 | $this->onEnd(); |
||
231 | } |
||
232 | 1 | ); |
|
233 | 1 | } |
|
234 | |||
235 | /** |
||
236 | * @param HttpRequest $request |
||
237 | */ |
||
238 | 1 | View Code Duplication | public function setConnectionTimeout(HttpRequest $request) |
|
|||
239 | { |
||
240 | 1 | if ($this->options['connect_timeout'] > 0) { |
|
241 | 1 | $this->connectionTimer = $this->loop->addTimer( |
|
242 | 1 | $this->options['connect_timeout'], |
|
243 | function () use ($request) { |
||
244 | $request->closeError(new \Exception('Connection time out')); |
||
245 | } |
||
246 | 1 | ); |
|
247 | 1 | } |
|
248 | 1 | } |
|
249 | |||
250 | /** |
||
251 | * @param HttpRequest $request |
||
252 | */ |
||
253 | 2 | View Code Duplication | public function setRequestTimeout(HttpRequest $request) |
254 | { |
||
255 | 2 | if ($this->options['timeout'] > 0) { |
|
256 | 1 | $this->requestTimer = $this->loop->addTimer( |
|
257 | 1 | $this->options['timeout'], |
|
258 | function () use ($request) { |
||
259 | $request->closeError(new \Exception('Transaction time out')); |
||
260 | } |
||
261 | 1 | ); |
|
262 | 1 | } |
|
263 | 2 | } |
|
264 | |||
265 | protected function onHeadersWritten() |
||
266 | { |
||
267 | if ($this->connectionTimer !== null && $this->loop->isTimerActive($this->connectionTimer)) { |
||
268 | $this->loop->cancelTimer($this->connectionTimer); |
||
269 | } |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * @param HttpResponse $response |
||
274 | * @param HttpRequest $request |
||
275 | */ |
||
276 | protected function onResponse(HttpResponse $response, HttpRequest $request) |
||
277 | { |
||
278 | $this->httpResponse = $response; |
||
279 | if (isset($this->options['sink'])) { |
||
280 | $this->saveTo(); |
||
281 | } |
||
282 | |||
283 | $this->handleResponse($request); |
||
284 | } |
||
285 | |||
286 | protected function saveTo() |
||
287 | { |
||
288 | $saveTo = $this->options['sink']; |
||
289 | |||
290 | $writeStream = fopen($saveTo, 'w'); |
||
291 | stream_set_blocking($writeStream, 0); |
||
292 | $saveToStream = new ReactStream($writeStream, $this->loop); |
||
293 | |||
294 | $saveToStream->on( |
||
295 | 'end', |
||
296 | function () { |
||
297 | $this->onEnd(); |
||
298 | } |
||
299 | ); |
||
300 | |||
301 | $this->httpResponse->pipe($saveToStream); |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * @param string $data |
||
306 | */ |
||
307 | protected function onData($data) |
||
311 | |||
312 | /** |
||
313 | * @param \Exception $error |
||
314 | */ |
||
315 | View Code Duplication | protected function onError(\Exception $error) |
|
316 | { |
||
317 | if ($this->requestTimer !== null && $this->loop->isTimerActive($this->requestTimer)) { |
||
318 | $this->loop->cancelTimer($this->requestTimer); |
||
319 | } |
||
328 | |||
329 | /** |
||
330 | * |
||
331 | */ |
||
332 | View Code Duplication | protected function onEnd() |
|
348 | |||
349 | /** |
||
350 | * |
||
351 | */ |
||
352 | protected function handleResponse($request) |
||
374 | |||
375 | protected function resolveResponse($response) |
||
381 | |||
382 | protected function createStream($request) |
||
390 | |||
391 | 3 | private function applyOptions(array $options = []) |
|
414 | } |
||
415 |
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.