HttpApi   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 7

Test Coverage

Coverage 75.68%

Importance

Changes 0
Metric Value
wmc 17
lcom 2
cbo 7
dl 0
loc 170
ccs 28
cts 37
cp 0.7568
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A httpGet() 0 10 2
A httpPost() 0 4 1
A httpPostRaw() 0 6 1
A httpPut() 0 6 1
A httpDelete() 0 6 1
A createJsonBody() 0 4 3
A handleErrors() 0 14 3
A handleResponse() 0 12 3
1
<?php
2
3
declare(strict_types=1);
4
/*
5
 * This software may be modified and distributed under the terms
6
 * of the MIT license. See the LICENSE file for details.
7
 */
8
9
namespace Billogram\Api;
10
11
use Billogram\Exception\Domain as DomainException;
12
use Billogram\Hydrator\NoopHydrator;
13
use Http\Client\HttpClient;
14
use Billogram\Hydrator\Hydrator;
15
use Billogram\RequestBuilder;
16
use Psr\Http\Message\ResponseInterface;
17
18
/**
19
 * @author Tobias Nyholm <[email protected]>
20
 */
21
abstract class HttpApi
22
{
23
    /**
24
     * @var HttpClient
25
     */
26
    protected $httpClient;
27
28
    /**
29
     * @var Hydrator
30
     */
31
    protected $hydrator;
32
33
    /**
34
     * @var RequestBuilder
35
     */
36
    protected $requestBuilder;
37
38
    /**
39
     * @param HttpClient     $httpClient
40
     * @param RequestBuilder $requestBuilder
41
     * @param Hydrator       $hydrator
42
     */
43 19
    public function __construct(HttpClient $httpClient, Hydrator $hydrator, RequestBuilder $requestBuilder)
44
    {
45 19
        $this->httpClient = $httpClient;
46 19
        $this->requestBuilder = $requestBuilder;
47 19
        if (!$hydrator instanceof NoopHydrator) {
48 19
            $this->hydrator = $hydrator;
49
        }
50 19
    }
51
52
    /**
53
     * Send a GET request with query parameters.
54
     *
55
     * @param string $path           Request path
56
     * @param array  $params         GET parameters
57
     * @param array  $requestHeaders Request Headers
58
     *
59
     * @return ResponseInterface
60
     */
61 11
    protected function httpGet(string $path, array $params = [], array $requestHeaders = []): ResponseInterface
62
    {
63 11
        if (count($params) > 0) {
64 4
            $path .= '?'.http_build_query($params);
65
        }
66
67 11
        return $this->httpClient->sendRequest(
68 11
            $this->requestBuilder->create('GET', $path, $requestHeaders)
69
        );
70
    }
71
72
    /**
73
     * Send a POST request with JSON-encoded parameters.
74
     *
75
     * @param string $path           Request path
76
     * @param array  $params         POST parameters to be JSON encoded
77
     * @param array  $requestHeaders Request headers
78
     *
79
     * @return ResponseInterface
80
     */
81 4
    protected function httpPost(string $path, array $params = [], array $requestHeaders = []): ResponseInterface
82
    {
83 4
        return $this->httpPostRaw($path, $this->createJsonBody($params), $requestHeaders);
84
    }
85
86
    /**
87
     * Send a POST request with raw data.
88
     *
89
     * @param string       $path           Request path
90
     * @param array|string $body           Request body
91
     * @param array        $requestHeaders Request headers
92
     *
93
     * @return ResponseInterface
94
     */
95 4
    protected function httpPostRaw(string $path, $body, array $requestHeaders = []): ResponseInterface
96
    {
97 4
        return $response = $this->httpClient->sendRequest(
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
98 4
            $this->requestBuilder->create('POST', $path, $requestHeaders, $body)
99
        );
100
    }
101
102
    /**
103
     * Send a PUT request with JSON-encoded parameters.
104
     *
105
     * @param string $path           Request path
106
     * @param array  $params         POST parameters to be JSON encoded
107
     * @param array  $requestHeaders Request headers
108
     *
109
     * @return ResponseInterface
110
     */
111 4
    protected function httpPut(string $path, array $params = [], array $requestHeaders = []): ResponseInterface
112
    {
113 4
        return $this->httpClient->sendRequest(
114 4
            $this->requestBuilder->create('PUT', $path, $requestHeaders, $this->createJsonBody($params))
115
        );
116
    }
117
118
    /**
119
     * Send a DELETE request with JSON-encoded parameters.
120
     *
121
     * @param string $path           Request path
122
     * @param array  $params         POST parameters to be JSON encoded
123
     * @param array  $requestHeaders Request headers
124
     *
125
     * @return ResponseInterface
126
     */
127 1
    protected function httpDelete(string $path, array $params = [], array $requestHeaders = []): ResponseInterface
128
    {
129 1
        return $this->httpClient->sendRequest(
130 1
            $this->requestBuilder->create('DELETE', $path, $requestHeaders, $this->createJsonBody($params))
131
        );
132
    }
133
134
    /**
135
     * Create a JSON encoded version of an array of parameters.
136
     *
137
     * @param array $params Request parameters
138
     *
139
     * @return null|string
140
     */
141 9
    private function createJsonBody(array $params)
142
    {
143 9
        return (0 === count($params)) ? null : json_encode($params, empty($params) ? JSON_FORCE_OBJECT : 0);
144
    }
145
146
    /**
147
     * Handle HTTP errors.
148
     *
149
     * Call is controlled by the specific API methods.
150
     *
151
     * @param ResponseInterface $response
152
     *
153
     * @throws \Billogram\Exception\DomainException
154
     */
155
    protected function handleErrors(ResponseInterface $response)
156
    {
157
        switch ($response->getStatusCode()) {
158
            case 404:
159
                throw new DomainException\NotFoundException($response->getBody()->__toString());
160
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
161
            case 400:
162
                throw new DomainException\ValidationException($response->getBody()->__toString());
163
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
164
            default:
165
                throw new DomainException\UnknownErrorException($response->getBody()->__toString());
166
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
167
        }
168
    }
169
170
    /**
171
     * @param ResponseInterface $response
172
     * @param string            $class    to hydrate
173
     *
174
     * @return mixed
175
     *
176
     * @throws \Billogram\Exception
177
     */
178 19
    protected function handleResponse(ResponseInterface $response, $class)
179
    {
180 19
        if (!$this->hydrator) {
181
            return $response;
182
        }
183
184 19
        if (200 !== $response->getStatusCode()) {
185
            $this->handleErrors($response);
186
        }
187
188 19
        return $this->hydrator->hydrate($response, $class);
189
    }
190
}
191