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 ( 8f64b9...e82cb7 )
by Cees-Jan
06:31
created

Client::createRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace ApiClients\Foundation\Transport;
5
6
use ApiClients\Foundation\Hydrator\Factory;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, ApiClients\Foundation\Transport\Factory.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
7
use ApiClients\Foundation\Hydrator\Hydrator;
8
use GuzzleHttp\Client as GuzzleClient;
9
use GuzzleHttp\Psr7\Request;
10
use GuzzleHttp\Psr7\Response;
11
use Psr\Http\Message\RequestInterface;
12
use Psr\Http\Message\ResponseInterface;
13
use Psr\Http\Message\UriInterface;
14
use React\Cache\CacheInterface;
15
use React\EventLoop\LoopInterface;
16
use React\Promise\Deferred;
17
use React\Promise\PromiseInterface;
18
use function React\Promise\reject;
19
use React\Promise\RejectedPromise;
20
use function React\Promise\resolve;
21
use function WyriHaximus\React\futureFunctionPromise;
22
23
class Client
24
{
25
    const DEFAULT_OPTIONS = [
26
        Options::SCHEMA => 'https',
27
        Options::PATH => '/',
28
        Options::USER_AGENT => 'WyriHaximus/php-api-client',
29
        Options::HEADERS => [],
30
    ];
31
32
    /**
33
     * @var GuzzleClient
34
     */
35
    protected $handler;
36
37
    /**
38
     * @var LoopInterface
39
     */
40
    protected $loop;
41
42
    /**
43
     * @var array
44
     */
45
    protected $options = [];
46
47
    /**
48
     * @var Hydrator
49
     */
50
    protected $hydrator;
51
52
    /**
53
     * @var CacheInterface
54
     */
55
    protected $cache;
56
57
    /**
58
     * @param LoopInterface $loop
59
     * @param GuzzleClient $handler
60
     * @param array $options
61
     */
62 12
    public function __construct(LoopInterface $loop, GuzzleClient $handler, array $options = [])
63
    {
64 12
        $this->loop = $loop;
65 12
        $this->handler = $handler;
66 12
        $this->options = $options + self::DEFAULT_OPTIONS;
67 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...
68 3
            $this->cache = $this->options[Options::CACHE];
69
        }
70 12
        $this->hydrator = $this->determineHydrator();
71 12
    }
72
73
    /**
74
     * @return Hydrator
75
     */
76 12
    protected function determineHydrator(): Hydrator
77
    {
78 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...
79
            return $this->options[Options::HYDRATOR];
80
        }
81
82 12
        return Factory::create($this->options[Options::HYDRATOR] ?? []);
83
    }
84
85
    /**
86
     * @param string $path
87
     * @param bool $refresh
88
     * @return PromiseInterface
89
     */
90 5
    public function request(string $path, bool $refresh = false): PromiseInterface
91
    {
92
        return $this->requestRaw($path, $refresh)->then(function ($json) {
93 3
            return $this->jsonDecode($json);
94 5
        });
95
    }
96
97
    /**
98
     * @param string $path
99
     * @param bool $refresh
100
     * @return PromiseInterface
101
     */
102 5
    public function requestRaw(string $path, bool $refresh = false): PromiseInterface
103
    {
104 5
        return $this->requestPsr7(
105 5
            $this->createRequest($path),
106
            $refresh
107
        )->then(function ($response) {
108 3
            return resolve($response->getBody()->getContents());
109 5
        });
110
    }
111
112
    /**
113
     * @param UriInterface $uri
114
     * @return PromiseInterface
115
     */
116 3
    protected function checkCache(UriInterface $uri): PromiseInterface
117
    {
118 3
        if (!($this->cache instanceof CacheInterface)) {
119 1
            return reject();
120
        }
121
122 2
        $key = $this->determineCacheKey($uri);
123
        return $this->cache->get($key)->then(function ($document) {
124 1
            $document = json_decode($document, true);
125 1
            $response = new Response(
126 1
                $document['status_code'],
127 1
                $document['headers'],
128 1
                $document['body'],
129 1
                $document['protocol_version'],
130 1
                $document['reason_phrase']
131
            );
132
133 1
            return resolve($response);
134 2
        });
135
    }
136
137
    /**
138
     * @param RequestInterface $request
139
     * @param ResponseInterface $response
140
     */
141 2
    protected function storeCache(RequestInterface $request, ResponseInterface $response)
