Completed
Push — master ( 780aa3...25859b )
by Andrii
02:28
created

Connection::disableAuth()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
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 GuzzleHttp\Client as Handler;
16
use Yii;
17
use yii\base\Component;
18
use yii\base\InvalidConfigException;
19
use yii\base\InvalidParamException;
20
use yii\helpers\Json;
21
22
/**
23
 * Connection class.
24
 *
25
 * Example configuration:
26
 * ```php
27
 * 'components' => [
28
 *     'hiart' => [
29
 *         'class' => 'hiqdev\hiart\Connection',
30
 *         'config' => [
31
 *             'base_uri' => 'https://api.site.com/',
32
 *         ],
33
 *     ],
34
 * ],
35
 * ```
36
 */
37
class Connection extends Component
38
{
39
    const EVENT_AFTER_OPEN = 'afterOpen';
40
41
    /**
42
     * @var array Config
43
     */
44
    public $config = [];
45
46
    /**
47
     * @var Handler
48
     */
49
    protected static $_handler = null;
50
51
    /**
52
     * @var array authorization config
53
     */
54
    protected $_auth = [];
55
56
    /**
57
     * @var bool is auth disabled
58
     */
59
    protected $_disabledAuth = false;
60
61
    /**
62
     * @var Closure Callback to test if API response has error
63
     * The function signature: `function ($response)`
64
     * Must return `null`, if the response does not contain an error.
65
     */
66
    protected $_errorChecker;
67
68
    public function setAuth($auth)
69
    {
70
        $this->_auth = $auth;
71
    }
72
73
    /**
74
     * Returns auth settings.
75
     * @return array
76
     */
77 2
    public function getAuth()
78
    {
79 2
        if ($this->_disabledAuth) {
80
            return [];
81
        }
82 2
        if ($this->_auth instanceof Closure) {
83
            $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...
84
        }
85
86 2
        return $this->_auth;
87
    }
88
89
    public function disableAuth()
90
    {
91
        $this->_disabledAuth = true;
92
    }
93
94
    public function enableAuth()
95
    {
96
        $this->_disabledAuth = false;
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     * @throws InvalidConfigException
102
     */
103 2
    public function init()
104
    {
105 2
        if (!$this->config['base_uri']) {
106
            throw new InvalidConfigException('The `base_uri` config option must be set');
107
        }
108 2
    }
109
110
    /**
111
     * Closes the connection when this component is being serialized.
112
     * @return array
113
     */
114
    public function __sleep()
115
    {
116
        return array_keys(get_object_vars($this));
117
    }
118
119
    /**
120
     * Returns the name of the DB driver for the current [[dsn]].
121
     *
122
     * @return string name of the DB driver
123
     */
124
    public function getDriverName()
125
    {
126
        return 'hiresource';
127
    }
128
129
    /**
130
     * Creates a command for execution.
131
     *
132
     * @param array $config the configuration for the Command class
133
     *
134
     * @return Command the DB command
135
     */
136
    public function createCommand($config = [])
137
    {
138
        $config['db'] = $this;
139
        $command      = new Command($config);
140
141
        return $command;
142
    }
143
144
    /**
145
     * Creates new query builder instance.
146
     *
147
     * @return QueryBuilder
148
     */
149
    public function getQueryBuilder()
150
    {
151
        return new QueryBuilder($this);
152
    }
153
154
    /**
155
     * Performs GET HTTP request.
156
     * @param string $url   URL
157
     * @param array  $query query options
158
     * @param string $body  request body
159
     * @param bool   $raw   if response body contains JSON and should be decoded
160
     * @throws HiArtException
161
     * @throws \yii\base\InvalidConfigException
162
     * @return mixed response
163
     */
164 2
    public function get($url, $query = [], $body = null, $raw = false)
165
    {
166 2
        return $this->makeRequest('GET', $url, $query, $body, $raw);
167
    }
168
169
    /**
170
     * Performs HEAD HTTP request.
171
     * @param string $url   URL
172
     * @param array  $query query options
173
     * @param string $body  request body
174
     * @throws HiArtException
175
     * @throws \yii\base\InvalidConfigException
176
     * @return mixed response
177
     */
178
    public function head($url, $query = [], $body = null)
179
    {
180
        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...
181
    }
182
183
    /**
184
     * Performs POST HTTP request.
185
     * @param string $url   URL
186
     * @param array  $query query options
187
     * @param string $body  request body
188
     * @param bool   $raw   if response body contains JSON and should be decoded
189
     * @throws HiArtException
190
     * @throws \yii\base\InvalidConfigException
191
     * @return mixed response
192
     */
193
    public function post($url, $query = [], $body = null, $raw = false)
194
    {
195
        return $this->makeRequest('POST', $url, $query, $body, $raw);
196
    }
197
198
    /**
199
     * Performs PUT HTTP request.
200
     * @param string $url   URL
201
     * @param array  $query query options
202
     * @param string $body  request body
203
     * @param bool   $raw   if response body contains JSON and should be decoded
204
     * @throws HiArtException
205
     * @throws \yii\base\InvalidConfigException
206
     * @return mixed response
207
     */
208
    public function put($url, $query = [], $body = null, $raw = false)
209
    {
210
        return $this->makeRequest('PUT', $url, $query, $body, $raw);
211
    }
212
213
    /**
214
     * Performs DELETE HTTP request.
215
     * @param string $url   URL
216
     * @param array  $query query options
217
     * @param string $body  request body
218
     * @param bool   $raw   if response body contains JSON and should be decoded
219
     * @throws HiArtException
220
     * @throws \yii\base\InvalidConfigException
221
     * @return mixed response
222
     */
223
    public function delete($url, $query = [], $body = null, $raw = false)
224
    {
225
        return $this->makeRequest('DELETE', $url, $query, $body, $raw);
226
    }
227
228
    /**
229
     * XXX DEPRECATED in favour of post().
230
     * @param $url
231
     * @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...
232
     * @return mixed
233
     */
234
    public function perform($url, $body = [])
235
    {
236
        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...
237
    }
238
239
    /**
240
     * Make request and check for error.
241
     * @param string $url   URL
242
     * @param array  $query query options, (GET parameters)
243
     * @param string $body  request body, (POST parameters)
244
     * @param bool   $raw   if response body contains JSON and should be decoded
245
     * @throws HiArtException
246
     * @throws \yii\base\InvalidConfigException
247
     * @return mixed response
248
     */
249 2
    public function makeRequest($method, $url, $query = [], $body = null, $raw = false)
250
    {
251 2
        $result = $this->handleRequest($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...
252
253 2
        return $this->checkResponse($result, $url, $query);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->handleRequest($me..., $query), $body, $raw) on line 251 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...
254
    }
255
256
    /**
257
     * Creates URL.
258
     * @param mixed $path path
259
     * @param array $query query options
260
     * @return array
261
     */
262 2
    private function prepareUrl($path, array $query = [])
263
    {
264 2
        $url = $path;
265 2
        $query = array_merge($this->getAuth(), $query);
266 2
        if (!empty($query)) {
267
            $url .= (strpos($url, '?') === false ? '?' : '&') . http_build_query($query);
268
        }
269
270 2
        return $url;
271
    }
272
273
    /**
274
     * Handles the request with handler.
275
     * Returns array or raw response content, if $raw is true.
276
     *
277
     * @param string $method POST, GET, etc
278
     * @param string $url the URL for request, not including proto and site
279
     * @param array|string $body the request body. When array - will be sent as POST params, otherwise - as RAW body.
280
     * @param bool $raw Whether to decode data, when response is decodeable (JSON).
281
     * @return array|string
282
     */
283 2
    protected function handleRequest($method, $url, $body = null, $raw = false)
284
    {
285 2
        $method  = strtoupper($method);
286 2
        $profile = $method . ' ' . $url . '#' . (is_array($body) ? http_build_query($body) : $body);
287 2
        $options = [(is_array($body) ? 'form_params' : 'body') => $body];
288 2
        Yii::beginProfile($profile, __METHOD__);
289 2
        $response = $this->getHandler()->request($method, $url, $options);
290 2
        Yii::endProfile($profile, __METHOD__);
291
292 2
        $res = $response->getBody()->getContents();
293 2
        if (!$raw && preg_grep('|application/json|i', $response->getHeader('Content-Type'))) {
294
            $res = Json::decode($res);
295
        }
296
297 2
        return $res;
298
    }
299
300
    /**
301
     * Returns the request handler (Guzzle client for the moment).
302
     * Creates and setups handler if not set.
303
     * @return Handler
304
     */
305 2
    public function getHandler()
306
    {
307 2
        if (static::$_handler === null) {
308
            static::$_handler = new Handler($this->config);
309
            static::$_handler->setUserAgent('hiart/0.x');
310
        }
311
312 2
        return static::$_handler;
313
    }
314
315
    /**
316
     * Set handler manually.
317
     * @param Handler $value
318
     * @return void
319
     */
320 2
    public function setHandler($value)
321
    {
322 2
        static::$_handler = $value;
323 2
    }
324
325
    /**
326
     * Try to decode error information if it is valid json, return it if not.
327
     * @param $body
328
     * @return mixed
329
     */
330
    protected function decodeErrorBody($body)
331
    {
332
        try {
333
            $decoded = Json::decode($body);
334
            if (isset($decoded['error'])) {
335
                $decoded['error'] = preg_replace('/\b\w+?Exception\[/',
336
                    "<span style=\"color: red;\">\\0</span>\n               ", $decoded['error']);
337
            }
338
339
            return $decoded;
340
        } catch (InvalidParamException $e) {
341
            return $body;
342
        }
343
    }
344
345
    /**
346
     * Setter for errorChecker.
347
     * @param Closure|array $value
348
     * @return void
349
     */
350 2
    public function setErrorChecker($value)
351
    {
352 2
        $this->_errorChecker = $value;
0 ignored issues
show
Documentation Bug introduced by
It seems like $value can also be of type array. However, the property $_errorChecker is declared as type object<Closure>. 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...
353 2
    }
354
355
    /**
356
     * Checks response with errorChecker callback and raises exception if error.
357
     * @param array  $response response data from API
358
     * @param string $url      request URL
359
     * @param array  $options  request data
360
     * @throws ErrorResponseException
361
     * @return array
362
     */
363 2
    protected function checkResponse($response, $url, $options)
364
    {
365 2
        if (isset($this->_errorChecker)) {
366 2
            $error = call_user_func($this->_errorChecker, $response);
367 2
            if ($error !== null) {
368 1
                throw new ErrorResponseException($error, [
369 1
                    'requestUrl' => $url,
370 1
                    'request'    => $options,
371 1
                    'response'   => $response,
372 1
                ]);
373
            }
374 1
        }
375
376 1
        return $response;
377
    }
378
}
379