1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace IproSoftwareApi; |
4
|
|
|
|
5
|
|
|
use BadMethodCallException; |
6
|
|
|
use GuzzleHttp\ClientInterface; |
7
|
|
|
use GuzzleHttp\Exception\GuzzleException; |
8
|
|
|
use IproSoftwareApi\Contracts\AccessToken as AccessTokenInterface; |
9
|
|
|
use IproSoftwareApi\Contracts\AccessTokenCacher; |
10
|
|
|
use IproSoftwareApi\DTOs\ClientCredentials; |
11
|
|
|
use IproSoftwareApi\Exceptions\IproSoftwareApiAccessTokenException; |
12
|
|
|
use IproSoftwareApi\Exceptions\IproSoftwareApiException; |
13
|
|
|
use Psr\Http\Message\ResponseInterface; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Class HttpClient. |
17
|
|
|
* |
18
|
|
|
* @method get($path = '', array $options = []) |
19
|
|
|
* @method post($path = '', array $options = []) |
20
|
|
|
* @method put($path = '', array $options = []) |
21
|
|
|
* @method delete($path = '', array $options = []) |
22
|
|
|
* @method head($path = '', array $options = []) |
23
|
|
|
* @method patch($path = '', array $options = []) |
24
|
|
|
*/ |
25
|
|
|
class HttpClient implements Contracts\HttpClient |
26
|
|
|
{ |
27
|
|
|
/** |
28
|
|
|
* HTTP Methods. |
29
|
|
|
*/ |
30
|
|
|
const HTTP_METHOD_GET = 'GET'; |
31
|
|
|
const HTTP_METHOD_POST = 'POST'; |
32
|
|
|
const HTTP_METHOD_PUT = 'PUT'; |
33
|
|
|
const HTTP_METHOD_DELETE = 'DELETE'; |
34
|
|
|
const HTTP_METHOD_HEAD = 'HEAD'; |
35
|
|
|
const HTTP_METHOD_PATCH = 'PATCH'; |
36
|
|
|
|
37
|
|
|
const HTTP_METHODS = [ |
38
|
|
|
self::HTTP_METHOD_GET, |
39
|
|
|
self::HTTP_METHOD_POST, |
40
|
|
|
self::HTTP_METHOD_PUT, |
41
|
|
|
self::HTTP_METHOD_DELETE, |
42
|
|
|
self::HTTP_METHOD_HEAD, |
43
|
|
|
self::HTTP_METHOD_PATCH, |
44
|
|
|
]; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var string |
48
|
|
|
*/ |
49
|
|
|
protected $accessTokenClass; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var AccessTokenCacher |
53
|
|
|
*/ |
54
|
|
|
protected $cacheManager; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var null|callable |
58
|
|
|
*/ |
59
|
|
|
protected $responseFilter; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var ClientCredentials |
63
|
|
|
*/ |
64
|
|
|
protected $clientCredentials; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var ClientInterface |
68
|
|
|
*/ |
69
|
|
|
protected $http; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @var AccessTokenInterface |
73
|
|
|
*/ |
74
|
|
|
protected $accessToken; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* HttpClient constructor. |
78
|
|
|
* |
79
|
|
|
* @param AccessTokenCacher $cacheManager |
80
|
|
|
* @param ClientCredentials $clientCredentials |
81
|
|
|
* @param array $httpConfiguration |
82
|
|
|
*/ |
83
|
11 |
|
public function __construct(ClientCredentials $clientCredentials, AccessTokenCacher $cacheManager, array $httpConfiguration = []) |
84
|
|
|
{ |
85
|
11 |
|
$this->cacheManager = $cacheManager; |
86
|
11 |
|
$this->clientCredentials = $clientCredentials; |
87
|
|
|
|
88
|
11 |
|
$this->accessTokenClass = $httpConfiguration['access_token_class'] |
89
|
11 |
|
?? \IproSoftwareApi\AccessToken\AccessToken::class; |
90
|
|
|
|
91
|
11 |
|
$configs = $httpConfiguration['client_conf'] ?? []; |
92
|
11 |
|
if (!isset($configs['base_uri'])) { |
93
|
11 |
|
$configs['base_uri'] = $this->clientCredentials->apiHost; |
94
|
|
|
} |
95
|
11 |
|
$this->http = new \GuzzleHttp\Client($configs); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @param $method |
100
|
|
|
* @param $arguments |
101
|
|
|
* |
102
|
|
|
* @return mixed|ResponseInterface |
103
|
|
|
* @throws GuzzleException |
104
|
|
|
* |
105
|
|
|
* @throws IproSoftwareApiAccessTokenException |
106
|
|
|
*/ |
107
|
1 |
|
public function __call($method, $arguments) |
108
|
|
|
{ |
109
|
1 |
|
if (in_array(strtoupper($method), self::HTTP_METHODS)) { |
110
|
1 |
|
return $this->request(strtoupper($method), $arguments[0], $arguments[1] ?? []); |
111
|
|
|
} |
112
|
|
|
|
113
|
1 |
|
throw new BadMethodCallException('Method ' . $method . ' not found on ' . get_class() . '.', 500); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @param AccessTokenCacher $cacheManager |
118
|
|
|
* |
119
|
|
|
* @return static |
120
|
|
|
*/ |
121
|
3 |
|
public function setCacheManager(AccessTokenCacher $cacheManager): static |
122
|
|
|
{ |
123
|
3 |
|
$this->cacheManager = $cacheManager; |
124
|
|
|
|
125
|
3 |
|
return $this; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* @param callable|null $responseFilter |
130
|
|
|
* |
131
|
|
|
* @return static |
132
|
|
|
*/ |
133
|
1 |
|
public function setResponseFilter(?callable $responseFilter): static |
134
|
|
|
{ |
135
|
1 |
|
$this->responseFilter = $responseFilter; |
136
|
|
|
|
137
|
1 |
|
return $this; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* @param ClientInterface $http |
142
|
|
|
* |
143
|
|
|
* @return static |
144
|
|
|
*/ |
145
|
5 |
|
public function setHttp(ClientInterface $http): static |
146
|
|
|
{ |
147
|
5 |
|
$this->http = $http; |
148
|
|
|
|
149
|
5 |
|
return $this; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* @param null $option |
|
|
|
|
154
|
|
|
* |
155
|
|
|
* @return mixed |
156
|
|
|
* @throws IproSoftwareApiException |
157
|
|
|
* |
158
|
|
|
*/ |
159
|
2 |
|
public function getConfig($option = null) |
160
|
|
|
{ |
161
|
2 |
|
if (!is_null($this->http)) { |
162
|
2 |
|
return $this->http->getConfig($option); |
|
|
|
|
163
|
|
|
} |
164
|
|
|
|
165
|
1 |
|
throw new IproSoftwareApiException('Http client not specified'); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* @param $method |
170
|
|
|
* @param string $path |
171
|
|
|
* @param array $options |
172
|
|
|
* |
173
|
|
|
* @return mixed|ResponseInterface |
174
|
|
|
* @throws IproSoftwareApiAccessTokenException |
175
|
|
|
* |
176
|
|
|
* @throws GuzzleException |
177
|
|
|
*/ |
178
|
3 |
|
public function request($method, $path = '', array $options = []): ResponseInterface |
179
|
|
|
{ |
180
|
3 |
|
if (!$this->hasAccessToken()) { |
181
|
1 |
|
$this->generateAccessToken(); |
182
|
|
|
} |
183
|
|
|
|
184
|
3 |
|
if (!isset($options['headers']['Authorization'])) { |
185
|
3 |
|
$options['headers']['Authorization'] = $this->accessToken->getAuthorizationHeader(); |
|
|
|
|
186
|
|
|
} |
187
|
|
|
|
188
|
3 |
|
if (is_string($path) && !empty($path)) { |
189
|
3 |
|
if ($path[0] == '/') { |
190
|
1 |
|
$path = substr($path, 1); |
191
|
|
|
} |
192
|
|
|
} |
193
|
|
|
|
194
|
3 |
|
$response = $this->http->request($method, $path, $options); |
195
|
|
|
|
196
|
3 |
|
if (is_callable($this->responseFilter)) { |
197
|
1 |
|
$response = ($this->responseFilter)($response, $options, $path, $method); |
198
|
|
|
} |
199
|
|
|
|
200
|
3 |
|
return $response; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* @return bool |
205
|
|
|
*/ |
206
|
7 |
|
public function hasAccessToken(): bool |
207
|
|
|
{ |
208
|
7 |
|
return $this->accessToken instanceof AccessTokenInterface && $this->accessToken->hasAccessToken(); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* @return AccessTokenInterface |
213
|
|
|
* @throws IproSoftwareApiAccessTokenException |
214
|
|
|
* |
215
|
|
|
*/ |
216
|
4 |
|
public function generateAccessToken(): AccessTokenInterface |
217
|
|
|
{ |
218
|
4 |
|
$this->accessToken = $this->cacheManager->get(); |
219
|
|
|
|
220
|
|
|
// If empty access token or expired then make request for new token |
221
|
4 |
|
if (!$this->hasAccessToken()) { |
222
|
2 |
|
$this->receiveAccessToken(); |
223
|
|
|
} |
224
|
|
|
|
225
|
3 |
|
return $this->accessToken; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* @throws IproSoftwareApiAccessTokenException |
230
|
|
|
*/ |
231
|
2 |
|
protected function receiveAccessToken() |
232
|
|
|
{ |
233
|
2 |
|
$response = $this->http->post($this->clientCredentials->tokenEndpoint, [ |
234
|
2 |
|
'auth' => [ |
235
|
2 |
|
$this->clientCredentials->clientId, |
236
|
2 |
|
$this->clientCredentials->clientSecret, |
237
|
2 |
|
], |
238
|
2 |
|
'form_params' => [ |
239
|
2 |
|
'grant_type' => 'client_credentials', |
240
|
2 |
|
], |
241
|
2 |
|
]); |
242
|
|
|
|
243
|
2 |
|
if ($response->getStatusCode() != 200) { |
244
|
1 |
|
throw new IproSoftwareApiAccessTokenException($response, 'Get Access Token Error'); |
245
|
|
|
} |
246
|
|
|
|
247
|
1 |
|
$this->accessToken = call_user_func( |
248
|
1 |
|
[$this->accessTokenClass, 'makeFromApiResponse'], |
249
|
1 |
|
$response |
250
|
1 |
|
); |
251
|
|
|
|
252
|
1 |
|
$this->cacheManager->put($this->accessToken); |
253
|
|
|
} |
254
|
|
|
} |
255
|
|
|
|