142
    {
143 2
        if (!($this->cache instanceof CacheInterface)) {
144
            return;
145
        }
146
147
        $document = [
148 2
            'body' => $response->getBody()->getContents(),
149 2
            'headers' => $response->getHeaders(),
150 2
            'protocol_version' => $response->getProtocolVersion(),
151 2
            'reason_phrase' => $response->getReasonPhrase(),
152 2
            'status_code' => $response->getStatusCode(),
153
        ];
154
155 2
        $key = $this->determineCacheKey($request->getUri());
156
157 2
        $this->cache->set($key, json_encode($document));
158 2
    }
159
160
    /**
161
     * @param UriInterface $uri
162
     * @return string
163
     */
164 3
    protected function determineCacheKey(UriInterface $uri): string
165
    {
166 3
        return $this->stripExtraSlashes(
167
            implode(
168 3
                '/',
169
                [
170 3
                    $uri->getScheme(),
171 3
                    $uri->getHost(),
172 3
                    $uri->getPort(),
173 3
                    $uri->getPath(),
174 3
                    md5($uri->getQuery()),
175
                ]
176
            )
177
        );
178
    }
179
180
    /**
181
     * @param string $string
182
     * @return string
183
     */
184 3
    protected function stripExtraSlashes(string $string): string
185
    {
186 3
        return preg_replace('#/+#', '/', $string);
187
    }
188
189
    /**
190
     * @param RequestInterface $request
191
     * @param bool $refresh
192
     * @return PromiseInterface
193
     */
194 5
    public function requestPsr7(RequestInterface $request, bool $refresh = false): PromiseInterface
195
    {
196 5
        $promise = new RejectedPromise();
197
198 5
        if (!$refresh) {
199 3
            $promise = $this->checkCache($request->getUri());
200
        }
201
202
        return $promise->otherwise(function () use ($request) {
203 4
            $deferred = new Deferred();
204
205 4
            $this->handler->sendAsync(
206
                $request
207
            )->then(function (ResponseInterface $response) use ($deferred, $request) {
208 2
                $contents = $response->getBody()->getContents();
209 2
                $this->storeCache(
210
                    $request,
211 2
                    new Response(
212 2
                        $response->getStatusCode(),
213 2
                        $response->getHeaders(),
214
                        $contents,
215 2
                        $response->getProtocolVersion(),
216 2
                        $response->getReasonPhrase()
217
                    )
218
                );
219 2
                $deferred->resolve(
220 2
                    new Response(
221 2
                        $response->getStatusCode(),
222 2
                        $response->getHeaders(),
223
                        $contents,
224 2
                        $response->getProtocolVersion(),
225 2
                        $response->getReasonPhrase()
226
                    )
227
                );
228
            }, function ($error) use ($deferred) {
229
                $deferred->reject($error);
230 4
            });
231
232 4
            return $deferred->promise();
233 5
        });
234
    }
235
236
    /**
237
     * @param string $path
238
     * @return RequestInterface
239
     */
240 5
    protected function createRequest(string $path): RequestInterface
241
    {
242 5
        $url = $this->getBaseURL() . $path;
243 5
        $headers = $this->getHeaders();
244 5
        return new Request('GET', $url, $headers);
245
    }
246
247
    /**
248
     * @return array
249
     */
250 5
    public function getHeaders(): array
251
    {
252
        $headers = [
253 5
            'User-Agent' => $this->options[Options::USER_AGENT],
254
        ];
255 5
        $headers += $this->options[Options::HEADERS];
256 5
        return $headers;
257
    }
258
259
    /**
260
     * @param string $json
261
     * @return PromiseInterface
262
     */
263
    public function jsonDecode(string $json): PromiseInterface
264
    {
265 3
        return futureFunctionPromise($this->loop, $json, function ($json) {
266 3
            return json_decode($json, true);
267 3
        });
268
    }
269
270
    /**
271
     * @return Hydrator
272
     */
273 1
    public function getHydrator(): Hydrator
274
    {
275 1
        return $this->hydrator;
276
    }
277
278
    /**
279
     * @return LoopInterface
280
     */
281 3
    public function getLoop(): LoopInterface
282
    {
283 3
        return $this->loop;
284
    }
285
286
    /**
287
     * @return string
288
     */
289 8
    public function getBaseURL(): string
290
    {
291 8
        return $this->options[Options::SCHEMA] . '://' . $this->options[Options::HOST] . $this->options[Options::PATH];
292
    }
293
}
294