Completed
Push — master ( f2b799...761b51 )
by Tobias
04:02
created

HttpApi   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 168
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 168
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
     * @var Hydrator
29
     */
30
    protected $hydrator;
31
    /**
32
     * @var RequestBuilder
33
     */
34
    protected $requestBuilder;
35
36
    /**
37
     * @param HttpClient     $httpClient
38
     * @param RequestBuilder $requestBuilder
39
     * @param Hydrator       $hydrator
40
     */
41 19
    public function __construct(HttpClient $httpClient, Hydrator $hydrator, RequestBuilder $requestBuilder)
42
    {
43 19
        $this->httpClient = $httpClient;
44 19
        $this->requestBuilder = $requestBuilder;
45 19
        if (!$hydrator instanceof NoopHydrator) {
46 19
            $this->hydrator = $hydrator;
47
        }
48 19
    }
49
50
    /**
51
     * Send a GET request with query parameters.
52
     *
53
     * @param string $path           Request path
54
     * @param array  $params         GET parameters
55
     * @param array  $requestHeaders Request Headers
56
     *
57
     * @return ResponseInterface
58
     */
59 11
    protected function httpGet(string $path, array $params = [], array $requestHeaders = []): ResponseInterface
60
    {
61 11
        if (count($params) > 0) {
62 4
            $path .= '?'.http_build_query($params);
63
        }
64
65 11
        return $this->httpClient->sendRequest(
66 11
            $this->requestBuilder->create('GET', $path, $requestHeaders)
67
        );
68
    }
69
70
    /**
71
     * Send a POST request with JSON-encoded parameters.
72
     *
73
     * @param string $path           Request path
74
     * @param array  $params         POST parameters to be JSON encoded
75
     * @param array  $requestHeaders Request headers
76
     *
77
     * @return ResponseInterface
78
     */
79 4
    protected function httpPost(string $path, array $params = [], array $requestHeaders = []): ResponseInterface
80
    {
81 4
        return $this->httpPostRaw($path, $this->createJsonBody($params), $requestHeaders);
82
    }
83
84
    /**
85
     * Send a POST request with raw data.
86
     *
87
     * @param string       $path           Request path
88
     * @param array|string $body           Request body
89
     * @param array        $requestHeaders Request headers
90
     *
91
     * @return ResponseInterface
92
     */
93 4
    protected function httpPostRaw(string $path, $body, array $requestHeaders = []): ResponseInterface
94
    {
95 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...
96 4
            $this->requestBuilder->create('POST', $path, $requestHeaders, $body)
97
        );
98
    }
99
100
    /**
101
     * Send a PUT request with JSON-encoded parameters.
102
     *
103
     * @param string $path           Request path
104
     * @param array  $params         POST parameters to be JSON encoded
105
     * @param array  $requestHeaders Request headers
106
     *
107
     * @return ResponseInterface
108
     */
109 4
    protected function httpPut(string $path, array $params = [], array $requestHeaders = []): ResponseInterface
110
    {
111 4
        return $this->httpClient->sendRequest(
112 4
            $this->requestBuilder->create('PUT', $path, $requestHeaders, $this->createJsonBody($params))
113
        );
114
    }
115
116
    /**
117
     * Send a DELETE request with JSON-encoded parameters.
118
     *
119
     * @param string $path           Request path
120
     * @param array  $params         POST parameters to be JSON encoded
121
     * @param array  $requestHeaders Request headers
122
     *
123
     * @return ResponseInterface
124
     */
125 1
    protected function httpDelete(string $path, array $params = [], array $requestHeaders = []): ResponseInterface
126
    {
127 1
        return $this->httpClient->sendRequest(
128 1
            $this->requestBuilder->create('DELETE', $path, $requestHeaders, $this->createJsonBody($params))
129
        );
130
    }
131
132
    /**
133
     * Create a JSON encoded version of an array of parameters.
134
     *
135
     * @param array $params Request parameters
136
     *
137
     * @return null|string
138
     */
139 9
    private function createJsonBody(array $params)
140
    {
141 9
        return (count($params) === 0) ? null : json_encode($params, empty($params) ? JSON_FORCE_OBJECT : 0);
142
    }
143
144
    /**
145
     * Handle HTTP errors.
146
     *
147
     * Call is controlled by the specific API methods.
148
     *
149
     * @param ResponseInterface $response
150
     *
151
     * @throws \Billogram\Exception\DomainException
152
     */
153
    protected function handleErrors(ResponseInterface $response)
154
    {
155
        switch ($response->getStatusCode()) {
156
            case 404:
157
                throw new DomainException\NotFoundException($response->getBody()->__toString());
158
                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...
159
            case 400:
160
                throw new DomainException\ValidationException($response->getBody()->__toString());
161
                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...
162
            default:
163
                throw new DomainException\UnknownErrorException($response->getBody()->__toString());
164
                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...
165
        }
166
    }
167
168
    /**
169
     * @param ResponseInterface $response
170
     * @param string            $class    to hydrate
171
     *
172
     * @return mixed
173
     *
174
     * @throws \Billogram\Exception
175
     */
176 19
    protected function handleResponse(ResponseInterface $response, $class)
177
    {
178 19
        if (!$this->hydrator) {
179
            return $response;
180
        }
181
182 19
        if ($response->getStatusCode() !== 200) {
183
            $this->handleErrors($response);
184
        }
185
186 19
        return $this->hydrator->hydrate($response, $class);
187
    }
188
}
189