Request   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 312
Duplicated Lines 6.41 %

Coupling/Cohesion

Components 2
Dependencies 0

Importance

Changes 18
Bugs 1 Features 1
Metric Value
wmc 43
c 18
b 1
f 1
lcom 2
cbo 0
dl 20
loc 312
rs 8.3157

25 Methods

Rating   Name   Duplication   Size   Complexity  
A camelize() 0 4 1
A __set() 10 10 2
A __get() 10 10 2
A __construct() 0 9 1
A __destruct() 0 6 2
A __clone() 0 6 2
A normalize() 0 12 3
A setUrl() 0 6 2
A getUrl() 0 4 1
A setPostData() 0 8 2
A getPostData() 0 4 1
A getTime() 0 7 2
A getResult() 0 4 1
A callBack() 0 4 1
A addListener() 0 8 3
A notify() 0 6 2
A setTimeout() 0 7 2
A getTimeout() 0 4 1
A getHandle() 0 8 2
A setHeaders() 0 5 1
A getHeaders() 0 4 1
A setOptions() 0 4 1
A getOptions() 0 4 1
A getResponse() 0 9 3
A getHttpCode() 0 9 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

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 Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Request 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 Request, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace CurlX;
4
5
/**
6
 * Class Request
7
 * @package CurlX
8
 *
9
 * @property string $url url of the Request
10
 * @property array $post_data array of post data
11
 * @property float $total_time running time of the request
12
 * @property int $timeout time (in msec) after which the request will be aborted
13
 * @property array $options cUrl options of the request
14
 * @property array $headers headers of the request
15
 * @property resource $handle cUrl handle of the request
16
 * @property callable[] $listeners array of registered listeners which will be called upon when request finishes
17
 * @property mixed $response curl's response
18
 * @property int $http_code the http code of the response
19
 */
