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 ( 34e8b0...476ee1 )
by Cees-Jan
02:02
created

Request::send()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 5
crap 2
1
<?php
2
3
/**
4
 * This file is part of ReactGuzzleRing.
5
 *
6
 ** (c) 2014 Cees-Jan Kiewiet
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
namespace WyriHaximus\React\Guzzle\HttpClient;
12
13
use GuzzleHttp\Psr7\Response;
14
use Psr\Http\Message\RequestInterface;
15
use React\EventLoop\LoopInterface;
16
use React\HttpClient\Client as ReactHttpClient;
17
use React\HttpClient\Request as HttpRequest;
18
use React\HttpClient\Response as HttpResponse;
19
use React\Promise\Deferred;
20
use React\Stream\Stream as ReactStream;
21
use Exception;
22
23
/**
24
 * Class Request
25
 *
26
 * @package WyriHaximus\React\Guzzle\HttpClient
27
 */
28
class Request
29
{
30
    /**
31
     * @var ReactHttpClient
32
     */
33
    protected $httpClient;
34
35
    /**
36
     * @var LoopInterface
37
     */
38
    protected $loop;
39
40
    /**
41
     * @var HttpResponse
42
     */
43
    protected $httpResponse;
44
45
    /**
46
     * @var string
47
     */
48
    protected $buffer = '';
49
50
    /**
51
     * @var Stream
52
     */
53
    protected $stream;
54
55
    /**
56
     * @var \Exception
57
     */
58
    protected $error = '';
59
60
    /**
61
     * @var \React\EventLoop\Timer\TimerInterface
62
     */
63
    protected $connectionTimer;
64
65
    /**
66
     * @var \React\EventLoop\Timer\TimerInterface
67
     */
68
    protected $requestTimer;
69
70
    /**
71
     * @var ProgressInterface
72
     */
73
    protected $progress;
74
75
    /**
76
     * @var Deferred
77
     */
78
    protected $deferred;
79
80
    /**
81
     * @var array
82
     */
83
    protected $options;
84
85
    /**
86
     * @var array
87
     */
88
    protected $defaultOptions = [
89
        'stream' => false,
90
        'connect_timeout' => 0,
91
        'timeout' => 0,
92
        'delay' => 0,
93
    ];
94
95
    /**
96
     * @var RequestInterface
97
     */
98
    protected $request;
99
100
    /**
101
     * @var bool
102
     */
103
    protected $connectionTimedOut = false;
104
105
    /**
106
     * @param RequestInterface $request
107
     * @param array $options
108
     * @param ReactHttpClient $httpClient
109
     * @param LoopInterface $loop
110
111
     */
112 4
    protected function __construct(
113
        RequestInterface $request,
114
        array $options,
115
        ReactHttpClient $httpClient,
116
        LoopInterface $loop
117
    ) {
118 4
        $this->request = $request;
119 4
        $this->applyOptions($options);
120 4
        $this->httpClient = $httpClient;
121 4
        $this->loop = $loop;
122 4
    }
123
124
    /**
125
     * @param RequestInterface $request
126
     * @param array $options
127
     * @param ReactHttpClient $httpClient
128
     * @param LoopInterface $loop
129
     * @return \React\Promise\Promise
130
     */
131 2
    public static function send(
132
        RequestInterface $request,
133
        array $options,
134
        ReactHttpClient $httpClient,
135
        LoopInterface $loop,
136
        Request $requestObject = null
137
    ) {
138 2
        if ($requestObject === null) {
139 1
            $requestObject = new static($request, $options, $httpClient, $loop);
140 1
        }
141 2
        return $requestObject->perform();
142
    }
143
144
    /**
145
     * @return \React\Promise\Promise
146
     */
147 2
    protected function perform()
148
    {
149 2
        $this->deferred = new Deferred();
150
151 2
        $this->loop->addTimer(
152 2
            (int)$this->options['delay'] / 1000,
0 ignored issues
show
Documentation introduced by
(int) $this->options['delay'] / 1000 is of type integer, but the function expects a object<React\EventLoop\numeric>.

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...
153
            function () {
154 1
                $this->tickRequest();
155 1
            }
156 2
        );
157
158 2
        return $this->deferred->promise();
159
    }
160
161
    /**
162
     *
163
     */
164 1
    protected function tickRequest()
165
    {
166
        $this->loop->futureTick(function () {
167 1
            $request = $this->setupRequest();
168 1
            $this->setupListeners($request);
169
170 1
            $body = $this->request->getBody()->getContents();
171
172 1
            $this->progress->onSending($body);
173
174 1
            $this->setConnectionTimeout($request);
175 1
            $request->end($body);
176 1
            $this->setRequestTimeout($request);
177 1
        });
178 1
    }
179
180
    /**
181
     * @return HttpRequest mixed
182
     */
183 1
    protected function setupRequest()
184
    {
185 1
        $headers = [];
186 1
        foreach ($this->request->getHeaders() as $key => $values) {
187 1
            $headers[$key] = implode(';', $values);
188 1
        }
189
190 1
        return $this->httpClient->request(
191 1
            $this->request->getMethod(),
192 1
            (string)$this->request->getUri(),
193 1
            $headers,
194 1
            $this->request->getProtocolVersion()
195 1
        );
196
    }
197
198
    /**
199
     * @param HttpRequest $request
200
     */
201 1
    protected function setupListeners(HttpRequest $request)
202
    {
203 1
        $request->on(
204 1
            'headers-written',
205
            function () {
206
                $this->onHeadersWritten();
207
            }
208 1
        );
209 1
        $request->on(
210 1
            'drain',
211
            function () {
212
                $this->progress->onSent();
213
            }
214 1
        );
215 1
        $request->on(
216 1
            'response',
217
            function (HttpResponse $response) use ($request) {
218
                $this->onResponse($response, $request);
219
            }
220 1
        );
221 1
        $request->on(
222 1
            'error',
223
            function ($error) {
224
                $this->onError($error);
225
            }
226 1
        );
227 1
        $request->on(
228 1
            'end',
229
            function () {
230
                $this->onEnd();
231
            }
232 1
        );
233 1
    }
234
235
    /**
236
     * @param HttpRequest $request
237
     */
238 1 View Code Duplication
    public function setConnectionTimeout(HttpRequest $request)
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...
239
    {
240 1
        if ($this->options['connect_timeout'] > 0) {
241 1
            $this->connectionTimer = $this->loop->addTimer(
242 1
                $this->options['connect_timeout'],
243
                function () use ($request) {
244
                    $request->closeError(new \Exception('Connection time out'));
245
                }
246 1
            );
247 1
        }
248 1
    }
249
250
    /**
251
     * @param HttpRequest $request
252
     */
253 2 View Code Duplication
    public function setRequestTimeout(HttpRequest $request)
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...
254
    {
255 2
        if ($this->options['timeout'] > 0) {
256 1
            $this->requestTimer = $this->loop->addTimer(
257 1
                $this->options['timeout'],
258
                function () use ($request) {
259
                    $request->closeError(new \Exception('Transaction time out'));
260
                }
261 1
            );
262 1
        }
263 2
    }
264
265
    protected function onHeadersWritten()
266
    {
267
        if ($this->connectionTimer !== null && $this->loop->isTimerActive($this->connectionTimer)) {
268
            $this->loop->cancelTimer($this->connectionTimer);
269
        }
270
    }
271
272
    /**
273
     * @param HttpResponse $response
274
     * @param HttpRequest  $request
275
     */
276
    protected function onResponse(HttpResponse $response, HttpRequest $request)
277
    {
278
        $this->httpResponse = $response;
279
        if (isset($this->options['sink'])) {
280
            $this->saveTo();
281
        }
282
283
        $this->handleResponse($request);
284
    }
285
286
    protected function saveTo()
287
    {
288
        $saveTo = $this->options['sink'];
289
290
        $writeStream = fopen($saveTo, 'w');
291
        stream_set_blocking($writeStream, 0);
292
        $saveToStream = new ReactStream($writeStream, $this->loop);
293
294
        $saveToStream->on(
295
            'end',
296
            function () {
297
                $this->onEnd();
298
            }
299
        );
300
301
        $this->httpResponse->pipe($saveToStream);
302
    }
303
304
    /**
305
     * @param string $data
306
     */
307
    protected function onData($data)
308
    {
309
        $this->progress->onData($data);
310
    }
311
312
    /**
313
     * @param \Exception $error
314
     */
315 View Code Duplication
    protected function onError(\Exception $error)
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...
316
    {
317
        if ($this->requestTimer !== null && $this->loop->isTimerActive($this->requestTimer)) {
318
            $this->loop->cancelTimer($this->requestTimer);
319
        }
320
321
        if ($this->connectionTimer !== null && $this->loop->isTimerActive($this->connectionTimer)) {
322
            $this->loop->cancelTimer($this->connectionTimer);
323
        }
324
325
        $this->error = $error;
326
        $this->deferred->reject($this->error);
327
    }
328
329
    /**
330
     *
331
     */
332 View Code Duplication
    protected function onEnd()
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...
333
    {
334
        if ($this->requestTimer !== null && $this->loop->isTimerActive($this->requestTimer)) {
335
            $this->loop->cancelTimer($this->requestTimer);
336
        }
337
338
        if ($this->connectionTimer !== null && $this->loop->isTimerActive($this->connectionTimer)) {
339
            $this->loop->cancelTimer($this->connectionTimer);
340
        }
341
342
        $this->loop->futureTick(function () {
343
            if ($this->httpResponse === null) {
344
                $this->deferred->reject($this->error);
345
            }
346
        });
347
    }
348
349
    /**
350
     *
351
     */
352
    protected function handleResponse($request)
353
    {
354
        $this->progress->onResponse($this->httpResponse);
355
356
        $this->createStream($request);
357
358
        $response = new Response(
359
            $this->httpResponse->getCode(),
360
            $this->httpResponse->getHeaders(),
361
            $this->stream,
362
            $this->httpResponse->getVersion(),
363
            $this->httpResponse->getReasonPhrase()
364
        );
365
366
        if (!$this->options['stream']) {
367
            return $request->on('end', function () use ($response) {
368
                $this->resolveResponse($response);
369
            });
370
        }
371
372
        $this->resolveResponse($response);
373
    }
374
375
    protected function resolveResponse($response)
376
    {
377
        $this->loop->futureTick(function () use ($response) {
378
            $this->deferred->resolve($response);
379
        });
380
    }
381
382
    protected function createStream($request)
383
    {
384
        $this->stream = new Stream([
385
            'response' => $this->httpResponse,
386
            'request' => $request,
387
            'loop' => $this->loop,
388
        ]);
389
    }
390
    
391 4
    private function applyOptions(array $options = [])
392
    {
393 4
        $this->options = array_replace_recursive($this->defaultOptions, $options);
394
        
395
        // provides backwards compatibility for Guzzle 3-5.
396 4
        if (isset($this->options['client'])) {
397 2
            $this->options = array_merge($this->options, $this->options['client']);
398 2
            unset($this->options['client']);
399 2
        }
400
        
401
        // provides for backwards compatibility for Guzzle 3-5
402 4
        if (isset($this->options['save_to'])) {
403
            $this->options['sink'] = $options['save_to'];
404
            unset($this->options['save_to']);
405
        }
406
        
407 4
        if (isset($this->options['progress']) && is_callable($this->options['progress'])) {
408
            $this->progress = new Progress($this->options['progress']);
409
        } else {
410 4
            $this->progress = new Progress(function () {
411 4
            });
412
        }
413 4
    }
414
}
415