HttpClient   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 95.92%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 10
dl 0
loc 165
ccs 47
cts 49
cp 0.9592
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 3
A isReturnBadResponse() 0 4 1
A request() 0 20 3
A handleRequest() 0 11 2
A handleResponse() 0 16 2
A streamToDocument() 0 11 2
A documentToStream() 0 15 2
1
<?php
2
declare(strict_types = 1);
3
4
namespace Mikemirten\Component\JsonApi\HttpClient;
5
6
use GuzzleHttp\Psr7\Stream;
7
use Mikemirten\Component\JsonApi\Document\AbstractDocument;
8
use Mikemirten\Component\JsonApi\HttpClient\Exception\HttpClientException;
9
use Mikemirten\Component\JsonApi\HttpClient\Exception\InvalidOptionException;
10
use Mikemirten\Component\JsonApi\HttpClient\Exception\ResponseException;
11
use Mikemirten\Component\JsonApi\Hydrator\DocumentHydrator;
12
use Psr\Http\Message\RequestInterface;
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\StreamInterface;
15
16
/**
17
 * HttpClient
18
 * Supports JsonApi requests & responses
19
 *
20
 * @package Mikemirten\Component\JsonApi\HttpClient
21
 */
22
class HttpClient implements HttpClientInterface
23
{
24
    const CONTENT_TYPE_JSON_API = 'application/vnd.api+json';
25
26
    /**
27
     * Options possible to configure
28
     *
29
     * @var array
30
     */
31
    static protected $possibleOptions = ['returnBadResponse'];
32
33
    /**
34
     * @var HttpClientInterface
35
     */
36
    protected $client;
37
38
    /**
39
     * @var DocumentHydrator
40
     */
41
    protected $hydrator;
42
43
    /**
44
     * Return bad response instead of throwing an exception
45
     *
46
     * @var bool
47
     */
48
    protected $returnBadResponse = false;
49
50
    /**
51
     * HttpClient constructor.
52
     *
53
     * @param HttpClientInterface $client
54
     * @param DocumentHydrator    $hydrator
55
     * @param array               $options
56
     */
57 7
    public function __construct(HttpClientInterface $client, DocumentHydrator $hydrator, array $options = [])
58
    {
59 7
        $this->client   = $client;
60 7
        $this->hydrator = $hydrator;
61
62 7
        foreach ($options as $option => $value)
63
        {
64 3
            if (! in_array($option, static::$possibleOptions, true)) {
65 1
                throw new InvalidOptionException($option, static::$possibleOptions);
66
            }
67
68 2
            $this->$option = $value;
69
        }
70 6
    }
71
72
    /**
73
     * Is the client returns bad response instead of throwing an exception ?
74
     *
75
     * @return bool
76
     */
77 1
    public function isReturnBadResponse(): bool
78
    {
79 1
        return $this->returnBadResponse;
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 5
    public function request(RequestInterface $request): ResponseInterface
86
    {
87 5
        $request = $this->handleRequest($request);
88
89
        try {
90 5
            $response = $this->client->request($request);
91
        }
92 2
        catch (ResponseException $exception) {
93 2
            $responseRaw = $exception->getResponse();
94 2
            $response    = $this->handleResponse($responseRaw);
95
96 2
            if ($this->returnBadResponse) {
97 1
                return $response;
98
            }
99
100 1
            throw new ResponseException($request, $response, $exception);
101
        }
102
103 3
        return $this->handleResponse($response);
104
    }
105
106
    /**
107
     * Handle request
108
     *
109
     * @param  RequestInterface $request
110
     * @return RequestInterface
111
     */
112 5
    protected function handleRequest(RequestInterface $request): RequestInterface
113
    {
114 5
        if ($request instanceof JsonApiRequest) {
115 1
            $document = $request->getDocument();
116 1
            $stream   = $this->documentToStream($document);
117
118 1
            return $request->withBody($stream);
119
        }
120
121 4
        return $request;
122
    }
123
124
    /**
125
     * Handle response
126
     *
127
     * @param  ResponseInterface $response
128
     * @return ResponseInterface
129
     */
130 5
    protected function handleResponse(ResponseInterface $response): ResponseInterface
131
    {
132 5
        $contentType = $response->getHeader('Content-Type');
133
134 5
        if (in_array(self::CONTENT_TYPE_JSON_API, $contentType, true)) {
135 1
            $stream = $response->getBody();
136
137 1
            return new JsonApiResponse(
138 1
                $response->getStatusCode(),
139 1
                $response->getHeaders(),
140 1
                $this->streamToDocument($stream)
141
            );
142
        }
143
144 4
        return $response;
145
    }
146
147
    /**
148
     * Create a JsonApi document by serialized data from stream
149
     *
150
     * @param  StreamInterface $stream
151
     * @return AbstractDocument
152
     */
153 1
    protected function streamToDocument(StreamInterface $stream): AbstractDocument
154
    {
155 1
        $content = $stream->getContents();
156 1
        $decoded = json_decode($content);
157
158 1
        if (json_last_error() !== JSON_ERROR_NONE) {
159
            throw new \RuntimeException('Decoding error: "' . json_last_error_msg() . '""');
160
        }
161
162 1
        return $this->hydrator->hydrate($decoded);
163
    }
164
165
    /**
166
     * Create a stream contains serialized JsonApi-document
167
     *
168
     * @param  AbstractDocument $document
169
     * @return StreamInterface
170
     */
171 1
    protected function documentToStream(AbstractDocument $document): StreamInterface
172
    {
173 1
        $encoded = json_encode($document->toArray());
174
175 1
        if (json_last_error() !== JSON_ERROR_NONE) {
176
            throw new \RuntimeException('Encoding error: "' . json_last_error_msg() . '""');
177
        }
178
179 1
        $stream = fopen('php://memory', 'r+');
180
181 1
        fwrite($stream, $encoded);
182 1
        fseek($stream, 0);
183
184 1
        return new Stream($stream);
185
    }
186
}