Completed
Pull Request — master (#42)
by Chad
01:31
created

GuzzleAdapter   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 129
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 129
c 0
b 0
f 0
wmc 10
lcom 1
cbo 5
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A start() 0 14 1
B end() 0 39 6
A fulfillPromises() 0 19 2
1
<?php
2
3
namespace TraderInteractive\Api;
4
5
use ArrayObject;
6
use DominionEnterprises\Util;
7
use GuzzleHttp\Client as GuzzleClient;
8
use GuzzleHttp\Exception\RequestException;
9
use GuzzleHttp\Promise;
10
use Psr\Http\Message\ResponseInterface;
11
12
/**
13
 * Concrete implentation of Adapter interface
14
 */
15
final class GuzzleAdapter implements AdapterInterface
16
{
17
    /**
18
     * Collection of Promise\PromiseInterface instances with keys matching what was given from start().
19
     *
20
     * @var array
21
     */
22
    private $promises = [];
23
24
    /**
25
     * Collection of Api\Response with keys matching what was given from start().
26
     *
27
     * @var array
28
     */
29
    private $responses = [];
30
31
    /**
32
     * Collection of \Exception with keys matching what was given from start().
33
     *
34
     * @var ArrayObject
35
     */
36
    private $exceptions;
37
38
    /**
39
     * @var \Guzzle\Http\Client
40
     */
41
    private $client;
42
43
    public function __construct(GuzzleClient $client = null)
44
    {
45
        $this->exceptions = new ArrayObject();
46
        $this->client = $client ?? new GuzzleClient(
0 ignored issues
show
Documentation Bug introduced by
It seems like $client ?? new \GuzzleHt...http_errors' => false)) of type object<GuzzleHttp\Client> is incompatible with the declared type object<Guzzle\Http\Client> of property $client.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
47
            [
48
                'allow_redirects' => false, //stop guzzle from following redirects
49
                'http_errors' => false, //only for 400/500 error codes, actual exceptions can still happen
50
            ]
51
        );
52
    }
53
54
    /**
55
     * @see AdapterInterface::start()
56
     */
57
    public function start(Request $request) : string
58
    {
59
        $handle = uniqid();
60
        $this->promises[$handle] = $this->client->requestAsync(
0 ignored issues
show
Bug introduced by
The method requestAsync() does not seem to exist on object<Guzzle\Http\Client>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
61
            $request->getMethod(),
62
            $request->getUrl(),
63
            [
64
                'headers' => $request->getHeaders(),
65
                'body' => $request->getBody(),
66
            ]
67
        );
68
69
        return $handle;
70
    }
71
72
    /**
73
     * @see Adapter::end()
74
     *
75
     * @throws \InvalidArgumentException
76
     */
77
    public function end(string $endHandle) : Response
78
    {
79
        $results = $this->fulfillPromises($this->promises, $this->exceptions);
80
        foreach ($results as $handle => $response) {
81
            try {
82
                $body = []; //default to empty body
83
                $contents = (string)$response->getBody();
84
                if (trim($contents) !== '') {
85
                    $body = json_decode($contents, true);
86
                    Util::ensure(
87
                        JSON_ERROR_NONE,
88
                        json_last_error(),
89
                        '\UnexpectedValueException',
90
                        [json_last_error_msg()]
91
                    );
92
                }
93
94
                $this->responses[$handle] = new Response($response->getStatusCode(), $response->getHeaders(), $body);
95
            } catch (\Exception $e) {
96
                $this->exceptions[$handle] = $e;
97
            }
98
        }
99
100
        $this->promises = [];
101
102
        if (array_key_exists($endHandle, $this->exceptions)) {
103
            $exception = $this->exceptions[$endHandle];
104
            unset($this->exceptions[$endHandle]);
105
            throw $exception;
106
        }
107
108
        if (array_key_exists($endHandle, $this->responses)) {
109
            $response = $this->responses[$endHandle];
110
            unset($this->responses[$endHandle]);
111
            return $response;
112
        }
113
114
        throw new \InvalidArgumentException('$endHandle not found');
115
    }
116
117
    /**
118
     * Helper method to execute all guzzle promises.
119
     *
120
     * @param array $promises
121
     * @param array $exceptions
122
     *
123
     * @return array Array of fulfilled PSR7 responses.
124
     */
125
    private function fulfillPromises(array $promises, ArrayObject $exceptions) : array
126
    {
127
        if (empty($promises)) {
128
            return [];
129
        }
130
131
        $results = new ArrayObject();
132
        Promise\each(
133
            $this->promises,
134
            function (ResponseInterface $response, $index) use ($results) {
135
                $results[$index] = $response;
136
            },
137
            function (RequestException $e, $index) use ($exceptions) {
138
                $exceptions[$index] = $e;
139
            }
140
        )->wait();
141
142
        return $results->getArrayCopy();
143
    }
144
}
145