Passed
Push — master ( 5dc874...308456 )
by Fran
03:58
created

Service   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 365
Duplicated Lines 6.85 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 12.06%

Importance

Changes 0
Metric Value
dl 25
loc 365
ccs 17
cts 141
cp 0.1206
rs 8.3673
c 0
b 0
f 0
wmc 45
lcom 1
cbo 4

30 Methods

Rating   Name   Duplication   Size   Complexity  
A closeConnection() 0 5 2
A __destruct() 0 4 1
A getUrl() 0 4 1
A setUrl() 0 5 1
A getResult() 0 4 1
A setResult() 0 4 1
A getParams() 0 4 1
A addParam() 0 6 1
A getOptions() 0 4 1
A addOption() 0 6 1
A setParams() 0 4 1
A getHeaders() 0 4 1
A setHeaders() 0 4 1
A addHeader() 0 6 1
A getType() 0 4 1
A setType() 0 4 1
A getLog() 0 4 1
A setLog() 0 4 1
A setIsJson() 0 3 1
A getIsJson() 0 3 1
A clearContext() 0 8 1
A init() 0 5 1
A initialize() 0 5 1
A addRequestToken() 0 4 1
A addAuthHeader() 0 4 1
A applyOptions() 0 5 2
A applyHeaders() 0 9 3
D setDefaults() 25 43 9
B callSrv() 0 24 4
A getCallInfo() 0 3 1

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 Service 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 Service, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace PSFS\base;
3
4
use PSFS\base\config\Config;
5
use PSFS\base\types\helpers\SecurityHelper;
6
7
/**
8
 * Class Service
9
 * @package PSFS\base
10
 */
