123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- <?php
- namespace App\Libs\TikTok\Kernel;
- use App\Libs\TikTok\Kernel\Contracts\AccessTokenInterface;
- use App\Libs\TikTok\Kernel\Exceptions\BadRequestException;
- use App\Libs\TikTok\Kernel\Exceptions\InvalidArgumentException;
- use App\Libs\TikTok\Kernel\Http\Response;
- use App\Libs\TikTok\Kernel\Support\Collection;
- use App\Libs\TikTok\Kernel\Traits\HasHttpRequests;
- use GuzzleHttp\Exception\GuzzleException;
- use GuzzleHttp\Middleware;
- use GuzzleHttp\Psr7\Utils;
- use Psr\Http\Message\RequestInterface;
- use Psr\Http\Message\ResponseInterface;
- use Closure;
- /**
- * Class BaseClient.
- *
- * @author overtrue <i@overtrue.me>
- */
- class BaseClient {
- use HasHttpRequests {
- request as performRequest;
- }
- /**
- * @var ServiceContainer
- */
- protected $app;
- /**
- * @var string
- */
- protected $baseUri;
- /**
- * @var bool 是否需要传递AccessToken
- */
- protected $needAccessToken = true;
- /**
- * @var bool
- */
- protected $postAccessToken = true;
- /**
- * @var bool
- */
- protected $headerAccessToken = false;
- /**
- * @var AccessTokenInterface
- */
- private $accessToken;
- /**
- * BaseClient constructor.
- * @param ServiceContainer $app
- * @param null $accessToken
- */
- public function __construct(ServiceContainer $app, $accessToken = null) {
- $this->app = $app;
- $this->accessToken = $accessToken ?? $this->app['access_token'];
- }
- /**
- * @param string $url
- * @param array $query
- * @return array|Collection|object|ResponseInterface|string
- * @throws Exceptions\HttpException
- * @throws Exceptions\InvalidConfigException
- * @throws GuzzleException
- */
- public function httpGet(string $url, array $query = []) {
- return $this->request($url, 'GET', ['query' => $query]);
- }
- /**
- * @param string $url
- * @param array $data
- * @return array|Collection|object|ResponseInterface|string
- * @throws Exceptions\HttpException
- * @throws Exceptions\InvalidConfigException
- * @throws GuzzleException
- */
- public function httpPost(string $url, array $data = []) {
- return $this->request($url, 'POST', ['form_params' => $data]);
- }
- /**
- * @param string $url
- * @param array $data
- * @return array|Collection|object|ResponseInterface|string
- * @throws Exceptions\HttpException
- * @throws Exceptions\InvalidConfigException
- * @throws GuzzleException
- */
- public function httpPostFormData(string $url, array $data = []) {
- $multipartData = [];
- foreach ($data as $key => $value) {
- $multipartData[] = [
- 'name' => $key,
- 'contents' => $value
- ];
- }
- return $this->request($url, 'POST', ['multipart' => $multipartData]);
- }
- /**
- * @param string $url
- * @param array $data
- * @param array $query
- * @return array
- * @throws Exceptions\HttpException
- * @throws Exceptions\InvalidConfigException
- * @throws GuzzleException
- */
- public function httpPostJson(string $url, array $data = [], array $query = []): array {
- return $this->request($url, 'POST', ['query' => $query, 'json' => $data]);
- }
- /**
- * 分片上传文件
- * @param string $url
- * @param string $file
- * @param array $query
- * @param int|float $chunkSize 每个分片的大小,不建议修改
- * @return array
- * @throws Exceptions\HttpException
- * @throws Exceptions\InvalidConfigException
- * @throws GuzzleException|BadRequestException|InvalidArgumentException
- * @author zhaoxiang <zhaoxiang051405@gmail.com>
- */
- public function httpChunkUpload(string $url, string $file, array $query = [], int $chunkSize = 1024 * 1024 * 10): array {
- $result = [];
- $fh = Utils::tryFopen($file, 'rb');
- $filesize = filesize($file);
- if ($filesize <= 1024 * 1024 * 5) {
- throw new InvalidArgumentException('The file size cannot be less than 5M');
- }
- $chunkNum = (int)round($filesize / $chunkSize);
- rewind($fh);
- $chunkIndex = 1;
- $tempPart = md5($query['upload_id']) . '.part';
- while ($chunkIndex <= $chunkNum) {
- $left = $filesize - ($chunkIndex - 1) * $chunkSize;
- $tempPartFile = dirname($file) . DIRECTORY_SEPARATOR . $tempPart . $chunkIndex;
- if (!is_writable($tempPartFile)) {
- throw new InvalidArgumentException("{$tempPartFile} can not be write");
- }
- file_put_contents($tempPartFile, $chunkIndex === $chunkNum ? fread($fh, $left) : fread($fh, $chunkSize));
- $multipart = [
- [
- 'name' => 'video',
- 'contents' => fopen($tempPartFile, 'rb')
- ]
- ];
- $query['part_number'] = $chunkIndex;
- $response = $this->request(
- $url,
- 'POST',
- ['query' => $query, 'multipart' => $multipart, 'connect_timeout' => 300, 'timeout' => 300, 'read_timeout' => 300]
- );
- if ($response['data']['error_code'] !== 0) {
- throw new BadRequestException($response['data']['description']);
- }
- @unlink($tempPartFile);
- $result[$chunkIndex] = $response;
- $chunkIndex++;
- }
- return $result;
- }
- /**
- * @param string $url
- * @param array $files
- * @param array $form
- * @param array $query
- * @return array|Collection|object|ResponseInterface|string
- * @throws Exceptions\HttpException
- * @throws Exceptions\InvalidConfigException
- * @throws GuzzleException
- */
- public function httpUpload(string $url, array $files = [], array $form = [], array $query = []) {
- $multipart = [];
- $headers = [];
- if (isset($form['filename'])) {
- $headers = [
- 'Content-Disposition' => 'form-data; name="media"; filename="' . $form['filename'] . '"'
- ];
- }
- foreach ($files as $name => $path) {
- $multipart[] = [
- 'name' => $name,
- 'contents' => fopen($path, 'r'),
- 'headers' => $headers
- ];
- }
- foreach ($form as $name => $contents) {
- $multipart[] = compact('name', 'contents');
- }
- return $this->request(
- $url,
- 'POST',
- ['query' => $query, 'multipart' => $multipart, 'connect_timeout' => 300, 'timeout' => 300, 'read_timeout' => 300]
- );
- }
- public function getAccessToken() {
- return $this->accessToken;
- }
- /**
- * @param AccessTokenInterface $accessToken
- *
- * @return $this
- */
- public function setAccessToken(AccessTokenInterface $accessToken): BaseClient {
- $this->accessToken = $accessToken;
- return $this;
- }
- /**
- *
- * @param string $url
- * @param string $method
- * @param array $options
- * @param false $returnRaw
- * @return array|Collection|object|ResponseInterface|string
- * @throws Exceptions\HttpException
- * @throws Exceptions\InvalidConfigException
- * @throws GuzzleException
- * @author zhaoxiang <zhaoxiang051405@gmail.com>
- */
- public function request(string $url, string $method = 'GET', array $options = [], bool $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'));
- }
- /**
- * @param string $url
- * @param string $method
- * @param array $options
- * @return Response
- * @throws Exceptions\HttpException
- * @throws Exceptions\InvalidConfigException
- * @throws GuzzleException
- */
- public function requestRaw(string $url, string $method = 'GET', array $options = []): Response {
- return Response::buildFromPsrResponse($this->request($url, $method, $options, true));
- }
- /**
- * Register Guzzle middlewares.
- */
- protected function registerHttpMiddlewares(): void {
- // retry
- $this->pushMiddleware($this->retryMiddleware(), 'retry');
- // access token
- if ($this->needAccessToken) {
- $this->pushMiddleware($this->accessTokenMiddleware(), 'access_token');
- }
- }
- /**
- * Attache access token to request query.
- *
- * @return Closure
- */
- protected function accessTokenMiddleware(): Closure {
- return function(callable $handler) {
- return function(RequestInterface $request, array $options) use ($handler) {
- if ($this->accessToken) {
- if ($this->headerAccessToken) {
- $request = $this->accessToken->applyToHeaderRequest($request);
- }else{
- if ($this->postAccessToken) {
- $request = $this->accessToken->applyToPostRequest($request, $options);
- } else {
- $request = $this->accessToken->applyToRequest($request, $options);
- }
- }
- }
- return $handler($request, $options);
- };
- };
- }
- /**
- * Return retry middleware.
- *
- * @return Closure
- */
- protected function retryMiddleware(): Closure {
- return Middleware::retry(
- function($retries, RequestInterface $request, ResponseInterface $response = null) {
- if ($retries < $this->app->config->get('http.max_retries', 1) && $response && $body = $response->getBody()) {
- $response = json_decode($body, true);
- if (
- (!empty($response['errcode']) && in_array(abs($response['errcode']), [40002], true)) ||
- (!empty($response['extra']['error_code']) && in_array(abs($response['extra']['error_code']), [2190008, 2190002, 10008], true))
- ) {
- $this->accessToken->refresh();
- return true;
- }
- }
- return false;
- },
- function() {
- return abs($this->app->config->get('http.retry_delay', 500));
- }
- );
- }
- }
|