Passed
Push — master ( d0de83...2b31d6 )
by Fran
04:36
created

Service::setDefaults()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 33
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 8
eloc 28
c 3
b 0
f 0
nc 10
nop 0
dl 0
loc 33
ccs 0
cts 23
cp 0
crap 72
rs 8.4444
1
<?php
2
namespace PSFS\base;
3
4
use PSFS\base\config\Config;
5
use PSFS\base\types\helpers\SecurityHelper;
6
use PSFS\base\types\helpers\ServiceHelper;
7
8
/**
9
 * Class Service
10
 * @package PSFS\base
11
 */
12
class Service extends Singleton
13
{
14
    const CTYPE_JSON = 'application/json';
15
    const CTYPE_MULTIPART = 'multipart/form-data';
16
    const CTYPE_FORM = 'application/x-www-form-urlencoded';
17
    const CTYPE_PLAIN = 'text/plain';
18
    const PSFS_TRACK_HEADER = 'X-PSFS-UID';
19
20
    /**
21
     * @var String Url de destino de la llamada
22
     */
23
    private $url;
24
    /**
25
     * @var array Parámetros de la llamada
26
     */
27
    private $params;
28
    /**
29
     * @var array Opciones llamada
30
     */
31
    private $options;
32
    /**
33
     * @var array Cabeceras de la llamada
34
     */
35
    private $headers;
36
    /**
37
     * @var string type
38
     */
39
    private $type;
40
    /**
41
     * @var resource $con
42
     */
43
    private $con;
44
    /**
45
     * @var string $result
46
     */
47
    private $result;
48
    /**
49
     * @var mixed
50
     */
51
    private $info = [];
52
53
    /**
54
     * @Injectable
55
     * @var \PSFS\base\Logger Log de las llamadas
56
     */
57
    protected $log;
58
    /**
59
     * @Injectable
60
     * @var \PSFS\base\Cache $cache
61
     */
62
    protected $cache;
63
    /**
64
     * @var bool
65
     */
66
    protected $isJson = true;
67
    /**
68
     * @var bool
69
     */
70
    protected $isMultipart = false;
71
72 2
    private function closeConnection() {
73 2
        if(null !== $this->con) {
74 1
            if(is_resource($this->con)) {
75 1
                @curl_close($this->con);
0 ignored issues
show
Bug introduced by
Are you sure the usage of curl_close($this->con) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition for curl_close(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

75
                /** @scrutinizer ignore-unhandled */ @curl_close($this->con);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
76
            } else {
77
                $this->con = null;
78
            }
79
        }
80 2
    }
81
82
    public function __destruct()
83
    {
84
        $this->closeConnection();
85
    }
86
87
    /**
88
     * @return String
89
     */
90 1
    public function getUrl()
91
    {
92 1
        return $this->url;
93
    }
94
95
    /**
96
     * @param String $url
97
     */
98 1
    public function setUrl($url)
99
    {
100 1
        $this->url = $url;
101 1
        $this->initialize();
102 1
    }
103
104
    /**
105
     * @return string
106
     */
107
    public function getResult()
108
    {
109
        return $this->result;
110
    }
111
112
    /**
113
     * @param string $result
114
     */
115
    public function setResult($result)
116
    {
117
        $this->result = $result;
118
    }
119
120
    /**
121
     * @return array
122
     */
123
    public function getParams()
124
    {
125
        return $this->params;
126
    }
127
128
    /**
129
     * Add request param
130
     *
131
     * @param $key
132
     * @param null $value
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $value is correct as it would always require null to be passed?
Loading history...
133
     *
134
     * @return \PSFS\base\Service
135
     */
136
    public function addParam($key, $value = NULL)
137
    {
138
        $this->params[$key] = $value;
139
140
        return $this;
141
    }
142
143
    /**
144
     * @return array
145
     */
146
    public function getOptions()
147
    {
148
        return $this->options;
149
    }
150
151
    /**
152
     * Add request param
153
     *
154
     * @param $key
155
     * @param null $value
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $value is correct as it would always require null to be passed?
Loading history...
156
     *
157
     * @return \PSFS\base\Service
158
     */
159
    public function addOption($key, $value = NULL)
160
    {
161
        $this->options[$key] = $value;
162
163
        return $this;
164
    }
165
166
    /**
167
     * @param array $params
168
     */
169
    public function setParams($params)
170
    {
171
        $this->params = $params;
172
    }
173
174
    /**
175
     * @return array
176
     */
177
    public function getHeaders()
178
    {
179
        return $this->headers;
180
    }
181
182
    /**
183
     * @param array $headers
184
     */
185
    public function setHeaders($headers)
186
    {
187
        $this->headers = $headers;
188
    }
189
190
    /**
191
     * @param $header
192
     * @param null $content
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $content is correct as it would always require null to be passed?
Loading history...
193
     *
194
     * @return $this
195
     */
196
    public function addHeader($header, $content = NULL)
197
    {
198
        $this->headers[$header] = $content;
199
200
        return $this;
201
    }
202
203
    /**
204
     * @return string
205
     */
206
    public function getType()
207
    {
208
        return $this->type;
209
    }
210
211
    /**
212
     * @param string $type
213
     */
214
    public function setType($type)
215
    {
216
        $this->type = $type;
217
    }
218
219
    /**
220
     * @return Logger
221
     */
222
    public function getLog()
223
    {
224
        return $this->log;
225
    }
226
227
    /**
228
     * @param Logger $log
229
     */
230 2
    public function setLog($log)
231
    {
232 2
        $this->log = $log;
233 2
    }
234
235
    /**
236
     * @param bool $isJson
237
     */
238
    public function setIsJson($isJson = true) {
239
        $this->isJson = $isJson;
240
        if($isJson) {
241
            $this->setIsMultipart(false);
242
        }
243
    }
244
245
    /**
246
     * @return bool
247
     */
248
    public function getIsJson() {
249
        return $this->isJson;
250
    }
251
252
    /**
253
     * @param bool $isMultipart
254
     */
255
    public function setIsMultipart($isMultipart = true) {
256
        $this->isMultipart = $isMultipart;
257
        if($isMultipart) {
258
            $this->setIsJson(false);
259
        }
260
    }
261
262
    /**
263
     * @return bool
264
     */
265
    public function getIsMultipart() {
266
        return $this->isMultipart;
267
    }
268
269
    /**
270
     * Método que limpia el contexto de la llamada
271
     */
272 2
    private function clearContext()
273
    {
274 2
        $this->url = NULL;
275 2
        $this->params = array();
276 2
        $this->headers = array();
277 2
        Logger::log('Context service for ' . static::class . ' cleared!');
278 2
        $this->closeConnection();
279 2
    }
280
281
    /**
282
     *
283
     */
284 2
    public function init()
285
    {
286 2
        parent::init();
287 2
        $this->clearContext();
288 2
    }
289
290
    /**
291
     * Initialize CURL
292
     */
293 1
    private function initialize()
294
    {
295 1
        $this->closeConnection();
296 1
        $this->params = [];
297 1
        $this->con = curl_init($this->url);
0 ignored issues
show
Documentation Bug introduced by
It seems like curl_init($this->url) can also be of type false. However, the property $con is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
298 1
    }
299
300
    /**
301
     * Generate auth header
302
     * @param string $secret
303
     * @param string $module
304
     */
305
    protected function addRequestToken($secret, $module = 'PSFS')
306
    {
307
        $this->addHeader('X-PSFS-SEC-TOKEN', SecurityHelper::generateToken($secret, $module));
308
    }
309
310
    /**
311
     * @param $user
312
     * @param $pass
313
     */
314
    protected function addAuthHeader($user, $pass) {
315
        $this->addOption(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
316
        $this->addOption(CURLOPT_USERPWD, "$user:$pass");
317
    }
318
319
    protected function applyOptions() {
320
        if(count($this->options)) {
321
            curl_setopt_array($this->con, $this->options);
322
        }
323
    }
324
325
    protected function applyHeaders() {
326
        $headers = [];
327
        foreach($this->headers as $key => $value) {
328
            $headers[] = $key . ': ' . $value;
329
        }
330
        $headers[self::PSFS_TRACK_HEADER] = Logger::getUid();
331
        if(count($headers)) {
332
            curl_setopt($this->con, CURLOPT_HTTPHEADER, $headers);
333
        }
334
    }
335
336
    /**
337
     * @return int
338
     */
339
    private function parseServiceType() {
340
        if($this->getIsJson()) {
341
            return ServiceHelper::TYPE_JSON;
342
        }
343
        if($this->getIsMultipart()) {
344
            return ServiceHelper::TYPE_MULTIPART;
345
        }
346
        return ServiceHelper::TYPE_HTTP;
347
    }
348
349
    protected function setDefaults()
350
    {
351
        $serviceType = $this->parseServiceType();
352
        switch (strtoupper($this->type)) {
353
            case Request::VERB_GET:
354
            default:
355
                $this->addOption(CURLOPT_CUSTOMREQUEST, Request::VERB_GET);
356
                if(!empty($this->params)) {
357
                    $sep = false === strpos($this->getUrl(), '?') ? '?' : '';
358
                    $this->url = $this->url . $sep . http_build_query($this->params);
359
                }
360
                break;
361
            case Request::VERB_POST:
362
                $this->addOption(CURLOPT_CUSTOMREQUEST, Request::VERB_POST);
363
                $this->addOption(CURLOPT_POSTFIELDS, ServiceHelper::parseRawData($serviceType, $this->getParams()));
364
                break;
365
            case Request::VERB_DELETE:
366
                $this->addOption(CURLOPT_CUSTOMREQUEST, Request::VERB_DELETE);
367
                break;
368
            case Request::VERB_PUT:
369
                $this->addOption(CURLOPT_CUSTOMREQUEST, Request::VERB_PUT);
370
                $this->addOption(CURLOPT_POSTFIELDS, ServiceHelper::parseRawData($serviceType, $this->getParams()));
371
                break;
372
            case Request::VERB_PATCH:
373
                $this->addOption(CURLOPT_CUSTOMREQUEST, Request::VERB_PATCH);
374
                $this->addOption(CURLOPT_POSTFIELDS, ServiceHelper::parseRawData($serviceType, $this->getParams()));
375
                break;
376
        }
377
378
        $this->addOption(CURLOPT_RETURNTRANSFER, true);
379
        $this->addOption(CURLOPT_FOLLOWLOCATION, true);
380
        $this->addOption(CURLOPT_SSL_VERIFYHOST, false);
381
        $this->addOption(CURLOPT_SSL_VERIFYPEER, false);
382
    }
383
384
    public function callSrv()
385
    {
386
        $this->setDefaults();
387
        $this->applyOptions();
388
        $this->applyHeaders();
389
        $verbose = null;
390
        if('debug' === Config::getParam('log.level')) {
391
            curl_setopt($this->con, CURLOPT_VERBOSE, true);
392
            $verbose = fopen('php://temp', 'wb+');
393
            curl_setopt($this->con, CURLOPT_STDERR, $verbose);
394
        }
395
        $result = curl_exec($this->con);
396
        $this->setResult($this->isJson ? json_decode($result, true) : $result);
397
        if('debug' === Config::getParam('log.level')) {
398
            rewind($verbose);
0 ignored issues
show
Bug introduced by
It seems like $verbose can also be of type false; however, parameter $handle of rewind() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

398
            rewind(/** @scrutinizer ignore-type */ $verbose);
Loading history...
399
            $verboseLog = stream_get_contents($verbose);
0 ignored issues
show
Bug introduced by
It seems like $verbose can also be of type false; however, parameter $handle of stream_get_contents() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

399
            $verboseLog = stream_get_contents(/** @scrutinizer ignore-type */ $verbose);
Loading history...
400
            Logger::log($verboseLog, LOG_DEBUG, [
401
                'headers' => $this->getHeaders(),
402
                'options' => $this->getOptions(),
403
                'url' => $this->getUrl(),
404
            ]);
405
            $this->info['verbose'] = $verboseLog;
406
        }
407
        Logger::log($this->url . ' response: ', LOG_DEBUG, is_array($this->result) ? $this->result : [$this->result]);
408
        $this->info = array_merge($this->info, curl_getinfo($this->con));
409
    }
410
411
    /**
412
     * @return mixed
413
     */
414
    public function getCallInfo() {
415
        return $this->info;
416
    }
417
418
}
419