init
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace yzh52521\EasyHttp;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ConnectionException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace yzh52521\EasyHttp;
|
||||
|
||||
class Facade
|
||||
{
|
||||
protected $facade;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->facade = new $this->facade;
|
||||
}
|
||||
|
||||
public function __call($name, $params)
|
||||
{
|
||||
if (method_exists($this->facade, 'removeOptions')) {
|
||||
call_user_func_array([$this->facade, 'removeOptions'], []);
|
||||
}
|
||||
return call_user_func_array([$this->facade, $name], $params);
|
||||
}
|
||||
|
||||
public static function __callStatic($name, $params)
|
||||
{
|
||||
return call_user_func_array([new static(), $name], $params);
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace yzh52521\EasyHttp;
|
||||
|
||||
|
||||
/**
|
||||
* @method static \yzh52521\EasyHttp\Request asJson()
|
||||
* @method static \yzh52521\EasyHttp\Request asForm()
|
||||
* @method static \yzh52521\EasyHttp\Request asMultipart(string $name, string $contents, string|null $filename = null, array $headers)
|
||||
* @method static \yzh52521\EasyHttp\Request attach(string $name, string $contents, string|null $filename = null, array $headers)
|
||||
*
|
||||
* @method static \yzh52521\EasyHttp\Request withRedirect(bool|array $redirect)
|
||||
* @method static \yzh52521\EasyHttp\Request withStream(bool $boolean)
|
||||
* @method static \yzh52521\EasyHttp\Request withVerify(bool|string $verify)
|
||||
* @method static \yzh52521\EasyHttp\Request withHost(string $host)
|
||||
* @method static \yzh52521\EasyHttp\Request withHeaders(array $headers)
|
||||
* @method static \yzh52521\EasyHttp\Request withBody($content,$contentType='application/json')
|
||||
* @method static \yzh52521\EasyHttp\Request withBasicAuth(string $username, string $password)
|
||||
* @method static \yzh52521\EasyHttp\Request withDigestAuth(string $username, string $password)
|
||||
* @method static \yzh52521\EasyHttp\Request withUA(string $ua)
|
||||
* @method static \yzh52521\EasyHttp\Request withToken(string $token, string $type = 'Bearer')
|
||||
* @method static \yzh52521\EasyHttp\Request withCookies(array $cookies, string $domain)
|
||||
* @method static \yzh52521\EasyHttp\Request withProxy(string|array $proxy)
|
||||
* @method static \yzh52521\EasyHttp\Request withVersion(string $version)
|
||||
* @method static \yzh52521\EasyHttp\Request withOptions(array $options)
|
||||
* @method static \yzh52521\EasyHttp\Request withMiddleware(callable $middleware)
|
||||
* @method static \yzh52521\EasyHttp\Request withRequestMiddleware(callable $middleware)
|
||||
* @method static \yzh52521\EasyHttp\Request withResponseMiddleware(callable $middleware)
|
||||
*
|
||||
* @method static \yzh52521\EasyHttp\Request debug($class)
|
||||
* @method static \yzh52521\EasyHttp\Request retry(int $retries=1,int $sleep=0)
|
||||
* @method static \yzh52521\EasyHttp\Request delay(int $seconds)
|
||||
* @method static \yzh52521\EasyHttp\Request timeout(float $seconds)
|
||||
* @method static \yzh52521\EasyHttp\Request connectTimeout(float $seconds)
|
||||
* @method static \yzh52521\EasyHttp\Request sink(string|resource $to)
|
||||
* @method static \yzh52521\EasyHttp\Request concurrency(int $times)
|
||||
* @method static \yzh52521\EasyHttp\Request removeBodyFormat()
|
||||
* @method static \yzh52521\EasyHttp\Request maxRedirects(int $max)
|
||||
*
|
||||
* @method static \yzh52521\EasyHttp\Response get(string $url, array $query = [])
|
||||
* @method static \yzh52521\EasyHttp\Response post(string $url, array $data = [])
|
||||
* @method static \yzh52521\EasyHttp\Response patch(string $url, array $data = [])
|
||||
* @method static \yzh52521\EasyHttp\Response put(string $url, array $data = [])
|
||||
* @method static \yzh52521\EasyHttp\Response delete(string $url, array $data = [])
|
||||
* @method static \yzh52521\EasyHttp\Response head(string $url, array $data = [])
|
||||
* @method static \yzh52521\EasyHttp\Response options(string $url, array $data = [])
|
||||
* @method static \yzh52521\EasyHttp\Response client(string $method, string $url, array $options = [])
|
||||
* @method static \yzh52521\EasyHttp\Response clientAsync(string $method, string $url, array $options = [])
|
||||
*
|
||||
* @method static \GuzzleHttp\Promise\PromiseInterface getAsync(string $url, array|null $query = null, callable $success = null, callable $fail = null)
|
||||
* @method static \GuzzleHttp\Promise\PromiseInterface postAsync(string $url, array|null $data = null, callable $success = null, callable $fail = null)
|
||||
* @method static \GuzzleHttp\Promise\PromiseInterface patchAsync(string $url, array|null $data = null, callable $success = null, callable $fail = null)
|
||||
* @method static \GuzzleHttp\Promise\PromiseInterface putAsync(string $url, array|null $data = null, callable $success = null, callable $fail = null)
|
||||
* @method static \GuzzleHttp\Promise\PromiseInterface deleteAsync(string $url, array|null $data = null, callable $success = null, callable $fail = null)
|
||||
* @method static \GuzzleHttp\Promise\PromiseInterface headAsync(string $url, array|null $data = null, callable $success = null, callable $fail = null)
|
||||
* @method static \GuzzleHttp\Promise\PromiseInterface optionsAsync(string $url, array|null $data = null, callable $success = null, callable $fail = null)
|
||||
* @method static \GuzzleHttp\Pool multiAsync(array $promises, callable $success = null, callable $fail = null)
|
||||
* @method static void wait()
|
||||
*/
|
||||
|
||||
class Http extends Facade
|
||||
{
|
||||
protected $facade = Request::class;
|
||||
}
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
namespace yzh52521\EasyHttp;
|
||||
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\MessageFormatter;
|
||||
use GuzzleHttp\Promise;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Guzzle middleware which logs a request and its response.
|
||||
*/
|
||||
class Logger
|
||||
{
|
||||
/**
|
||||
* @var \Psr\Log\LoggerInterface|callable
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* @var \GuzzleHttp\MessageFormatter|callable
|
||||
*/
|
||||
protected $formatter;
|
||||
|
||||
/**
|
||||
* @var string|callable Constant or callable that accepts a Response.
|
||||
*/
|
||||
protected $logLevel;
|
||||
|
||||
/**
|
||||
* @var boolean Whether or not to log requests as they are made.
|
||||
*/
|
||||
protected $logRequests;
|
||||
|
||||
/**
|
||||
* Creates a callable middleware for logging requests and responses.
|
||||
*
|
||||
* @param LoggerInterface|callable $logger
|
||||
* @param string|callable Constant or callable that accepts a Response.
|
||||
*/
|
||||
public function __construct($logger, $formatter = null)
|
||||
{
|
||||
// Use the setters to take care of type validation
|
||||
$this->setLogger($logger);
|
||||
$this->setFormatter($formatter ?: $this->getDefaultFormatter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default formatter;
|
||||
*
|
||||
* @return MessageFormatter
|
||||
*/
|
||||
protected function getDefaultFormatter()
|
||||
{
|
||||
return new MessageFormatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether requests should be logged before the response is received.
|
||||
*
|
||||
* @param boolean $logRequests
|
||||
*/
|
||||
public function setRequestLoggingEnabled($logRequests = true)
|
||||
{
|
||||
$this->logRequests = (bool) $logRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logger, which can be a PSR-3 logger or a callable that accepts
|
||||
* a log level, message, and array context.
|
||||
*
|
||||
* @param LoggerInterface|callable $logger
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setLogger($logger)
|
||||
{
|
||||
if ($logger instanceof LoggerInterface || is_callable($logger)) {
|
||||
$this->logger = $logger;
|
||||
} else {
|
||||
throw new InvalidArgumentException(
|
||||
"Logger has to be a Psr\Log\LoggerInterface or callable"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the formatter, which can be a MessageFormatter or callable that
|
||||
* accepts a request, response, and a reason if an error has occurred.
|
||||
*
|
||||
* @param MessageFormatter|callable $formatter
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setFormatter($formatter)
|
||||
{
|
||||
if ($formatter instanceof MessageFormatter || is_callable($formatter)) {
|
||||
$this->formatter = $formatter;
|
||||
} else {
|
||||
throw new InvalidArgumentException(
|
||||
"Formatter has to be a \GuzzleHttp\MessageFormatter or callable"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the log level to use, which can be either a string or a callable
|
||||
* that accepts a response (which could be null). A log level could also
|
||||
* be null, which indicates that the default log level should be used.
|
||||
*
|
||||
* @param string|callable|null
|
||||
*/
|
||||
public function setLogLevel($logLevel)
|
||||
{
|
||||
$this->logLevel = $logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a request and/or a response.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface|null $response
|
||||
* @param $reason
|
||||
* @return mixed
|
||||
*/
|
||||
protected function log(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response = null,
|
||||
$reason = null
|
||||
) {
|
||||
if ($reason instanceof RequestException) {
|
||||
$response = $reason->getResponse();
|
||||
}
|
||||
|
||||
$level = $this->getLogLevel($response);
|
||||
$message = $this->getLogMessage($request, $response, $reason);
|
||||
$context = compact('request', 'response', 'reason');
|
||||
|
||||
// Make sure that the content of the body is available again.
|
||||
if ($response) {
|
||||
$response->getBody()->seek(0);;
|
||||
}
|
||||
|
||||
if (is_callable($this->logger)) {
|
||||
return call_user_func($this->logger, $level, $message, $context);
|
||||
}
|
||||
|
||||
$this->logger->log($level, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a request and response as a log message.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
* @param ResponseInterface|null $response
|
||||
* @param mixed $reason
|
||||
*
|
||||
* @return string The formatted message.
|
||||
*/
|
||||
protected function getLogMessage(
|
||||
RequestInterface $request,
|
||||
ResponseInterface $response = null,
|
||||
$reason = null
|
||||
) {
|
||||
if ($this->formatter instanceof MessageFormatter) {
|
||||
return $this->formatter->format(
|
||||
$request,
|
||||
$response,
|
||||
$reason
|
||||
);
|
||||
}
|
||||
|
||||
return call_user_func($this->formatter, $request, $response, $reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a log level for a given response.
|
||||
*
|
||||
* @param ResponseInterface $response The response being logged.
|
||||
*
|
||||
* @return string LogLevel
|
||||
*/
|
||||
protected function getLogLevel(ResponseInterface $response = null)
|
||||
{
|
||||
if ( ! $this->logLevel) {
|
||||
return $this->getDefaultLogLevel($response);
|
||||
}
|
||||
|
||||
if (is_callable($this->logLevel)) {
|
||||
return call_user_func($this->logLevel, $response);
|
||||
}
|
||||
|
||||
return (string) $this->logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default log level for a response.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return string LogLevel
|
||||
*/
|
||||
protected function getDefaultLogLevel(ResponseInterface $response = null) {
|
||||
if ($response && $response->getStatusCode() >= 300) {
|
||||
return LogLevel::NOTICE;
|
||||
}
|
||||
|
||||
return LogLevel::INFO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function which is handled when a request was successful.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function onSuccess(RequestInterface $request)
|
||||
{
|
||||
return function ($response) use ($request) {
|
||||
$this->log($request, $response);
|
||||
return $response;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function which is handled when a request was rejected.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function onFailure(RequestInterface $request)
|
||||
{
|
||||
return function ($reason) use ($request) {
|
||||
|
||||
// Only log a rejected request if it hasn't already been logged.
|
||||
if ( ! $this->logRequests) {
|
||||
$this->log($request, null, $reason);
|
||||
}
|
||||
|
||||
return Promise\rejection_for($reason);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the middleware is handled by the client.
|
||||
*
|
||||
* @param callable $handler
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
public function __invoke(callable $handler)
|
||||
{
|
||||
return function ($request, array $options) use ($handler) {
|
||||
|
||||
// Only log requests if explicitly set to do so
|
||||
if ($this->logRequests) {
|
||||
$this->log($request);
|
||||
}
|
||||
|
||||
return $handler($request, $options)->then(
|
||||
$this->onSuccess($request),
|
||||
$this->onFailure($request)
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
+711
@@ -0,0 +1,711 @@
|
||||
<?php
|
||||
|
||||
namespace yzh52521\EasyHttp;
|
||||
|
||||
use GuzzleHttp\Handler\CurlHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
|
||||
use GuzzleHttp\Middleware;
|
||||
use GuzzleHttp\Pool;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Promise;
|
||||
use GuzzleHttp\Cookie\CookieJar;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
|
||||
/**
|
||||
* @method \yzh52521\EasyHttp\Response body()
|
||||
* @method \yzh52521\EasyHttp\Response array()
|
||||
* @method \yzh52521\EasyHttp\Response json()
|
||||
* @method \yzh52521\EasyHttp\Response headers()
|
||||
* @method \yzh52521\EasyHttp\Response header(string $header)
|
||||
* @method \yzh52521\EasyHttp\Response status()
|
||||
* @method \yzh52521\EasyHttp\Response successful()
|
||||
* @method \yzh52521\EasyHttp\Response ok()
|
||||
* @method \yzh52521\EasyHttp\Response redirect()
|
||||
* @method \yzh52521\EasyHttp\Response clientError()
|
||||
* @method \yzh52521\EasyHttp\Response serverError()
|
||||
*/
|
||||
class Request
|
||||
{
|
||||
/**
|
||||
* \GuzzleHttp\Client单例
|
||||
* @var array
|
||||
*/
|
||||
private static $instances = [];
|
||||
|
||||
/**
|
||||
* \GuzzleHttp\Client;
|
||||
* @var Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Body格式
|
||||
* @var string
|
||||
*/
|
||||
protected $bodyFormat;
|
||||
|
||||
/**
|
||||
* The raw body for the request.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pendingBody;
|
||||
|
||||
protected $isRemoveBodyFormat = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $promises = [];
|
||||
|
||||
/**
|
||||
* 并发次数
|
||||
* @var
|
||||
*/
|
||||
protected $concurrency;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var HandlerStack
|
||||
*/
|
||||
protected $handlerStack;
|
||||
|
||||
|
||||
/**
|
||||
* Request constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->client = $this->getInstance();
|
||||
|
||||
$this->bodyFormat = 'form_params';
|
||||
$this->options = [
|
||||
'http_errors' => false,
|
||||
];
|
||||
$this->handlerStack = HandlerStack::create(new CurlHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Request destructor.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例
|
||||
* @return mixed
|
||||
*/
|
||||
public function getInstance()
|
||||
{
|
||||
$name = get_called_class();
|
||||
|
||||
if (!isset(self::$instances[$name])) {
|
||||
self::$instances[$name] = new Client();
|
||||
}
|
||||
|
||||
return self::$instances[$name];
|
||||
}
|
||||
|
||||
public function removeOptions()
|
||||
{
|
||||
$this->bodyFormat = 'form_params';
|
||||
$this->isRemoveBodyFormat = false;
|
||||
$this->options = [
|
||||
'http_errors' => false,
|
||||
'verify' => false
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function asForm()
|
||||
{
|
||||
$this->bodyFormat = 'form_params';
|
||||
$this->withHeaders(['Content-Type' => 'application/x-www-form-urlencoded']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function asJson()
|
||||
{
|
||||
$this->bodyFormat = 'json';
|
||||
$this->withHeaders(['Content-Type' => 'application/json']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function asMultipart(string $name, string $contents, string $filename = null, array $headers = [])
|
||||
{
|
||||
$this->bodyFormat = 'multipart';
|
||||
|
||||
$this->options = array_filter([
|
||||
'name' => $name,
|
||||
'contents' => $contents,
|
||||
'headers' => $headers,
|
||||
'filename' => $filename,
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withMiddleware(callable $middleware)
|
||||
{
|
||||
$this->handlerStack->push($middleware);
|
||||
|
||||
$this->options['handler'] = $this->handlerStack;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withRequestMiddleware(callable $middleware)
|
||||
{
|
||||
$this->handlerStack->push(Middleware::mapRequest($middleware));
|
||||
|
||||
$this->options['handler'] = $this->handlerStack;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withResponseMiddleware(callable $middleware)
|
||||
{
|
||||
$this->handlerStack->push(Middleware::mapResponse($middleware));
|
||||
|
||||
$this->options['handler'] = $this->handlerStack;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withHost(string $host)
|
||||
{
|
||||
$this->options['base_uri'] = $host;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withOptions(array $options)
|
||||
{
|
||||
unset($this->options[$this->bodyFormat], $this->options['body']);
|
||||
|
||||
$this->options = array_merge_recursive($this->options, $options);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withCert(string $path, string $password)
|
||||
{
|
||||
$this->options['cert'] = [$path, $password];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withHeaders(array $headers)
|
||||
{
|
||||
$this->options = array_merge_recursive($this->options, [
|
||||
'headers' => $headers,
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withBody($content, $contentType = 'application/json')
|
||||
{
|
||||
$this->bodyFormat = 'body';
|
||||
|
||||
$this->options['headers']['Content-Type'] = $contentType;
|
||||
|
||||
$this->pendingBody = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withBasicAuth(string $username, string $password)
|
||||
{
|
||||
$this->options['auth'] = [$username, $password];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withDigestAuth(string $username, string $password)
|
||||
{
|
||||
$this->options['auth'] = [$username, $password, 'digest'];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withUA(string $ua)
|
||||
{
|
||||
$this->options['headers']['User-Agent'] = trim($ua);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withToken(string $token, string $type = 'Bearer')
|
||||
{
|
||||
$this->options['headers']['Authorization'] = trim($type . ' ' . $token);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withCookies(array $cookies, string $domain)
|
||||
{
|
||||
$this->options = array_merge_recursive($this->options, [
|
||||
'cookies' => CookieJar::fromArray($cookies, $domain),
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withProxy($proxy)
|
||||
{
|
||||
$this->options['proxy'] = $proxy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withVersion($version)
|
||||
{
|
||||
$this->options['version'] = $version;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function maxRedirects(int $max)
|
||||
{
|
||||
$this->options['allow_redirects']['max'] = $max;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withRedirect($redirect = false)
|
||||
{
|
||||
$this->options['allow_redirects'] = $redirect;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withVerify($verify = false)
|
||||
{
|
||||
$this->options['verify'] = $verify;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withStream($boolean = false)
|
||||
{
|
||||
$this->options['stream'] = $boolean;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function concurrency(int $times)
|
||||
{
|
||||
$this->concurrency = $times;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function retry(int $retries = 1, int $sleep = 0)
|
||||
{
|
||||
$this->handlerStack->push((new Retry())->handle($retries, $sleep));
|
||||
|
||||
$this->options['handler'] = $this->handlerStack;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function delay(int $seconds)
|
||||
{
|
||||
$this->options['delay'] = $seconds * 1000;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function timeout(float $seconds)
|
||||
{
|
||||
$this->options['timeout'] = $seconds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function connectTimeout(float $seconds)
|
||||
{
|
||||
$this->options['connect_timeout'] = $seconds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|resource $to
|
||||
* @return $this
|
||||
*/
|
||||
public function sink($to)
|
||||
{
|
||||
$this->options['sink'] = $to;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeBodyFormat()
|
||||
{
|
||||
$this->isRemoveBodyFormat = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function debug($class)
|
||||
{
|
||||
$logger = new Logger(function ($level, $message, array $context) use ($class) {
|
||||
$class::log($level, $message);
|
||||
}, function ($request, $response, $reason) {
|
||||
$requestBody = $request->getBody();
|
||||
$requestBody->rewind();
|
||||
|
||||
//请求头
|
||||
$requestHeaders = [];
|
||||
|
||||
foreach ((array)$request->getHeaders() as $k => $vs) {
|
||||
foreach ($vs as $v) {
|
||||
$requestHeaders[] = "$k: $v";
|
||||
}
|
||||
}
|
||||
|
||||
//响应头
|
||||
$responseHeaders = [];
|
||||
|
||||
foreach ((array)$response->getHeaders() as $k => $vs) {
|
||||
foreach ($vs as $v) {
|
||||
$responseHeaders[] = "$k: $v";
|
||||
}
|
||||
}
|
||||
|
||||
$uri = $request->getUri();
|
||||
$path = $uri->getPath();
|
||||
|
||||
if ($query = $uri->getQuery()) {
|
||||
$path .= '?' . $query;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
"Request %s\n%s %s HTTP/%s\r\n%s\r\n\r\n%s\r\n--------------------\r\nHTTP/%s %s %s\r\n%s\r\n\r\n%s",
|
||||
$uri,
|
||||
$request->getMethod(),
|
||||
$path,
|
||||
$request->getProtocolVersion(),
|
||||
join("\r\n", $requestHeaders),
|
||||
$requestBody->getContents(),
|
||||
$response->getProtocolVersion(),
|
||||
$response->getStatusCode(),
|
||||
$response->getReasonPhrase(),
|
||||
join("\r\n", $responseHeaders),
|
||||
$response->getBody()->getContents()
|
||||
);
|
||||
});
|
||||
$this->handlerStack->push($logger);
|
||||
$this->options['handler'] = $this->handlerStack;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function attach(string $name, string $contents, string $filename = null, array $headers = [])
|
||||
{
|
||||
$this->options['multipart'] = array_filter([
|
||||
'name' => $name,
|
||||
'contents' => $contents,
|
||||
'headers' => $headers,
|
||||
'filename' => $filename,
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get(string $url, array $query = [])
|
||||
{
|
||||
$params = parse_url($url, PHP_URL_QUERY);
|
||||
|
||||
parse_str($params ?: '', $result);
|
||||
|
||||
$this->options['query'] = array_merge($result, $query);
|
||||
|
||||
return $this->request('GET', $url, $query);
|
||||
}
|
||||
|
||||
public function post(string $url, array $data = [])
|
||||
{
|
||||
$this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->request('POST', $url, $data);
|
||||
}
|
||||
|
||||
public function patch(string $url, array $data = [])
|
||||
{
|
||||
$this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->request('PATCH', $url, $data);
|
||||
}
|
||||
|
||||
public function put(string $url, array $data = [])
|
||||
{
|
||||
$this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->request('PUT', $url, $data);
|
||||
}
|
||||
|
||||
public function delete(string $url, array $data = [])
|
||||
{
|
||||
$this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->request('DELETE', $url, $data);
|
||||
}
|
||||
|
||||
public function head(string $url, array $data = [])
|
||||
{
|
||||
$this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->request('HEAD', $url, $data);
|
||||
}
|
||||
|
||||
public function options(string $url, array $data = [])
|
||||
{
|
||||
$this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->request('OPTIONS', $url, $data);
|
||||
}
|
||||
|
||||
public function getAsync(string $url, $query = null, callable $success = null, callable $fail = null)
|
||||
{
|
||||
is_callable($query) || $this->options['query'] = $query;
|
||||
|
||||
return $this->requestAsync('GET', $url, $query, $success, $fail);
|
||||
}
|
||||
|
||||
public function postAsync(string $url, $data = null, callable $success = null, callable $fail = null)
|
||||
{
|
||||
is_callable($data) || $this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->requestAsync('POST', $url, $data, $success, $fail);
|
||||
}
|
||||
|
||||
public function patchAsync(string $url, $data = null, callable $success = null, callable $fail = null)
|
||||
{
|
||||
is_callable($data) || $this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->requestAsync('PATCH', $url, $data, $success, $fail);
|
||||
}
|
||||
|
||||
public function putAsync(string $url, $data = null, callable $success = null, callable $fail = null)
|
||||
{
|
||||
is_callable($data) || $this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->requestAsync('PUT', $url, $data, $success, $fail);
|
||||
}
|
||||
|
||||
public function deleteAsync(string $url, $data = null, callable $success = null, callable $fail = null)
|
||||
{
|
||||
is_callable($data) || $this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->requestAsync('DELETE', $url, $data, $success, $fail);
|
||||
}
|
||||
|
||||
public function headAsync(string $url, $data = null, callable $success = null, callable $fail = null)
|
||||
{
|
||||
is_callable($data) || $this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->requestAsync('HEAD', $url, $data, $success, $fail);
|
||||
}
|
||||
|
||||
public function optionsAsync(string $url, $data = null, callable $success = null, callable $fail = null)
|
||||
{
|
||||
is_callable($data) || $this->options[$this->bodyFormat] = $data;
|
||||
|
||||
return $this->requestAsync('OPTIONS', $url, $data, $success, $fail);
|
||||
}
|
||||
|
||||
public function multiAsync(array $promises, callable $success = null, callable $fail = null)
|
||||
{
|
||||
$count = count($promises);
|
||||
|
||||
$this->concurrency = $this->concurrency ?: $count;
|
||||
|
||||
$requests = function () use ($promises) {
|
||||
foreach ($promises as $promise) {
|
||||
yield function () use ($promise) {
|
||||
return $promise;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
$fulfilled = function ($response, $index) use ($success) {
|
||||
if (!is_null($success)) {
|
||||
$response = $this->response($response);
|
||||
call_user_func_array($success, [$response, $index]);
|
||||
}
|
||||
};
|
||||
|
||||
$rejected = function ($exception, $index) use ($fail) {
|
||||
if (!is_null($fail)) {
|
||||
$exception = $this->exception($exception);
|
||||
call_user_func_array($fail, [$exception, $index]);
|
||||
}
|
||||
};
|
||||
|
||||
$pool = new Pool($this->client, $requests(), [
|
||||
'concurrency' => $this->concurrency,
|
||||
'fulfilled' => $fulfilled,
|
||||
'rejected' => $rejected,
|
||||
]);
|
||||
|
||||
$pool->promise();
|
||||
|
||||
return $pool;
|
||||
}
|
||||
|
||||
protected function request(string $method, string $url, array $options = [])
|
||||
{
|
||||
if (isset($this->options[$this->bodyFormat])) {
|
||||
$this->options[$this->bodyFormat] = $options;
|
||||
} else {
|
||||
$this->options[$this->bodyFormat] = $this->pendingBody;
|
||||
}
|
||||
if ($this->isRemoveBodyFormat) {
|
||||
unset($this->options[$this->bodyFormat]);
|
||||
}
|
||||
try {
|
||||
$response = $this->client->request($method, $url, $this->options);
|
||||
return $this->response($response);
|
||||
} catch (ConnectException $e) {
|
||||
throw new ConnectionException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 原生请求
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return Response
|
||||
* @throws ConnectionException
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function client(string $method, string $url, array $options = [])
|
||||
{
|
||||
if (isset($this->options[$this->bodyFormat])) {
|
||||
$this->options[$this->bodyFormat] = $options;
|
||||
} else {
|
||||
$this->options[$this->bodyFormat] = $this->pendingBody;
|
||||
}
|
||||
if ($this->isRemoveBodyFormat) {
|
||||
unset($this->options[$this->bodyFormat]);
|
||||
}
|
||||
try {
|
||||
if (empty($options)) {
|
||||
$options = $this->options;
|
||||
}
|
||||
$response = $this->client->request($method, $url, $options);
|
||||
return $this->response($response);
|
||||
} catch (ConnectException $e) {
|
||||
throw new ConnectionException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 原生异步请求
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return Response
|
||||
* @throws ConnectionException
|
||||
*/
|
||||
public function clientAsync(string $method, string $url, array $options = [])
|
||||
{
|
||||
if (isset($this->options[$this->bodyFormat])) {
|
||||
$this->options[$this->bodyFormat] = $options;
|
||||
} else {
|
||||
$this->options[$this->bodyFormat] = $this->pendingBody;
|
||||
}
|
||||
if ($this->isRemoveBodyFormat) {
|
||||
unset($this->options[$this->bodyFormat]);
|
||||
}
|
||||
try {
|
||||
if (empty($options)) {
|
||||
$options = $this->options;
|
||||
}
|
||||
$response = $this->client->requestAsync($method, $url, $options);
|
||||
return $this->response($response);
|
||||
} catch (ConnectException $e) {
|
||||
throw new ConnectionException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function requestAsync(string $method, string $url, $options = null, callable $success = null, callable $fail = null)
|
||||
{
|
||||
if (is_callable($options)) {
|
||||
$successCallback = $options;
|
||||
$failCallback = $success;
|
||||
} else {
|
||||
$successCallback = $success;
|
||||
$failCallback = $fail;
|
||||
}
|
||||
|
||||
if (isset($this->options[$this->bodyFormat])) {
|
||||
$this->options[$this->bodyFormat] = $options;
|
||||
} else {
|
||||
$this->options[$this->bodyFormat] = $this->pendingBody;
|
||||
}
|
||||
|
||||
if ($this->isRemoveBodyFormat) {
|
||||
unset($this->options[$this->bodyFormat]);
|
||||
}
|
||||
|
||||
try {
|
||||
$promise = $this->client->requestAsync($method, $url, $this->options);
|
||||
|
||||
$fulfilled = function ($response) use ($successCallback) {
|
||||
if (!is_null($successCallback)) {
|
||||
$response = $this->response($response);
|
||||
call_user_func_array($successCallback, [$response]);
|
||||
}
|
||||
};
|
||||
|
||||
$rejected = function ($exception) use ($failCallback) {
|
||||
if (!is_null($failCallback)) {
|
||||
$exception = $this->exception($exception);
|
||||
call_user_func_array($failCallback, [$exception]);
|
||||
}
|
||||
};
|
||||
|
||||
$promise->then($fulfilled, $rejected);
|
||||
|
||||
$this->promises[] = $promise;
|
||||
|
||||
return $promise;
|
||||
} catch (ConnectException $e) {
|
||||
throw new ConnectionException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function wait()
|
||||
{
|
||||
if (!empty($this->promises)) {
|
||||
\GuzzleHttp\Promise\Utils($this->promises)->wait();
|
||||
}
|
||||
$this->promises = [];
|
||||
}
|
||||
|
||||
protected function response($response)
|
||||
{
|
||||
return new Response($response);
|
||||
}
|
||||
|
||||
protected function exception($exception)
|
||||
{
|
||||
return new RequestException($exception);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace yzh52521\EasyHttp;
|
||||
|
||||
class RequestException
|
||||
{
|
||||
public $exception;
|
||||
|
||||
public function __construct($exception)
|
||||
{
|
||||
$this->exception = $exception;
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return $this->exception->getCode();
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->exception->getMessage();
|
||||
}
|
||||
|
||||
public function getFile()
|
||||
{
|
||||
return $this->exception->getFile();
|
||||
}
|
||||
|
||||
public function getLine()
|
||||
{
|
||||
return $this->exception->getLine();
|
||||
}
|
||||
|
||||
public function getTrace()
|
||||
{
|
||||
return $this->exception->getTrace();
|
||||
}
|
||||
|
||||
public function getTraceAsString()
|
||||
{
|
||||
return $this->exception->getTraceAsString();
|
||||
}
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
namespace yzh52521\EasyHttp;
|
||||
|
||||
use ArrayAccess;
|
||||
use LogicException;
|
||||
|
||||
class Response implements ArrayAccess
|
||||
{
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* The decoded JSON response.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $decoded;
|
||||
|
||||
public function __construct($response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the body of the response.
|
||||
* @return string
|
||||
*/
|
||||
public function body()
|
||||
{
|
||||
return (string)$this->response->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Array decoded body of the response.
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function array()
|
||||
{
|
||||
if (!$this->decoded) {
|
||||
$this->decoded = json_decode( (string)$this->response->getBody(),true );
|
||||
}
|
||||
|
||||
return $this->decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON decoded body of the response.
|
||||
* @return object|mixed
|
||||
*/
|
||||
public function json()
|
||||
{
|
||||
if (!$this->decoded) {
|
||||
$this->decoded = json_decode( (string)$this->response->getBody() );
|
||||
}
|
||||
|
||||
return $this->decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a header from the response.
|
||||
* @param string $header
|
||||
* @return mixed
|
||||
*/
|
||||
public function header(string $header)
|
||||
{
|
||||
return $this->response->getHeaderLine( $header );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the headers from the response.
|
||||
* @return mixed
|
||||
*/
|
||||
public function headers()
|
||||
{
|
||||
return $this->mapWithKeys( $this->response->getHeaders(),function ($v,$k) {
|
||||
return [$k => $v];
|
||||
} )->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status code of the response.
|
||||
* @return int
|
||||
*/
|
||||
public function status()
|
||||
{
|
||||
return (int)$this->response->getStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the request was successful.
|
||||
* @return bool
|
||||
*/
|
||||
public function successful()
|
||||
{
|
||||
return $this->status() >= 200 && $this->status() < 300;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the response code was "OK".
|
||||
* @return bool
|
||||
*/
|
||||
public function ok()
|
||||
{
|
||||
return $this->status() === 200;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the response was a redirect.
|
||||
* @return bool
|
||||
*/
|
||||
public function redirect()
|
||||
{
|
||||
return $this->status() >= 300 && $this->status() < 400;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the response indicates a client error occurred.
|
||||
* @return bool
|
||||
*/
|
||||
public function clientError()
|
||||
{
|
||||
return $this->status() >= 400 && $this->status() < 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the response indicates a server error occurred.
|
||||
* @return bool
|
||||
*/
|
||||
public function serverError()
|
||||
{
|
||||
return $this->status() >= 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given offset exists.
|
||||
*
|
||||
* @param string $offset
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists( $offset,$this->json() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for a given offset.
|
||||
*
|
||||
* @param string $offset
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->json()[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value at the given offset.
|
||||
*
|
||||
* @param string $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset,$value)
|
||||
{
|
||||
throw new LogicException( 'Response data may not be mutated using array access.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset the value at the given offset.
|
||||
*
|
||||
* @param string $offset
|
||||
* @return void
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
throw new LogicException( 'Response data may not be mutated using array access.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the body of the response.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->body();
|
||||
}
|
||||
|
||||
protected function mapWithKeys($items,callable $callback)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ( $items as $key => $value ) {
|
||||
$assoc = $callback( $value,$key );
|
||||
|
||||
foreach ( $assoc as $mapKey => $mapValue ) {
|
||||
$result[$mapKey] = $mapValue;
|
||||
}
|
||||
}
|
||||
|
||||
return new static( $result );
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace yzh52521\EasyHttp;
|
||||
|
||||
use GuzzleHttp\Middleware;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
|
||||
class Retry
|
||||
{
|
||||
|
||||
public function handle($retries,$sleep)
|
||||
{
|
||||
return Middleware::retry($this->decider($retries), $this->delay($sleep));
|
||||
}
|
||||
|
||||
protected function decider(int $times)
|
||||
{
|
||||
return function (
|
||||
$retries,
|
||||
Request $request,
|
||||
Response $response = null,
|
||||
RequestException $exception = null
|
||||
) use ($times) {
|
||||
// 超过最大重试次数,不再重试
|
||||
if ($retries >= $times) {
|
||||
return false;
|
||||
}
|
||||
return $exception instanceof ConnectException || $exception instanceof ServerException || ($response && $response->getStatusCode() >= 500);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个匿名函数,该匿名函数返回下次重试的时间(毫秒)
|
||||
* @param int $retry_delay
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function delay(int $retry_delay)
|
||||
{
|
||||
return function ($retries) use ($retry_delay) {
|
||||
return $retry_delay * $retries;
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user