Completed
Pull Request — master (#6)
by
unknown
03:48 queued 10s
created

AbstractClient::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 9
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 14
ccs 0
cts 7
cp 0
crap 2
rs 9.9666
1
<?php
2
3
namespace Slides\Connector\Auth\Clients;
4
5
use GuzzleHttp\Client;
6
use GuzzleHttp\Middleware;
7
use GuzzleHttp\Psr7\Request;
8
use GuzzleHttp\HandlerStack;
9
use GuzzleHttp\Psr7\Response;
10
use InvalidArgumentException;
11
use Psr\Http\Message\RequestInterface;
12
use RuntimeException;
13
14
/**
15
 * Class AbstractClient
16
 *
17
 * @package Slides\Connector\Auth\Clients
18
 */
19
abstract class AbstractClient
20
{
21
    /**
22
     * HTTP client
23
     *
24
     * @var Client
25
     */
26
    protected $client;
27
28
    /**
29
     * Base URL of resource
30
     *
31
     * @var string
32
     */
33
    protected $baseUrl;
34
35
    /**
36
     * Client's request
37
     *
38
     * @var Request
39
     */
40
    protected $request;
41
42
    /**
43
     * Client's response
44
     *
45
     * @var Response
46
     */
47
    protected $response;
48
49
    /**
50
     * HTTP request headers
51
     *
52
     * @var array
53
     */
54
    protected $headers = [];
55
56
    /**
57
     * The query parameters.
58
     *
59
     * @var array
60
     */
61
    protected $query = [];
62
63
    /**
64
     * The body of an entity enclosing request.
65
     *
66
     * @var array
67
     */
68
    protected $body;
69
70
    /**
71
     * The composed credentials.
72
     *
73
     * @var array
74
     */
75
    protected $credentials = [];
76
77
    /**
78
     * Whether client is aborted to perform requests
79
     *
80
     * @var bool
81
     */
82
    protected $isAborted = false;
83
84
    /**
85
     * List of supported requests
86
     *
87
     * @return array
88
     */
89
    abstract public function requests(): array;
90
91
    /**
92
     * Authorize the client
93
     *
94
     * @return void
95
     */
96
    abstract protected function authorize();
97
98
    /**
99
     * Get client's credentials
100
     *
101
     * @return array
102
     */
103
    abstract protected function credentials(): array;
104
105
    /**
106
     * BaseClient constructor.
107
     *
108
     * @param array $config
109
     * @param array $credentials
110
     */
111
    public function __construct(array $config = [], array $credentials = [])
112
    {
113
        $this->credentials = array_merge($this->credentials(), $credentials);
114
115
        $this->client = new Client(array_merge([
116
            'base_uri' => $this->baseUrl,
117
            'handler' => $this->createClientHandler(),
118
            'http_errors' => false,
119
            'headers' => [
120
                'User-Agent' => null
121
            ]
122
        ], $config));
123
124
        $this->boot();
125
    }
126
127
    /**
128
     * Boot the client
129
     */
130
    protected function boot()
131
    {
132
        if (!$this->baseUrl) {
133
            throw new InvalidArgumentException('Base URL should be defined');
134
        }
135
136
        $this->authorize();
137
    }
138
139
    /**
140
     * Send a request
141
     *
142
     * @param AbstractRequest $request
143
     * @param array $options Request options
144
     *
145
     * @return mixed
146
     *
147
     * @throws RuntimeException
148
     */
149
    public function request(AbstractRequest $request, array $options = [])
150
    {
151
        if (!$this->resolveRequest($request)) {
152
            throw new InvalidArgumentException(static::class . ': ' . get_class($request) . ' should be registered');
153
        }
154
155
        $request->mergeOptions($options)
156
            ->mergeOptions(array_filter([
157
                'query' => $this->query,
158
                'json' => $this->body
159
            ]))
160
            ->compose();
161
162
        $response = $this->send(
163
            $request->getMethod(),
164
            $request->getUri(),
165
            $request->getOptions()
166
        );
167
168
        $request->setResponse($response);
169
170
        // Validate a response
171
        if (!$request->success()) {
172
            throw new RuntimeException($response);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type array; however, parameter $message of RuntimeException::__construct() does only seem to accept string, 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

172
            throw new RuntimeException(/** @scrutinizer ignore-type */ $response);
Loading history...
173
        }
174
175
        return $response;
176
    }
177
178
    /**
179
     * Send a request
180
     *
181
     * @param string $method
182
     * @param string $url
183
     * @param array $options
184
     *
185
     * @return array|null|string
186
     *
187
     * @throws
188
     */
189
    public function send(string $method, string $url, array $options = [])
190
    {
191
        if ($this->isAborted) {
192
            throw new RuntimeException('Cannot send a request while client is aborted');
193
        }
194
195
        $this->request = new Request($method, $url, $this->headers);
196
197
        if (!$this->response = $this->client->send($this->request, $options)) {
198
            return null;
199
        }
200
201
        $contents = (string) $this->response->getBody();
202
203
        return json_decode($contents, true) ?: $contents;
204
    }
205
206
    /**
207
     * Set headers
208
     *
209
     * @param array $headers
210
     */
211
    public function headers(array $headers)
212
    {
213
        $this->headers = $headers;
214
    }
215
216
    /**
217
     * Add query parameters.
218
     *
219
     * @param array $parameters
220
     *
221
     * @return void
222
     */
223
    public function query(array $parameters)
224
    {
225
        $this->query = array_merge($this->query, $parameters);
226
    }
227
228
    /**
229
     * Add body parameters.
230
     *
231
     * @param array $parameters
232
     */
233
    public function body(array $parameters)
234
    {
235
        $this->body = array_merge((array) $this->body, $parameters);
236
    }
237
238
    /**
239
     * Get credentials parameter
240
     *
241
     * @param string $key
242
     * @param mixed $default
243
     *
244
     * @return mixed
245
     */
246
    public function credential(string $key, $default = null)
247
    {
248
        return array_get($this->credentials, $key, $default);
249
    }
250
251
    /**
252
     * Set the client as aborted and send notification
253
     *
254
     * @return void
255
     */
256
    public function abort()
257
    {
258
        $this->isAborted = true;
259
    }
260
261
    /**
262
     * Get response
263
     *
264
     * @return Response
265
     */
266
    public function getResponse()
267
    {
268
        return $this->response;
269
    }
270
271
    /**
272
     * Create a handler for Guzzle HTTP Client
273
     *
274
     * @return HandlerStack
275
     */
276
    private function createClientHandler()
277
    {
278
        $stack = HandlerStack::create();
279
280
        $stack->push(Middleware::mapRequest(function (RequestInterface $request) {
281
            return $this->request = $request;
0 ignored issues
show
Documentation Bug introduced by
$request is of type Psr\Http\Message\RequestInterface, but the property $request was declared to be of type GuzzleHttp\Psr7\Request. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof 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 given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
282
        }));
283
284
        return $stack;
285
    }
286
287
    /**
288
     * Resolve request
289
     *
290
     * @param AbstractRequest $request
291
     *
292
     * @return AbstractRequest|null
293
     */
294
    private function resolveRequest(AbstractRequest $request)
295
    {
296
        foreach ($this->requests() as $class) {
297
            if (class_basename($class) !== class_basename($request)) {
298
                continue;
299
            }
300
301
            return $request;
302
        }
303
304
        return null;
305
    }
306
}