1 | <?php |
||||||
2 | |||||||
3 | /* |
||||||
4 | * This file is part of the overtrue/wechat. |
||||||
5 | * |
||||||
6 | * (c) overtrue <[email protected]> |
||||||
7 | * |
||||||
8 | * This source file is subject to the MIT license that is bundled |
||||||
9 | * with this source code in the file LICENSE. |
||||||
10 | */ |
||||||
11 | |||||||
12 | namespace EasyWeChat\Kernel; |
||||||
13 | |||||||
14 | use EasyWeChat\Kernel\Contracts\AccessTokenInterface; |
||||||
15 | use EasyWeChat\Kernel\Http\Response; |
||||||
16 | use EasyWeChat\Kernel\Traits\HasHttpRequests; |
||||||
17 | use GuzzleHttp\MessageFormatter; |
||||||
18 | use GuzzleHttp\Middleware; |
||||||
19 | use Psr\Http\Message\RequestInterface; |
||||||
20 | use Psr\Http\Message\ResponseInterface; |
||||||
21 | use Psr\Log\LogLevel; |
||||||
22 | |||||||
23 | /** |
||||||
24 | * Class BaseClient. |
||||||
25 | * |
||||||
26 | * @author overtrue <[email protected]> |
||||||
27 | */ |
||||||
28 | class BaseClient |
||||||
29 | { |
||||||
30 | use HasHttpRequests { |
||||||
31 | request as performRequest; |
||||||
32 | } |
||||||
33 | |||||||
34 | /** |
||||||
35 | * @var \EasyWeChat\Kernel\ServiceContainer |
||||||
36 | */ |
||||||
37 | protected $app; |
||||||
38 | /** |
||||||
39 | * @var \EasyWeChat\Kernel\Contracts\AccessTokenInterface|null |
||||||
40 | */ |
||||||
41 | protected $accessToken = null; |
||||||
42 | /** |
||||||
43 | * @var string |
||||||
44 | */ |
||||||
45 | protected $baseUri; |
||||||
46 | |||||||
47 | /** |
||||||
48 | * BaseClient constructor. |
||||||
49 | * |
||||||
50 | * @param \EasyWeChat\Kernel\ServiceContainer $app |
||||||
51 | * @param \EasyWeChat\Kernel\Contracts\AccessTokenInterface|null $accessToken |
||||||
52 | */ |
||||||
53 | 592 | public function __construct(ServiceContainer $app, AccessTokenInterface $accessToken = null) |
|||||
54 | { |
||||||
55 | 592 | $this->app = $app; |
|||||
56 | 592 | $this->accessToken = $accessToken ?? $this->app['access_token']; |
|||||
57 | 592 | } |
|||||
58 | |||||||
59 | /** |
||||||
60 | * GET request. |
||||||
61 | * |
||||||
62 | * @param string $url |
||||||
63 | * @param array $query |
||||||
64 | * |
||||||
65 | * @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string |
||||||
66 | * |
||||||
67 | * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException |
||||||
68 | * @throws \GuzzleHttp\Exception\GuzzleException |
||||||
69 | */ |
||||||
70 | 1 | public function httpGet(string $url, array $query = []) |
|||||
71 | { |
||||||
72 | 1 | return $this->request($url, 'GET', ['query' => $query]); |
|||||
73 | } |
||||||
74 | |||||||
75 | /** |
||||||
76 | * POST request. |
||||||
77 | * |
||||||
78 | * @param string $url |
||||||
79 | * @param array $data |
||||||
80 | * |
||||||
81 | * @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string |
||||||
82 | * |
||||||
83 | * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException |
||||||
84 | * @throws \GuzzleHttp\Exception\GuzzleException |
||||||
85 | */ |
||||||
86 | 1 | public function httpPost(string $url, array $data = []) |
|||||
87 | { |
||||||
88 | 1 | return $this->request($url, 'POST', ['form_params' => $data]); |
|||||
89 | } |
||||||
90 | |||||||
91 | /** |
||||||
92 | * JSON request. |
||||||
93 | * |
||||||
94 | * @param string $url |
||||||
95 | * @param array $data |
||||||
96 | * @param array $query |
||||||
97 | * |
||||||
98 | * @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string |
||||||
99 | * |
||||||
100 | * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException |
||||||
101 | * @throws \GuzzleHttp\Exception\GuzzleException |
||||||
102 | */ |
||||||
103 | 1 | public function httpPostJson(string $url, array $data = [], array $query = []) |
|||||
104 | { |
||||||
105 | 1 | return $this->request($url, 'POST', ['query' => $query, 'json' => $data]); |
|||||
106 | } |
||||||
107 | |||||||
108 | /** |
||||||
109 | * Upload file. |
||||||
110 | * |
||||||
111 | * @param string $url |
||||||
112 | * @param array $files |
||||||
113 | * @param array $form |
||||||
114 | * @param array $query |
||||||
115 | * |
||||||
116 | * @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string |
||||||
117 | * |
||||||
118 | * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException |
||||||
119 | * @throws \GuzzleHttp\Exception\GuzzleException |
||||||
120 | */ |
||||||
121 | 1 | public function httpUpload(string $url, array $files = [], array $form = [], array $query = []) |
|||||
122 | { |
||||||
123 | 1 | $multipart = []; |
|||||
124 | |||||||
125 | 1 | foreach ($files as $name => $path) { |
|||||
126 | 1 | $multipart[] = [ |
|||||
127 | 1 | 'name' => $name, |
|||||
128 | 1 | 'contents' => fopen($path, 'r'), |
|||||
129 | ]; |
||||||
130 | } |
||||||
131 | |||||||
132 | 1 | foreach ($form as $name => $contents) { |
|||||
133 | 1 | $multipart[] = compact('name', 'contents'); |
|||||
134 | } |
||||||
135 | |||||||
136 | 1 | return $this->request( |
|||||
137 | 1 | $url, |
|||||
138 | 1 | 'POST', |
|||||
139 | 1 | ['query' => $query, 'multipart' => $multipart, 'connect_timeout' => 30, 'timeout' => 30, 'read_timeout' => 30] |
|||||
140 | ); |
||||||
141 | } |
||||||
142 | |||||||
143 | /** |
||||||
144 | * @return AccessTokenInterface |
||||||
145 | */ |
||||||
146 | 1 | public function getAccessToken(): AccessTokenInterface |
|||||
147 | { |
||||||
148 | 1 | return $this->accessToken; |
|||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||||
149 | } |
||||||
150 | |||||||
151 | /** |
||||||
152 | * @param \EasyWeChat\Kernel\Contracts\AccessTokenInterface $accessToken |
||||||
153 | * |
||||||
154 | * @return $this |
||||||
155 | */ |
||||||
156 | 1 | public function setAccessToken(AccessTokenInterface $accessToken) |
|||||
157 | { |
||||||
158 | 1 | $this->accessToken = $accessToken; |
|||||
159 | |||||||
160 | 1 | return $this; |
|||||
161 | } |
||||||
162 | |||||||
163 | /** |
||||||
164 | * @param string $url |
||||||
165 | * @param string $method |
||||||
166 | * @param array $options |
||||||
167 | * @param bool $returnRaw |
||||||
168 | * |
||||||
169 | * @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string |
||||||
170 | * |
||||||
171 | * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException |
||||||
172 | * @throws \GuzzleHttp\Exception\GuzzleException |
||||||
173 | */ |
||||||
174 | 1 | public function request(string $url, string $method = 'GET', array $options = [], $returnRaw = false) |
|||||
175 | { |
||||||
176 | 1 | if (empty($this->middlewares)) { |
|||||
177 | 1 | $this->registerHttpMiddlewares(); |
|||||
178 | } |
||||||
179 | |||||||
180 | 1 | $response = $this->performRequest($url, $method, $options); |
|||||
181 | |||||||
182 | 1 | $this->app->events->dispatch(new Events\HttpResponseCreated($response)); |
|||||
183 | |||||||
184 | 1 | return $returnRaw ? $response : $this->castResponseToType($response, $this->app->config->get('response_type')); |
|||||
185 | } |
||||||
186 | |||||||
187 | /** |
||||||
188 | * @param string $url |
||||||
189 | * @param string $method |
||||||
190 | * @param array $options |
||||||
191 | * |
||||||
192 | * @return \EasyWeChat\Kernel\Http\Response |
||||||
193 | * |
||||||
194 | * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException |
||||||
195 | * @throws \GuzzleHttp\Exception\GuzzleException |
||||||
196 | */ |
||||||
197 | 1 | public function requestRaw(string $url, string $method = 'GET', array $options = []) |
|||||
198 | { |
||||||
199 | 1 | return Response::buildFromPsrResponse($this->request($url, $method, $options, true)); |
|||||
200 | } |
||||||
201 | |||||||
202 | /** |
||||||
203 | * Register Guzzle middlewares. |
||||||
204 | */ |
||||||
205 | 1 | protected function registerHttpMiddlewares() |
|||||
206 | { |
||||||
207 | // retry |
||||||
208 | 1 | $this->pushMiddleware($this->retryMiddleware(), 'retry'); |
|||||
209 | // access token |
||||||
210 | 1 | $this->pushMiddleware($this->accessTokenMiddleware(), 'access_token'); |
|||||
211 | // log |
||||||
212 | 1 | $this->pushMiddleware($this->logMiddleware(), 'log'); |
|||||
213 | 1 | } |
|||||
214 | |||||||
215 | /** |
||||||
216 | * Attache access token to request query. |
||||||
217 | * |
||||||
218 | * @return \Closure |
||||||
219 | */ |
||||||
220 | 1 | protected function accessTokenMiddleware() |
|||||
221 | { |
||||||
222 | 1 | return function (callable $handler) { |
|||||
223 | 1 | return function (RequestInterface $request, array $options) use ($handler) { |
|||||
224 | 1 | if ($this->accessToken) { |
|||||
225 | 1 | $request = $this->accessToken->applyToRequest($request, $options); |
|||||
226 | } |
||||||
227 | |||||||
228 | 1 | return $handler($request, $options); |
|||||
229 | 1 | }; |
|||||
230 | 1 | }; |
|||||
231 | } |
||||||
232 | |||||||
233 | /** |
||||||
234 | * Log the request. |
||||||
235 | * |
||||||
236 | * @return \Closure |
||||||
237 | */ |
||||||
238 | 1 | protected function logMiddleware() |
|||||
239 | { |
||||||
240 | 1 | $formatter = new MessageFormatter($this->app['config']['http.log_template'] ?? MessageFormatter::DEBUG); |
|||||
241 | |||||||
242 | 1 | return Middleware::log($this->app['logger'], $formatter, LogLevel::DEBUG); |
|||||
243 | } |
||||||
244 | |||||||
245 | /** |
||||||
246 | * Return retry middleware. |
||||||
247 | * |
||||||
248 | * @return \Closure |
||||||
249 | */ |
||||||
250 | 1 | protected function retryMiddleware() |
|||||
251 | { |
||||||
252 | 1 | return Middleware::retry( |
|||||
253 | 1 | function ( |
|||||
254 | $retries, |
||||||
255 | RequestInterface $request, |
||||||
256 | ResponseInterface $response = null |
||||||
257 | ) { |
||||||
258 | // Limit the number of retries to 2 |
||||||
259 | 1 | if ($retries < $this->app->config->get('http.max_retries', 1) && $response && $body = $response->getBody()) { |
|||||
260 | // Retry on server errors |
||||||
261 | 1 | $response = json_decode($body, true); |
|||||
262 | |||||||
263 | 1 | if (!empty($response['errcode']) && in_array(abs($response['errcode']), [40001, 40014, 42001], true)) { |
|||||
264 | 1 | $this->accessToken->refresh(); |
|||||
0 ignored issues
–
show
The method
refresh() does not exist on null .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
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. ![]() |
|||||||
265 | 1 | $this->app['logger']->debug('Retrying with refreshed access token.'); |
|||||
266 | |||||||
267 | 1 | return true; |
|||||
268 | } |
||||||
269 | } |
||||||
270 | |||||||
271 | 1 | return false; |
|||||
272 | 1 | }, |
|||||
273 | 1 | function () { |
|||||
274 | 1 | return abs($this->app->config->get('http.retry_delay', 500)); |
|||||
275 | 1 | } |
|||||
276 | ); |
||||||
277 | } |
||||||
278 | } |
||||||
279 |