GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 185d91...d5cee0 )
by Cees-Jan
08:42
created

Client::determineHydrator()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 3
Ratio 21.43 %

Code Coverage

Tests 5
CRAP Score 4.3731

Importance

Changes 0
Metric Value
dl 3
loc 14
ccs 5
cts 7
cp 0.7143
rs 9.2
c 0
b 0
f 0
cc 4
eloc 7
nc 3
nop 0
crap 4.3731
1
<?php
2
declare(strict_types=1);
3
4
namespace ApiClients\Foundation\Transport;
5
6
use ApiClients\Foundation\Hydrator\Factory as HydratorFactory;
7
use ApiClients\Foundation\Hydrator\Hydrator;
8
use ApiClients\Foundation\Hydrator\Options as HydratorOptions;
9
use GuzzleHttp\Client as GuzzleClient;
10
use GuzzleHttp\Psr7\Request;
11
use GuzzleHttp\Psr7\Response;
12
use Psr\Http\Message\RequestInterface;
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\UriInterface;
15
use React\Cache\CacheInterface;
16
use React\EventLoop\LoopInterface;
17
use React\Promise\Deferred;
18
use React\Promise\PromiseInterface;
19
use function React\Promise\reject;
20
use React\Promise\RejectedPromise;
21
use function React\Promise\resolve;
22
use function WyriHaximus\React\futureFunctionPromise;
23
24
class Client
25
{
26
    const DEFAULT_OPTIONS = [
27
        Options::SCHEMA => 'https',
28
        Options::PATH => '/',
29
        Options::USER_AGENT => 'WyriHaximus/php-api-client',
30
        Options::HEADERS => [],
31
    ];
32
33
    /**
34
     * @var GuzzleClient
35
     */
36
    protected $handler;
37
38
    /**
39
     * @var LoopInterface
40
     */
41
    protected $loop;
42
43
    /**
44
     * @var array
45
     */
46
    protected $options = [];
47
48
    /**
49
     * @var Hydrator
50
     */
51
    protected $hydrator;
52
53
    /**
54
     * @var CacheInterface
55
     */
56
    protected $cache;
57
58
    /**
59
     * @param LoopInterface $loop
60
     * @param GuzzleClient $handler
61
     * @param array $options
62
     */
63 12
    public function __construct(LoopInterface $loop, GuzzleClient $handler, array $options = [])
64
    {
65 12
        $this->loop = $loop;
66 12
        $this->handler = $handler;
67 12
        $this->options = $options + self::DEFAULT_OPTIONS;
68 12 View Code Duplication
        if (isset($this->options[Options::CACHE]) && $this->options[Options::CACHE] instanceof CacheInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
69 3
            $this->cache = $this->options[Options::CACHE];
70
        }
71 12
        $this->hydrator = $this->determineHydrator();
72 12
    }
73
74
    /**
75
     * @return Hydrator
76
     * @throws \Exception
77
     */
78 12
    protected function determineHydrator(): Hydrator
79
    {
80 12 View Code Duplication
        if (isset($this->options[Options::HYDRATOR]) && $this->options[Options::HYDRATOR] instanceof Hydrator) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
81
            return $this->options[Options::HYDRATOR];
82
        }
83
84 12
        if (!isset($this->options[Options::HYDRATOR_OPTIONS])) {
85
            throw new \Exception('Missing Hydrator options');
86
        }
87
88 12
        $this->ensureExtraProperties();
89 12
90
        return HydratorFactory::create($this->options[Options::HYDRATOR_OPTIONS]);
91
    }
92 12
93
    protected function ensureExtraProperties()
94 12
    {
95
        if (!isset($this->options[Options::HYDRATOR_OPTIONS][HydratorOptions::EXTRA_PROPERTIES])) {
96
            $this->options[Options::HYDRATOR_OPTIONS][HydratorOptions::EXTRA_PROPERTIES] = [];
97
        }
98
99
        $this->options[Options::HYDRATOR_OPTIONS][HydratorOptions::EXTRA_PROPERTIES]['transport'] = [
100
            'method' => 'setTransport',
101
            'value'  => $this,
102 5
        ];
103
    }
104
105 3
    /**
106 5
     * @param string $path
107
     * @param bool $refresh
108
     * @return PromiseInterface
109
     */
110
    public function request(string $path, bool $refresh = false): PromiseInterface
111
    {
112
        return $this->requestRaw($path, $refresh)->then(function ($json) {
113
            return $this->jsonDecode($json);
114 5
        });
115
    }
116 5
117 5
    /**
118
     * @param string $path
119
     * @param bool $refresh
120 3
     * @return PromiseInterface
121 5
     */
122
    public function requestRaw(string $path, bool $refresh = false): PromiseInterface
123
    {
124
        return $this->requestPsr7(
125
            $this->createRequest($path),
126
            $refresh
127
        )->then(function ($response) {
128 3
            return resolve($response->getBody()->getContents());
129
        });
130 3
    }
131 1
132
    /**
133
     * @param UriInterface $uri
134 2
     * @return PromiseInterface
135
     */
136 1
    protected function checkCache(UriInterface $uri): PromiseInterface
