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
Pull Request — master (#2)
by Cees-Jan
26:18 queued 16:21
created

Client::streamBody()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 11
nc 1
nop 1
1
<?php declare(strict_types=1);
2
3
namespace ApiClients\Foundation\Transport;
4
5
use ApiClients\Foundation\Middleware\MiddlewareInterface;
6
use ApiClients\Foundation\Transport\CommandBus;
7
use GuzzleHttp\Client as GuzzleClient;
8
use GuzzleHttp\Psr7\Request as Psr7Request;
9
use GuzzleHttp\Psr7\Uri;
10
use Interop\Container\ContainerInterface;
11
use Psr\Http\Message\RequestInterface;
12
use Psr\Http\Message\ResponseInterface;
13
use React\EventLoop\LoopInterface;
14
use React\EventLoop\Timer\TimerInterface;
15
use React\Promise\CancellablePromiseInterface;
16
use React\Promise\PromiseInterface;
17
use function React\Promise\reject;
18
use function React\Promise\resolve;
19
use function WyriHaximus\React\futureFunctionPromise;
20
21
class Client
22
{
23
    const DEFAULT_OPTIONS = [
24
        Options::SCHEMA => 'https',
25
        Options::PATH => '/',
26
        Options::USER_AGENT => 'WyriHaximus/php-api-client',
27
        Options::HEADERS => [],
28
    ];
29
30
    /**
31
     * @var LoopInterface
32
     */
33
    protected $loop;
34
35
    /**
36
     * @var ContainerInterface
37
     */
38
    protected $container;
39
40
    /**
41
     * @var GuzzleClient
42
     */
43
    protected $handler;
44
45
    /**
46
     * @var array
47
     */
48
    protected $options = [];
49
50
    /**
51
     * @var MiddlewareInterface[]
52
     */
53
    protected $middleware = [];
54
55
    /**
56
     * @param LoopInterface $loop
57
     * @param ContainerInterface $container
58
     * @param GuzzleClient $handler
59
     * @param array $options
60
     */
61
    public function __construct(
62
        LoopInterface $loop,
63
        ContainerInterface $container,
64
        GuzzleClient $handler,
65
        array $options = []
66
    ) {
67
        $this->loop = $loop;
68
        $this->container = $container;
69
        $this->handler = $handler;
70
        $this->options = $options + self::DEFAULT_OPTIONS;
71
72
        if (isset($this->options[Options::MIDDLEWARE])
73
        ) {
74
            $this->middleware = $this->options[Options::MIDDLEWARE];
75
        }
76
    }
77 View Code Duplication
    protected function preRequest(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
78
        array $middlewares,
79
        RequestInterface $request,
80
        array $options
81
    ): CancellablePromiseInterface {
82
        $promise = resolve($request);
83
84
        foreach ($middlewares as $middleware) {
85
            $requestMiddleware = $middleware;
86
            $promise = $promise->then(function (RequestInterface $request) use ($options, $requestMiddleware) {
87
                return $requestMiddleware->pre($request, $options);
88
            });
89
        }
90
91
        return $promise;
92
    }
93
94 View Code Duplication
    protected function postRequest(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
95
        array $middlewares,
96
        ResponseInterface $response,
97
        array $options
98
    ): CancellablePromiseInterface {
99
        $promise = resolve($response);
100
101
        foreach ($middlewares as $middleware) {
102
            $responseMiddleware = $middleware;
103
            $promise = $promise->then(function (ResponseInterface $response) use ($options, $responseMiddleware) {
104
                return $responseMiddleware->post($response, $options);
105
            });
106
        }
107
108
        return $promise;
109
    }
110
111
    protected function constructMiddlewares(array $options): array
112
    {
113
        $set = $this->middleware;
114
115
        if (isset($options[Options::MIDDLEWARE])) {
116
            $set = $options[Options::MIDDLEWARE];
117
        }
118
119
        $middlewares = [];
120
        foreach ($set as $middleware) {
121
            if (!is_subclass_of($middleware, MiddlewareInterface::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \ApiClients\Foundation\M...dlewareInterface::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
122
                continue;
123
            }
124
125
            $middlewares[] = $this->container->get($middleware);
126
        }
127
128
        return $middlewares;
129
    }
130
131
    /**
132
     * @param RequestInterface $request
133
     * @param array $options
134
     * @return PromiseInterface
135
     */
136
    public function request(RequestInterface $request, array $options = []): PromiseInterface
137
    {
138
        $request = $this->applyApiSettingsToRequest($request);
139
        $middlewares = $this->constructMiddlewares($options);
140
141
        return $this->preRequest($middlewares, $request, $options)->then(function ($request) use ($options) {
142
            return resolve($this->handler->sendAsync(
143
                $request,
144
                $options
145
            ));
146
        }, function (ResponseInterface $response) {
147
            return resolve($response);
148
        })->then(function (ResponseInterface $response) use ($middlewares, $options) {
149
            return $this->postRequest($middlewares, $response, $options);
150
        });
151
    }
152
153
    protected function streamBody(Response $response)
154
    {
155
        $stream = $response->getResponse()->getBody();
156
        $this->loop->addPeriodicTimer(0.001, function (TimerInterface $timer) use ($stream, $response) {
157
            if ($stream->eof()) {
158
                $timer->cancel();
159
                $response->emit('end');
160
                return;
161
            }
162
163
            $size = $stream->getSize();
164
            if ($size === 0) {
165
                return;
166
            }
167
168
            $response->emit('data', [$stream->read($size)]);
169
        });
170
    }
171
172
    protected function applyApiSettingsToRequest(RequestInterface $request): RequestInterface
173
    {
174
        $uri = $request->getUri();
175
        if (substr((string)$uri, 0, 4) !== 'http') {
176
            $uri = Uri::resolve(
177
                new Uri(
178
                    $this->options[Options::SCHEMA] .
179
                    '://' .
180
                    $this->options[Options::HOST] .
181
                    $this->options[Options::PATH]
182
                ),
183
                $request->getUri()
184
            );
185
        }
186
187
        return new Psr7Request(
188
            $request->getMethod(),
189
            $uri,
190
            $this->getHeaders() + $request->getHeaders(),
191
            $request->getBody(),
192
            $request->getProtocolVersion()
193
        );
194
    }
195
196
    /**
197
     * @return array
198
     */
199
    public function getHeaders(): array
200
    {
201
        $headers = [
202
            'User-Agent' => $this->options[Options::USER_AGENT],
203
        ];
204
        $headers += $this->options[Options::HEADERS];
205
        return $headers;
206
    }
207
208
    /**
209
     * @return string
210
     */
211
    public function getBaseURL(): string
212
    {
213
        return $this->options[Options::SCHEMA] . '://' . $this->options[Options::HOST] . $this->options[Options::PATH];
214
    }
215
}
216