You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
7.2 KiB
243 lines
7.2 KiB
<?php
|
|
|
|
/*
|
|
* This file is part of the overtrue/wechat.
|
|
*
|
|
* (c) overtrue <i@overtrue.me>
|
|
*
|
|
* This source file is subject to the MIT license that is bundled
|
|
* with this source code in the file LICENSE.
|
|
*/
|
|
|
|
namespace EasyWeChat\Kernel;
|
|
|
|
use EasyWeChat\Kernel\Contracts\AccessTokenInterface;
|
|
use EasyWeChat\Kernel\Http\Response;
|
|
use EasyWeChat\Kernel\Traits\HasHttpRequests;
|
|
use GuzzleHttp\MessageFormatter;
|
|
use GuzzleHttp\Middleware;
|
|
use Psr\Http\Message\RequestInterface;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
use Psr\Log\LogLevel;
|
|
|
|
/**
|
|
* Class BaseClient.
|
|
*
|
|
* @author overtrue <i@overtrue.me>
|
|
*/
|
|
class BaseClient
|
|
{
|
|
use HasHttpRequests { request as performRequest; }
|
|
|
|
/**
|
|
* @var \EasyWeChat\Kernel\ServiceContainer
|
|
*/
|
|
protected $app;
|
|
|
|
/**
|
|
* @var \EasyWeChat\Kernel\Contracts\AccessTokenInterface
|
|
*/
|
|
protected $accessToken;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $baseUri;
|
|
|
|
/**
|
|
* BaseClient constructor.
|
|
*
|
|
* @param \EasyWeChat\Kernel\ServiceContainer $app
|
|
*/
|
|
public function __construct(ServiceContainer $app, AccessTokenInterface $accessToken = null)
|
|
{
|
|
$this->app = $app;
|
|
$this->accessToken = $accessToken ?? $this->app['access_token'];
|
|
}
|
|
|
|
/**
|
|
* GET request.
|
|
*
|
|
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
|
|
*
|
|
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
|
|
* @throws \GuzzleHttp\Exception\GuzzleException
|
|
*/
|
|
public function httpGet(string $url, array $query = [])
|
|
{
|
|
return $this->request($url, 'GET', ['query' => $query]);
|
|
}
|
|
|
|
/**
|
|
* POST request.
|
|
*
|
|
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
|
|
*
|
|
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
|
|
* @throws \GuzzleHttp\Exception\GuzzleException
|
|
*/
|
|
public function httpPost(string $url, array $data = [])
|
|
{
|
|
return $this->request($url, 'POST', ['form_params' => $data]);
|
|
}
|
|
|
|
/**
|
|
* JSON request.
|
|
*
|
|
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
|
|
*
|
|
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
|
|
* @throws \GuzzleHttp\Exception\GuzzleException
|
|
*/
|
|
public function httpPostJson(string $url, array $data = [], array $query = [])
|
|
{
|
|
return $this->request($url, 'POST', ['query' => $query, 'json' => $data]);
|
|
}
|
|
|
|
/**
|
|
* Upload file.
|
|
*
|
|
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
|
|
*
|
|
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
|
|
* @throws \GuzzleHttp\Exception\GuzzleException
|
|
*/
|
|
public function httpUpload(string $url, array $files = [], array $form = [], array $query = [])
|
|
{
|
|
$multipart = [];
|
|
|
|
foreach ($files as $name => $path) {
|
|
$multipart[] = [
|
|
'name' => $name,
|
|
'contents' => fopen($path, 'r'),
|
|
];
|
|
}
|
|
|
|
foreach ($form as $name => $contents) {
|
|
$multipart[] = compact('name', 'contents');
|
|
}
|
|
|
|
return $this->request($url, 'POST', ['query' => $query, 'multipart' => $multipart, 'connect_timeout' => 30, 'timeout' => 30, 'read_timeout' => 30]);
|
|
}
|
|
|
|
public function getAccessToken(): AccessTokenInterface
|
|
{
|
|
return $this->accessToken;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setAccessToken(AccessTokenInterface $accessToken)
|
|
{
|
|
$this->accessToken = $accessToken;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param bool $returnRaw
|
|
*
|
|
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
|
|
*
|
|
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
|
|
* @throws \GuzzleHttp\Exception\GuzzleException
|
|
*/
|
|
public function request(string $url, string $method = 'GET', array $options = [], $returnRaw = false)
|
|
{
|
|
if (empty($this->middlewares)) {
|
|
$this->registerHttpMiddlewares();
|
|
}
|
|
|
|
$response = $this->performRequest($url, $method, $options);
|
|
|
|
$this->app->events->dispatch(new Events\HttpResponseCreated($response));
|
|
|
|
return $returnRaw ? $response : $this->castResponseToType($response, $this->app->config->get('response_type'));
|
|
}
|
|
|
|
/**
|
|
* @return \EasyWeChat\Kernel\Http\Response
|
|
*
|
|
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
|
|
* @throws \GuzzleHttp\Exception\GuzzleException
|
|
*/
|
|
public function requestRaw(string $url, string $method = 'GET', array $options = [])
|
|
{
|
|
return Response::buildFromPsrResponse($this->request($url, $method, $options, true));
|
|
}
|
|
|
|
/**
|
|
* Register Guzzle middlewares.
|
|
*/
|
|
protected function registerHttpMiddlewares()
|
|
{
|
|
// retry
|
|
$this->pushMiddleware($this->retryMiddleware(), 'retry');
|
|
// access token
|
|
$this->pushMiddleware($this->accessTokenMiddleware(), 'access_token');
|
|
// log
|
|
$this->pushMiddleware($this->logMiddleware(), 'log');
|
|
}
|
|
|
|
/**
|
|
* Attache access token to request query.
|
|
*
|
|
* @return \Closure
|
|
*/
|
|
protected function accessTokenMiddleware()
|
|
{
|
|
return function (callable $handler) {
|
|
return function (RequestInterface $request, array $options) use ($handler) {
|
|
if ($this->accessToken) {
|
|
$request = $this->accessToken->applyToRequest($request, $options);
|
|
}
|
|
|
|
return $handler($request, $options);
|
|
};
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Log the request.
|
|
*
|
|
* @return \Closure
|
|
*/
|
|
protected function logMiddleware()
|
|
{
|
|
$formatter = new MessageFormatter($this->app['config']['http.log_template'] ?? MessageFormatter::DEBUG);
|
|
|
|
return Middleware::log($this->app['logger'], $formatter, LogLevel::DEBUG);
|
|
}
|
|
|
|
/**
|
|
* Return retry middleware.
|
|
*
|
|
* @return \Closure
|
|
*/
|
|
protected function retryMiddleware()
|
|
{
|
|
return Middleware::retry(function (
|
|
$retries,
|
|
RequestInterface $request,
|
|
ResponseInterface $response = null
|
|
) {
|
|
// Limit the number of retries to 2
|
|
if ($retries < $this->app->config->get('http.max_retries', 1) && $response && $body = $response->getBody()) {
|
|
// Retry on server errors
|
|
$response = json_decode($body, true);
|
|
|
|
if (!empty($response['errcode']) && in_array(abs($response['errcode']), [40001, 40014, 42001], true)) {
|
|
$this->accessToken->refresh();
|
|
$this->app['logger']->debug('Retrying with refreshed access token.');
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}, function () {
|
|
return abs($this->app->config->get('http.retry_delay', 500));
|
|
});
|
|
}
|
|
}
|
|
|