137 1
    {
138 1
        if (!($this->cache instanceof CacheInterface)) {
139 1
            return reject();
140 1
        }
141 1
142 1
        $key = $this->determineCacheKey($uri);
143
        return $this->cache->get($key)->then(function ($document) {
144
            $document = json_decode($document, true);
145 1
            $response = new Response(
146 2
                $document['status_code'],
147
                $document['headers'],
148
                $document['body'],
149
                $document['protocol_version'],
150
                $document['reason_phrase']
151
            );
152
153 2
            return resolve($response);
154
        });
155 2
    }
156
157
    /**
158
     * @param RequestInterface $request
159
     * @param ResponseInterface $response
160 2
     */
161 2
    protected function storeCache(RequestInterface $request, ResponseInterface $response)
162 2
    {
163 2
        if (!($this->cache instanceof CacheInterface)) {
164 2
            return;
165
        }
166
167 2
        $document = [
168
            'body' => $response->getBody()->getContents(),
169 2
            'headers' => $response->getHeaders(),
170 2
            'protocol_version' => $response->getProtocolVersion(),
171
            'reason_phrase' => $response->getReasonPhrase(),
172
            'status_code' => $response->getStatusCode(),
173
        ];
174
175
        $key = $this->determineCacheKey($request->getUri());
176 3
177
        $this->cache->set($key, json_encode($document));
178 3
    }
179
180 3
    /**
181
     * @param UriInterface $uri
182 3
     * @return string
183 3
     */
184 3
    protected function determineCacheKey(UriInterface $uri): string
185 3
    {
186 3
        return $this->stripExtraSlashes(
187
            implode(
188
                '/',
189
                [
190
                    $uri->getScheme(),
191
                    $uri->getHost(),
192
                    $uri->getPort(),
193
                    $uri->getPath(),
194
                    md5($uri->getQuery()),
195
                ]
196 3
            )
197
        );
198 3
    }
199
200
    /**
201
     * @param string $string
202
     * @return string
203
     */
204
    protected function stripExtraSlashes(string $string): string
205
    {
206 5
        return preg_replace('#/+#', '/', $string);
207
    }
208 5
209
    /**
210 5
     * @param RequestInterface $request
211 3
     * @param bool $refresh
212
     * @return PromiseInterface
213
     */
214
    public function requestPsr7(RequestInterface $request, bool $refresh = false): PromiseInterface
215 4
    {
216
        $promise = new RejectedPromise();
217 4
218
        if (!$refresh) {
219
            $promise = $this->checkCache($request->getUri());
220 2
        }
221 2
222
        return $promise->otherwise(function () use ($request) {
223 2
            $deferred = new Deferred();
224 2
225 2
            $this->handler->sendAsync(
226
                $request
227 2
            )->then(function (ResponseInterface $response) use ($deferred, $request) {
228 2
                $contents = $response->getBody()->getContents();
229
                $this->storeCache(
230
                    $request,
231 2
                    new Response(
232 2
                        $response->getStatusCode(),
233 2
                        $response->getHeaders(),
234 2
                        $contents,
235
                        $response->getProtocolVersion(),
236 2
                        $response->getReasonPhrase()
237 2
                    )
238
                );
239
                $deferred->resolve(
240
                    new Response(
241
                        $response->getStatusCode(),
242 4
                        $response->getHeaders(),
243
                        $contents,
244 4
                        $response->getProtocolVersion(),
245 5
                        $response->getReasonPhrase()
246
                    )
247
                );
248
            }, function ($error) use ($deferred) {
249
                $deferred->reject($error);
250
            });
251
252 5
            return $deferred->promise();
253
        });
254 5
    }
255 5
256 5
    /**
257
     * @param string $path
258
     * @return RequestInterface
259
     */
260
    protected function createRequest(string $path): RequestInterface
261
    {
262 5
        $url = $this->getBaseURL() . $path;
263
        $headers = $this->getHeaders();
264
        return new Request('GET', $url, $headers);
265 5
    }
266
267 5
    /**
268 5
     * @return array
269
     */
270
    public function getHeaders(): array
271
    {
272
        $headers = [
273
            'User-Agent' => $this->options[Options::USER_AGENT],
274
        ];
275
        $headers += $this->options[Options::HEADERS];
276
        return $headers;
277 3
    }
278 3
279 3
    /**
280
     * @param string $json
281
     * @return PromiseInterface
282
     */
283
    public function jsonDecode(string $json): PromiseInterface
284
    {
285 1
        return futureFunctionPromise($this->loop, $json, function ($json) {
286
            return json_decode($json, true);
287 1
        });
288
    }
289
290
    /**
291
     * @return Hydrator
292
     */
293 3
    public function getHydrator(): Hydrator
294
    {
295 3
        return $this->hydrator;
296
    }
297
298
    /**
299
     * @return LoopInterface
300
     */
301 8
    public function getLoop(): LoopInterface
302
    {
303 8
        return $this->loop;
304
    }
305
306
    /**
307
     * @return string
308
     */
309
    public function getBaseURL(): string
310
    {
311
        return $this->options[Options::SCHEMA] . '://' . $this->options[Options::HOST] . $this->options[Options::PATH];
312
    }
313
}
314