11
class Service extends Singleton
12
{
13
    /**
14
     * @var String Url de destino de la llamada
15
     */
16
    private $url;
17
    /**
18
     * @var array Parámetros de la llamada
19
     */
20
    private $params;
21
    /**
22
     * @var array Opciones llamada
23
     */
24
    private $options;
25
    /**
26
     * @var array Cabeceras de la llamada
27
     */
28
    private $headers;
29
    /**
30
     * @var string type
31
     */
32
    private $type;
33
    /**
34
     * @var resource $con
35
     */
36
    private $con;
37
    /**
38
     * @var string $result
39
     */
40
    private $result;
41
    /**
42
     * @var mixed
43
     */
44
    private $info;
45
46
    /**
47
     * @Injectable
48
     * @var \PSFS\base\Logger Log de las llamadas
49
     */
50
    protected $log;
51
    /**
52
     * @Injectable
53
     * @var \PSFS\base\Cache $cache
54
     */
55
    protected $cache;
56
    /**
57
     * @var bool
58
     */
59
    protected $isJson = true;
60
61 1
    private function closeConnection() {
62 1
        if(null !== $this->con) {
63
            curl_close($this->con);
64
        }
65 1
    }
66
67
    public function __destruct()
68
    {
69
        $this->closeConnection();
70
    }
71
72
    /**
73
     * @return String
74
     */
75
    public function getUrl()
76
    {
77
        return $this->url;
78
    }
79
80
    /**
81
     * @param String $url
82
     */
83
    public function setUrl($url)
84
    {
85
        $this->url = $url;
86
        $this->initialize();
87
    }
88
89
    /**
90
     * @return string
91
     */
92
    public function getResult()
93
    {
94
        return $this->result;
95
    }
96
97
    /**
98
     * @param string $result
99
     */
100
    public function setResult($result)
101
    {
102
        $this->result = $result;
103
    }
104
105
    /**
106
     * @return array
107
     */
108
    public function getParams()
109
    {
110
        return $this->params;
111
    }
112
113
    /**
114
     * Add request param
115
     *
116
     * @param $key
117
     * @param null $value
118
     *
119
     * @return \PSFS\base\Service
120
     */
121
    public function addParam($key, $value = NULL)
122
    {
123
        $this->params[$key] = $value;
124
125
        return $this;
126
    }
127
128
    /**
129
     * @return array
130
     */
131
    public function getOptions()
132
    {
133
        return $this->options;
134
    }
135
136
    /**
137
     * Add request param
138
     *
139
     * @param $key
140
     * @param null $value
141
     *
142
     * @return \PSFS\base\Service
143
     */
144
    public function addOption($key, $value = NULL)
145
    {
146
        $this->options[$key] = $value;
147
148
        return $this;
149
    }
150
151
    /**
152
     * @param array $params
153
     */
154
    public function setParams($params)
155
    {
156
        $this->params = $params;
157
    }
158
159
    /**
160
     * @return array
161
     */
162
    public function getHeaders()
163
    {
164
        return $this->headers;
165
    }
166
167
    /**
168
     * @param array $headers
169
     */
170
    public function setHeaders($headers)
171
    {
172
        $this->headers = $headers;
173
    }
174
175
    /**
176
     * @param $header
177
     * @param null $content
178
     *
179
     * @return $this
180
     */
181
    public function addHeader($header, $content = NULL)
182
    {
183
        $this->headers[$header] = $content;
184
185
        return $this;
186
    }
187
188
    /**
189
     * @return string
190
     */
191
    public function getType()
192
    {
193
        return $this->type;
194
    }
195
196
    /**
197
     * @param string $type
198
     */
199
    public function setType($type)
200
    {
201
        $this->type = $type;
202
    }
203
204
    /**
205
     * @return Logger
206
     */
207
    public function getLog()
208
    {
209
        return $this->log;
210
    }
211
212
    /**
213
     * @param Logger $log
214
     */
215 1
    public function setLog($log)
216
    {
217 1
        $this->log = $log;
218 1
    }
219
220
    /**
221
     * @param bool $isJson
222
     */
223
    public function setIsJson($isJson = true) {
224
        $this->isJson = $isJson;
225
    }
226
227
    /**
228
     * @return bool
229
     */
230
    public function getIsJson() {
231
        return $this->isJson;
232
    }
233
234
    /**
235
     * Método que limpia el contexto de la llamada
236
     */
237 1
    private function clearContext()
238
    {
239 1
        $this->url = NULL;
240 1
        $this->params = array();
241 1
        $this->headers = array();
242 1
        Logger::log("Context service for " . get_called_class() . " cleared!");
243 1
        $this->closeConnection();
244 1
    }
245
246
    /**
247
     *
248
     */
249 1
    public function init()
250
    {
251 1
        parent::init();
252 1
        $this->clearContext();
253 1
    }
254
255
    /**
256
     * Initialize CURL
257
     */
258
    private function initialize()
259
    {
260
        $this->closeConnection();
261
        $this->con = curl_init($this->url);
262
    }
263
264
    /**
265
     * Generate auth header
266
     * @param string $secret
267
     * @param string $module
268
     */
269
    protected function addRequestToken($secret, $module = 'PSFS')
270
    {
271
        $this->addHeader('X-PSFS-SEC-TOKEN', SecurityHelper::generateToken($secret, $module));
272
    }
273
274
    /**
275
     * @param $user
276
     * @param $pass
277
     */
278
    protected function addAuthHeader($user, $pass) {
279
        $this->addOption(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
280
        $this->addOption(CURLOPT_USERPWD, "$user:$pass");
281
    }
282
283
    protected function applyOptions() {
284
        if(count($this->options)) {
285
            curl_setopt_array($this->con, $this->options);
286
        }
287
    }
288
289
    protected function applyHeaders() {
290
        $headers = [];
291
        foreach($this->headers as $key => $value) {
292
            $headers[] = $key . ': ' . $value;
293
        }
294
        if(count($headers)) {
295
            curl_setopt($this->con, CURLOPT_HTTPHEADER, $headers);
296
        }
297
    }
298
299
    protected function setDefaults()
300
    {
301
        switch (strtoupper($this->type)) {
302
            case 'GET':
303
            default:
304
                $this->addOption(CURLOPT_CUSTOMREQUEST, "GET");
305
                $this->setUrl($this->getUrl() . '?' . http_build_query($this->params));
306
                break;
307 View Code Duplication
            case 'POST':
308
                $this->addOption(CURLOPT_CUSTOMREQUEST, "POST");
309
                if($this->getIsJson()) {
310
                    $this->addOption(CURLOPT_POSTFIELDS, json_encode($this->params));
311
                } else {
312
                    $this->addOption(CURLOPT_POSTFIELDS, http_build_query($this->params));
313
                }
314
                break;
315
            case 'DELETE':
316
                $this->addOption(CURLOPT_CUSTOMREQUEST, "DELETE");
317
                break;
318 View Code Duplication
            case 'PUT':
319
                $this->addOption(CURLOPT_CUSTOMREQUEST, "PUT");
320
321
                if($this->getIsJson()) {
322
                    $this->addOption(CURLOPT_POSTFIELDS, json_encode($this->params));
323
                } else {
324
                    $this->addOption(CURLOPT_POSTFIELDS, http_build_query($this->params));
325
                }
326
                break;
327 View Code Duplication
            case 'PATCH':
328
                $this->addOption(CURLOPT_CUSTOMREQUEST, "PATCH");
329
                if($this->getIsJson()) {
330
                    $this->addOption(CURLOPT_POSTFIELDS, json_encode($this->params));
331
                } else {
332
                    $this->addOption(CURLOPT_POSTFIELDS, http_build_query($this->params));
333
                }
334
                break;
335
        }
336
337
        $this->addOption(CURLOPT_RETURNTRANSFER, true);
338
        $this->addOption(CURLOPT_FOLLOWLOCATION, true);
339
        $this->addOption(CURLOPT_SSL_VERIFYHOST, false);
340
        $this->addOption(CURLOPT_SSL_VERIFYPEER, false);
341
    }
342
343
    public function callSrv()
344
    {
345
        $this->setDefaults();
346
        $this->applyOptions();
347
        $this->applyHeaders();
348
        if('debug' === Config::getParam('log.level')) {
349
            curl_setopt($this->con, CURLOPT_VERBOSE, true);
350
            $verbose = fopen('php://temp', 'w+');
351
            curl_setopt($this->con, CURLOPT_STDERR, $verbose);
352
        }
353
        $result = curl_exec($this->con);
354
        $this->result = $this->isJson ? json_decode($result, true) : $result;
355
        if('debug' === Config::getParam('log.level')) {
356
            rewind($verbose);
0 ignored issues
show
Bug introduced by
The variable $verbose does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
357
            $verboseLog = stream_get_contents($verbose);
358
            Logger::log($verboseLog, LOG_DEBUG, [
359
                'headers' => $this->getHeaders(),
360
                'options' => $this->getOptions(),
361
                'url' => $this->getUrl(),
362
            ]);
363
        }
364
        Logger::log($this->url . ' response: ', LOG_DEBUG, $this->result);
365
        $this->info = curl_getinfo($this->con);
366
    }
367
368
    /**
369
     * @return mixed
370
     */
371
    public function getCallInfo() {
372
        return $this->info;
373
    }
374
375
}
376