Completed
Push — master ( 990e98...abd222 )
by Benjamin
02:12
created

Request   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 299
Duplicated Lines 6.69 %

Coupling/Cohesion

Components 2
Dependencies 0

Importance

Changes 16
Bugs 1 Features 1
Metric Value
wmc 42
c 16
b 1
f 1
lcom 2
cbo 0
dl 20
loc 299
rs 8.295

24 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 10 3
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

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