20
class Request implements RequestInterface
21
{
22
    protected $url;
23
    protected $post = [];
24
    protected $result;
25
    protected $listeners = [];
26
    protected $timeout;
27
    protected $curlHandle;
28
    protected $headers = [];
29
    protected $options = [];
30
    protected $response;
31
    protected $httpCode;
32
33
    /**
34
     * Camelizes a string
35
     * @param string $str string to camelize
36
     * @return string camelized string
37
     */
38
    public static function camelize($str)
39
    {
40
        return str_replace('_', '', ucwords($str, '_'));
41
    }
42
43
    /**
44
     * Magic setter function
45
     * @param string $name attribute to set
46
     * @param mixed $value the new value
47
     * @return void
48
     */
49 View Code Duplication
    public function __set($name, $value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
50
    {
51
        $c = static::camelize($name);
52
        $m = "set$c";
53
        if (method_exists($this, $m)) {
54
            $this->$m($value);
55
        } else {
56
            user_error("undefined property $name");
57
        }
58
    }
59
60
    /**
61
     * Magic getter function
62
     * @param string $name of the attribute to get
63
     * @return mixed the attribute's value
64
     */
65 View Code Duplication
    public function __get($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
66
    {
67
        $c = static::camelize($name);
68
        $m = "get$c";
69
        if (method_exists($this, $m)) {
70
            return $this->$m();
71
        } else {
72
            user_error("undefined property $name");
73
        }
74
    }
75
76
    /**
77
     * Request constructor.
78
     * @param string $url optional url
79
     */
80
    public function __construct($url = null)
81
    {
82
        $this->setUrl($url);
83
84
        // Defaults
85
        $this->options[CURLOPT_RETURNTRANSFER] = true;
86
        $this->options[CURLOPT_NOSIGNAL] = 1;
87
        $this->options[CURLOPT_FOLLOWLOCATION] = true;
88
    }
89
90
    /**
91
     * Destructor
92
     */
93
    public function __destruct()
94
    {
95
        if (isset($this->curlHandle)) {
96
            curl_close($this->curlHandle);
97
        }
98
    }
99
100
    /**
101
     * Clone the object
102
     * @return void
103
     */
104
    public function __clone()
105
    {
106
        if (isset($this->curlHandle)) {
107
            $this->curlHandle = curl_copy_handle($this->curlHandle);
108
        }
109
    }
110
111
    /**
112
     * Normalize an array
113
     * change from ['key' => 'value'] format to ['key: value']
114
     * @param array $array array to normalize
115
     * @return array normalized array
116
     */
117
    protected function normalize(array $array)
118
    {
119
        $normalized = [];
120
        foreach ($array as $key => $value) {
121
            if (is_string($key)) {
122
                $normalized[] = $key . ': ' . $value;
123
            } else {
124
                $normalized[] = $value;
125
            }
126
        }
127
        return $normalized;
128
    }
129
130
    /**
131
     * Setter for the url field
132
     * @param string $url url
133
     * @return void
134
     */
135
    public function setUrl($url)
136
    {
137
        if (!filter_var($url, FILTER_VALIDATE_URL) === false) {
138
            $this->url = $url;
139
        }
140
    }
141
142
    /**
143
     * Getter for url field
144
     * @return string url
145
     */
146
    public function getUrl()
147
    {
148
        return $this->url;
149
    }
150
151
    /**
152
     * Setter for the post data array
153
     * @param array $postData post data
154
     * @return void
155
     */
156
    public function setPostData(array $postData)
157
    {
158
        $this->post = $postData + $this->post;
159
        $this->options[CURLOPT_POST] = 1;
160
        if (!empty($this->post)) {
161
            $this->options[CURLOPT_POSTFIELDS] = http_build_query($this->post);
162
        }
163
    }
164
165
    /**
166
     * Getter for the post data array
167
     * @return array post data
168
     */
169
    public function getPostData()
170
    {
171
        return $this->post;
172
    }
173
174
    /**
175
     * Returns the time (msec) it took to make the request
176
     * @return float time
177
     */
178
    public function getTime()
179
    {
180
        if (isset($this->curlHandle)) {
181
            return curl_getinfo($this->curlHandle)['total_time'];
182
        }
183
        return (float)0;
184
    }
185
186
    /**
187
     * Get the result of a query
188
     * @return mixed result
189
     */
190
    public function getResult()
191
    {
192
        return $this->result;
193
    }
194
195
    /**
196
     * This gets called by an agent when a request has completed
197
     * @param array $multiInfo result
198
     * @return void
199
     */
200
    public function callBack(array $multiInfo)
201
    {
202
        $this->notify();
203
    }
204
205
    /**
206
     * Add a listener that gets notified when the Request has completed
207
     * @param callable $function callback function
208
     * @return void
209
     */
210
    public function addListener(callable $function)
211
    {
212
        if (is_callable($function)) {
213
            if (!in_array($function, $this->listeners)) {
214
                $this->listeners[] = $function;
215
            }
216
        }
217
    }
218
219
    /**
220
     * Notify all listeners of request completion
221
     * @return void
222
     */
223
    protected function notify()
224
    {
225
        foreach ($this->listeners as $listener) {
226
            call_user_func($listener, $this);
227
        }
228
    }
229
230
    /**
231
     * Set a timeout value for the request
232
     * @param float $timeout timeout (msec)
233
     * @return void
234
     */
235
    public function setTimeout($timeout)
236
    {
237
        if ($timeout > 0) {
238
            $this->timeout = $timeout;
239
            $this->options[CURLOPT_TIMEOUT_MS] = $this->timeout;
240
        }
241
    }
242
243
    /**
244
     * Get the timeout value registered for the request
245
     * @return float timeout
246
     */
247
    public function getTimeout()
248
    {
249
        return $this->timeout;
250
    }
251
252
    /**
253
     * Get the cUrl handle for the request
254
     * @return resource cUrl handle
255
     */
256
    public function getHandle()
257
    {
258
        if (!isset($this->curlHandle)) {
259
            $this->curlHandle = curl_init($this->url);
260
            curl_setopt_array($this->curlHandle, $this->options);
261
        }
262
        return $this->curlHandle;
263
    }
264
265
    /**
266
     * Add headers to the request
267
     * @param array $headers headers in ['key' => 'value] or ['key: value'] format
268
     * @return void
269
     */
270
    public function setHeaders(array $headers)
271
    {
272
        $this->headers = $headers + $this->headers;
273
        $this->options[CURLOPT_HTTPHEADER] = $this->normalize($this->headers);
274
    }
275
276
    /**
277
     * Get headers set for the request
278
     * @return array headers in ['key' => 'value'] format
279
     */
280
    public function getHeaders()
281
    {
282
        return $this->headers;
283
    }
284
285
    /**
286
     * Add cUrl options to the request
287
     * @param array $options options in ['key' => 'value'] format
288
     * @return void
289
     */
290
    public function setOptions(array $options)
291
    {
292
        $this->options = $options + $this->options;
293
    }
294
295
    /**
296
     * Get cUrl options set for the request
297
     * @return array options in ['key' => 'value'] format
298
     */
299
    public function getOptions()
300
    {
301
        return $this->options;
302
    }
303
304
    /**
305
     * Get the response for the finished query
306
     * @return mixed response
307
     */
308
    public function getResponse()
309
    {
310
        if (!isset($this->response)) {
311
            if (isset($this->curlHandle)) {
312
                $this->response = curl_multi_getcontent($this->curlHandle);
313
            }
314
        }
315
        return $this->response;
316
    }
317
318
    /**
319
     * Get the Http code of the response
320
     * @return mixed
321
     */
322
    public function getHttpCode()
323
    {
324
        if (!isset($this->httpCode)) {
325
            if (isset($this->curlHandle)) {
326
                $this->httpCode = curl_getinfo($this->curlHandle)['http_code'];
327
            }
328
        }
329
        return $this->httpCode;
330
    }
331
}
332