Completed
Push — master ( 1ddf7f...5b2c01 )
by Andrii
04:14
created

Connection::curlRequest()   F

Complexity

Conditions 23
Paths 1968

Size

Total Lines 126
Code Lines 93

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 552

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 126
ccs 0
cts 118
cp 0
rs 2
cc 23
eloc 93
nc 1968
nop 4
crap 552

1 Method

Rating   Name   Duplication   Size   Complexity  
A Connection::checkResponse() 0 13 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * Tools to use API as ActiveRecord for Yii2
5
 *
6
 * @link      https://github.com/hiqdev/yii2-hiart
7
 * @package   yii2-hiart
8
 * @license   BSD-3-Clause
9
 * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/)
10
 */
11
12
namespace hiqdev\hiart;
13
14
use Closure;
15
use Yii;
16
use yii\base\Component;
17
use yii\base\InvalidConfigException;
18
use yii\base\InvalidParamException;
19
use yii\helpers\Json;
20
21
/**
22
 * Connection class.
23
 *
24
 * Example configuration:
25
 * ```php
26
 * 'components' => [
27
 *     'hiart' => [
28
 *         'class' => 'hiqdev\hiart\Connection',
29
 *         'config' => [
30
 *             'base_uri' => 'https://api.site.com/',
31
 *         ],
32
 *     ],
33
 * ],
34
 * ```
35
 */
36
class Connection extends Component
37
{
38
    const EVENT_AFTER_OPEN = 'afterOpen';
39
40
    /**
41
     * @var array Config
42
     */
43
    public $config = [];
44
45
    public $connectionTimeout = null;
46
47
    public $dataTimeout = null;
48
49
    public static $curl = null;
50
51
    /**
52
     * @var \GuzzleHttp\Client
53
     */
54
    protected static $guzzle = null;
55
56
    /**
57
     * Authorization config.
58
     *
59
     * @var array
60
     */
61
    protected $_auth;
62
63
    public function setAuth($auth)
64
    {
65
        $this->_auth = $auth;
66
    }
67
68
    public function getAuth()
69
    {
70
        if ($this->_auth instanceof Closure) {
71
            $this->_auth = call_user_func($this->_auth, $this);
0 ignored issues
show
Documentation Bug introduced by
It seems like call_user_func($this->_auth, $this) of type * is incompatible with the declared type array of property $_auth.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
72
        }
73
74
        return $this->_auth;
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     * @throws InvalidConfigException
80
     */
81
    public function init()
82
    {
83
        if (!$this->errorChecker instanceof \Closure) {
84
            throw new InvalidConfigException('The errorChecker must be set');
85
        }
86
87
        if (!isset($this->config['api_url'])) {
88
            throw new InvalidConfigException('HiArt needs api_url configuration');
89
        }
90
    }
91
92
    public function getHandler()
93
    {
94
        if (!self::$curl) {
95
            self::$curl = static::$curl = curl_init();
96
        }
97
98
        return self::$curl;
99
    }
100
101
    /**
102
     * Closes the connection when this component is being serialized.
103
     * @return array
104
     */
105
    public function __sleep()
106
    {
107
        return array_keys(get_object_vars($this));
108
    }
109
110
    /**
111
     * Returns the name of the DB driver for the current [[dsn]].
112
     *
113
     * @return string name of the DB driver
114
     */
115
    public function getDriverName()
116
    {
117
        return 'hiresource';
118
    }
119
120
    /**
121
     * Creates a command for execution.
122
     *
123
     * @param array $config the configuration for the Command class
124
     *
125
     * @return Command the DB command
126
     */
127
    public function createCommand($config = [])
128
    {
129
        $config['db'] = $this;
130
        $command      = new Command($config);
131
132
        return $command;
133
    }
134
135
    /**
136
     * Creates new query builder instance.
137
     *
138
     * @return QueryBuilder
139
     */
140
    public function getQueryBuilder()
141
    {
142
        return new QueryBuilder($this);
143
    }
144
145
    /**
146
     * Performs GET HTTP request.
147
     * @param string $url   URL
148
     * @param array  $query query options
149
     * @param string $body  request body
150
     * @param bool   $raw   if response body contains JSON and should be decoded
151
     * @throws HiArtException
152
     * @throws \yii\base\InvalidConfigException
153
     * @return mixed response
154
     */
155
    public function get($url, $query = [], $body = null, $raw = false)
156
    {
157
        return $this->makeRequest('GET', $url, $query, $body, $raw);
158
    }
159
160
    /**
161
     * Performs HEAD HTTP request.
162
     * @param string $url   URL
163
     * @param array  $query query options
164
     * @param string $body  request body
165
     * @throws HiArtException
166
     * @throws \yii\base\InvalidConfigException
167
     * @return mixed response
168
     */
169
    public function head($url, $query = [], $body = null)
170
    {
171
        return $this->makeRequest('HEAD', $url, $query, $body, $raw);
0 ignored issues
show
Bug introduced by
The variable $raw does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
172
    }
173
174
    /**
175
     * Performs POST HTTP request.
176
     * @param string $url   URL
177
     * @param array  $query query options
178
     * @param string $body  request body
179
     * @param bool   $raw   if response body contains JSON and should be decoded
180
     * @throws HiArtException
181
     * @throws \yii\base\InvalidConfigException
182
     * @return mixed response
183
     */
184
    public function post($url, $query = [], $body = null, $raw = false)
185
    {
186
        return $this->makeRequest('POST', $url, $query, $body, $raw);
187
    }
188
189
    /**
190
     * Performs PUT HTTP request.
191
     * @param string $url   URL
192
     * @param array  $query query options
193
     * @param string $body  request body
194
     * @param bool   $raw   if response body contains JSON and should be decoded
195
     * @throws HiArtException
196
     * @throws \yii\base\InvalidConfigException
197
     * @return mixed response
198
     */
199
    public function put($url, $query = [], $body = null, $raw = false)
200
    {
201
        return $this->makeRequest('PUT', $url, $query, $body, $raw);
202
    }
203
204
    /**
205
     * Performs DELETE HTTP request.
206
     * @param string $url   URL
207
     * @param array  $query query options
208
     * @param string $body  request body
209
     * @param bool   $raw   if response body contains JSON and should be decoded
210
     * @throws HiArtException
211
     * @throws \yii\base\InvalidConfigException
212
     * @return mixed response
213
     */
214
    public function delete($url, $query = [], $body = null, $raw = false)
215
    {
216
        return $this->makeRequest('DELETE', $url, $query, $body, $raw);
217
    }
218
219
    /**
220
     * XXX DEPRECATED in favour of post().
221
     * @param $url
222
     * @param array $query
0 ignored issues
show
Bug introduced by
There is no parameter named $query. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
223
     * @return mixed
224
     */
225
    public function perform($url, $body = [])
226
    {
227
        return $this->makeRequest('DELETE', $url, [], $body);
0 ignored issues
show
Documentation introduced by
$body is of type array, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
228
    }
229
230
    /**
231
     * Make request and check for error.
232
     * @param string $url   URL
233
     * @param array  $query query options, (GET parameters)
234
     * @param string $body  request body, (POST parameters)
235
     * @param bool   $raw   if response body contains JSON and should be decoded
236
     * @throws HiArtException
237
     * @throws \yii\base\InvalidConfigException
238
     * @return mixed response
239
     */
240
    public function makeRequest($method, $url, $query = [], $body = null, $raw = false)
241
    {
242
        $result = $this->makeGuzzleRequest($method, $this->prepareUrl($url, $query), $body, $raw);
0 ignored issues
show
Documentation introduced by
$this->prepareUrl($url, $query) is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
243
244
        return $this->checkResponse($result, $url, $query);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->makeGuzzleRequest..., $query), $body, $raw) on line 242 can also be of type string; however, hiqdev\hiart\Connection::checkResponse() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
245
    }
246
247
    /**
248
     * Creates URL.
249
     * @param mixed $path path
250
     * @param array $query query options
251
     * @return array
252
     */
253
    private function prepareUrl($path, array $query = [])
254
    {
255
        $url = $path;
256
        $query = array_merge($this->getAuth(), $query);
257
        if (!empty($query)) {
258
            $url .= (strpos($url, '?') === false ? '?' : '&') . http_build_query($query);
259
        }
260
261
        return $url;
262
    }
263
264
    /**
265
     * Sends the request using guzzle, returns array or raw response content, if $raw is true.
266
     *
267
     * @param string $method POST, GET, etc
268
     * @param string $url the URL for request, not including proto and site
269
     * @param array|string $body the request body. When array - will be sent as POST params, otherwise - as RAW body.
270
     * @param bool $raw Whether to decode data, when response is JSON.
271
     * @return string|array
272
     */
273
    protected function makeGuzzleRequest($method, $url, $body = null, $raw = false)
274
    {
275
        $method  = strtoupper($method);
276
        $profile = $method . ' ' . $url . '#' . (is_array($body) ? http_build_query($body) : $body);
277
        $options = [(is_array($body) ? 'form_params' : 'body') => $body];
278
        Yii::beginProfile($profile, __METHOD__);
279
        $response = $this->getGuzzle()->request($method, $url, $options);
280
        Yii::endProfile($profile, __METHOD__);
281
282
        $res = $response->getBody()->getContents();
283
        if (!$raw && preg_grep('|application/json|i', $response->getHeader('Content-Type'))) {
284
            $res = Json::decode($res);
285
        }
286
287
        return $res;
288
    }
289
290
    /**
291
     * Returns the GuzzleHttp client.
292
     *
293
     * @return \GuzzleHttp\Client
294
     */
295
    public function getGuzzle()
296
    {
297
        if (static::$guzzle === null) {
298
            static::$guzzle = new \GuzzleHttp\Client($this->config);
299
            static::$guzzle->setUserAgent('hiart/0.x');
300
        }
301
302
        return static::$guzzle;
303
    }
304
305
306
    /**
307
     * Try to decode error information if it is valid json, return it if not.
308
     * @param $body
309
     * @return mixed
310
     */
311
    protected function decodeErrorBody($body)
312
    {
313
        try {
314
            $decoded = Json::decode($body);
315
            if (isset($decoded['error'])) {
316
                $decoded['error'] = preg_replace('/\b\w+?Exception\[/',
317
                    "<span style=\"color: red;\">\\0</span>\n               ", $decoded['error']);
318
            }
319
320
            return $decoded;
321
        } catch (InvalidParamException $e) {
322
            return $body;
323
        }
324
    }
325
326
    /**
327
     * @var \Closure Callback to test if API response has error
328
     * The function signature: `function ($response)`
329
     * Must return `null`, if the response does not contain an error.
330
     */
331
    public $errorChecker;
332
333
    /**
334
     * Checks response with errorChecker callback and raises exception if error.
335
     * @param array  $response response data from API
336
     * @param string $url      request URL
337
     * @param array  $options  request data
338
     * @throws ErrorResponseException
339
     * @return array
340
     */
341
    protected function checkResponse($response, $url, $options)
342
    {
343
        $error = call_user_func($this->errorChecker, $response);
344
        if ($error !== null) {
345
            throw new ErrorResponseException($error, [
346
                'requestUrl' => $url,
347
                'request'    => $options,
348
                'response'   => $response,
349
            ]);
350
        }
351
352
        return $response;
353
    }
354
}
355