Finish http client refactoring

This commit is contained in:
joyqi 2021-08-21 23:11:10 +08:00
parent 80746465c7
commit eeb893485b
6 changed files with 126 additions and 133 deletions

View File

@ -1,14 +1,8 @@
<?php
/**
* Http客户端
*
* @author qining
* @category typecho
* @package Http
* @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org)
* @license GNU General Public License 2.0
* @version $Id$
*/
namespace Typecho\Http;
use Typecho\Http\Client\Adapter;
/**
* Http客户端
@ -17,38 +11,34 @@
* @category typecho
* @package Http
*/
class Typecho_Http_Client
class Client
{
/** POST方法 */
const METHOD_POST = 'POST';
public const METHOD_POST = 'POST';
/** GET方法 */
const METHOD_GET = 'GET';
public const METHOD_GET = 'GET';
/** 定义行结束符 */
const EOL = "\r\n";
public const EOL = "\r\n";
private const ADAPTERS = ['Curl', 'Socket'];
/**
* 获取可用的连接
*
* @access public
* @return ?Typecho_Http_Client_Adapter
* @param string ...$adapters
* @return ?Adapter
*/
public static function get(): ?Typecho_Http_Client_Adapter
public static function get(string ...$adapters): ?Adapter
{
$adapters = func_get_args();
if (empty($adapters)) {
$adapters = [];
$adapterFiles = glob(dirname(__FILE__) . '/Client/Adapter/*.php');
foreach ($adapterFiles as $file) {
$adapters[] = substr(basename($file), 0, - 4);
}
$adapters = self::ADAPTERS;
}
foreach ($adapters as $adapter) {
$adapterName = 'Typecho_Http_Client_Adapter_' . $adapter;
if (Typecho_Common::isAvailableClass($adapterName) && call_user_func([$adapterName, 'isAvailable'])) {
if (call_user_func([$adapterName, 'isAvailable'])) {
return new $adapterName();
}
}

View File

@ -1,14 +1,9 @@
<?php
/**
* 客户端适配器
*
* @author qining
* @category typecho
* @package Http
* @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org)
* @license GNU General Public License 2.0
* @version $Id$
*/
namespace Typecho\Http\Client;
use Typecho\Common;
use Typecho\Http\Client;
/**
* 客户端适配器
@ -17,7 +12,7 @@
* @category typecho
* @package Http
*/
abstract class Typecho_Http_Client_Adapter
abstract class Adapter
{
/**
* 方法名
@ -25,7 +20,7 @@ abstract class Typecho_Http_Client_Adapter
* @access protected
* @var string
*/
protected $method = Typecho_Http_Client::METHOD_GET;
protected $method = Client::METHOD_GET;
/**
* 传递参数
@ -47,7 +42,7 @@ abstract class Typecho_Http_Client_Adapter
* 需要在body中传递的值
*
* @access protected
* @var array
* @var array|string
*/
protected $data = [];
@ -161,10 +156,7 @@ abstract class Typecho_Http_Client_Adapter
* @access public
* @return boolean
*/
public static function isAvailable()
{
return true;
}
abstract public static function isAvailable(): bool;
/**
* 设置指定的COOKIE值
@ -172,9 +164,9 @@ abstract class Typecho_Http_Client_Adapter
* @access public
* @param string $key 指定的参数
* @param mixed $value 设置的值
* @return Typecho_Http_Client_Adapter
* @return $this
*/
public function setCookie($key, $value)
public function setCookie(string $key, $value): Adapter
{
$this->cookies[$key] = $value;
return $this;
@ -185,9 +177,9 @@ abstract class Typecho_Http_Client_Adapter
*
* @access public
* @param mixed $query 传递参数
* @return Typecho_Http_Client_Adapter
* @return $this
*/
public function setQuery($query)
public function setQuery($query): Adapter
{
$query = is_array($query) ? http_build_query($query) : $query;
$this->query = empty($this->query) ? $query : $this->query . '&' . $query;
@ -198,13 +190,13 @@ abstract class Typecho_Http_Client_Adapter
* 设置需要POST的数据
*
* @access public
* @param array $data 需要POST的数据
* @return Typecho_Http_Client_Adapter
* @param array|string $data 需要POST的数据
* @return $this
*/
public function setData($data)
public function setData($data): Adapter
{
$this->data = $data;
$this->setMethod(Typecho_Http_Client::METHOD_POST);
$this->setMethod(Client::METHOD_POST);
return $this;
}
@ -213,9 +205,9 @@ abstract class Typecho_Http_Client_Adapter
*
* @access public
* @param string $method
* @return Typecho_Http_Client_Adapter
* @return $this
*/
public function setMethod($method)
public function setMethod(string $method): Adapter
{
$this->method = $method;
return $this;
@ -226,12 +218,12 @@ abstract class Typecho_Http_Client_Adapter
*
* @access public
* @param array $files 需要POST的文件
* @return Typecho_Http_Client_Adapter
* @return $this
*/
public function setFiles(array $files)
public function setFiles(array $files): Adapter
{
$this->files = empty($this->files) ? $files : array_merge($this->files, $files);
$this->setMethod(Typecho_Http_Client::METHOD_POST);
$this->setMethod(Client::METHOD_POST);
return $this;
}
@ -240,9 +232,9 @@ abstract class Typecho_Http_Client_Adapter
*
* @access public
* @param integer $timeout 超时时间
* @return Typecho_Http_Client_Adapter
* @return $this
*/
public function setTimeout($timeout)
public function setTimeout(int $timeout): Adapter
{
$this->timeout = $timeout;
return $this;
@ -253,9 +245,9 @@ abstract class Typecho_Http_Client_Adapter
*
* @access public
* @param string $rfc http协议
* @return Typecho_Http_Client_Adapter
* @return $this
*/
public function setRfc($rfc)
public function setRfc(string $rfc): Adapter
{
$this->rfc = $rfc;
return $this;
@ -266,9 +258,9 @@ abstract class Typecho_Http_Client_Adapter
*
* @access public
* @param string $ip ip地址
* @return Typecho_Http_Client_Adapter
* @return $this
*/
public function setIp($ip)
public function setIp(string $ip): Adapter
{
$this->ip = $ip;
return $this;
@ -279,21 +271,21 @@ abstract class Typecho_Http_Client_Adapter
*
* @access public
* @param string $url 请求地址
* @return string
* @throws Typecho_Http_Client_Exception
* @return string|null
* @throws Exception
*/
public function send($url)
public function send(string $url): ?string
{
$params = parse_url($url);
if (!empty($params['host'])) {
$this->host = $params['host'];
} else {
throw new Typecho_Http_Client_Exception('Unknown Host', 500);
throw new Exception('Unknown Host', 500);
}
if (!in_array($params['scheme'], ['http', 'https'])) {
throw new Typecho_Http_Client_Exception('Unknown Scheme', 500);
throw new Exception('Unknown Scheme', 500);
}
if (!empty($params['path'])) {
@ -313,7 +305,7 @@ abstract class Typecho_Http_Client_Adapter
$this->scheme = $params['scheme'];
$this->port = ('https' == $params['scheme']) ? 443 : 80;
$url = Typecho_Common::buildUrl($params);
$url = Common::buildUrl($params);
if (!empty($params['port'])) {
$this->port = $params['port'];
@ -327,7 +319,7 @@ abstract class Typecho_Http_Client_Adapter
$response = $this->httpSend($url);
if (!$response) {
return;
return null;
}
str_replace("\r", '', $response);
@ -374,9 +366,9 @@ abstract class Typecho_Http_Client_Adapter
* @access public
* @param string $key 参数名称
* @param string $value 参数值
* @return Typecho_Http_Client_Adapter
* @return $this
*/
public function setHeader($key, $value)
public function setHeader(string $key, string $value): Adapter
{
$key = str_replace(' ', '-', ucwords(str_replace('-', ' ', $key)));
$this->headers[$key] = $value;
@ -390,7 +382,7 @@ abstract class Typecho_Http_Client_Adapter
* @param string $url 请求地址
* @return string
*/
abstract protected function httpSend($url);
abstract protected function httpSend(string $url): string;
/**
* 获取回执的头部信息
@ -399,7 +391,7 @@ abstract class Typecho_Http_Client_Adapter
* @param string $key 头信息名称
* @return string
*/
public function getResponseHeader($key)
public function getResponseHeader(string $key): ?string
{
$key = strtolower($key);
return $this->responseHeader[$key] ?? null;
@ -411,7 +403,7 @@ abstract class Typecho_Http_Client_Adapter
* @access public
* @return integer
*/
public function getResponseStatus()
public function getResponseStatus(): int
{
return $this->responseStatus;
}
@ -422,7 +414,7 @@ abstract class Typecho_Http_Client_Adapter
* @access public
* @return string
*/
public function getResponseBody()
public function getResponseBody(): string
{
return $this->responseBody;
}

View File

@ -1,15 +1,13 @@
<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
/**
* CURL适配器
*
* @author qining
* @category typecho
* @package Http
* @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org)
* @license GNU General Public License 2.0
* @version $Id$
*/
namespace Typecho\Http\Client\Adapter;
use Typecho\Http\Client;
use Typecho\Http\Client\Adapter;
if (!defined('__TYPECHO_ROOT_DIR__')) {
exit;
}
/**
* CURL适配器
@ -18,7 +16,7 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit;
* @category typecho
* @package Http
*/
class Typecho_Http_Client_Adapter_Curl extends Typecho_Http_Client_Adapter
class Curl extends Adapter
{
/**
* 判断适配器是否可用
@ -26,7 +24,7 @@ class Typecho_Http_Client_Adapter_Curl extends Typecho_Http_Client_Adapter
* @access public
* @return boolean
*/
public static function isAvailable()
public static function isAvailable(): bool
{
return function_exists('curl_version');
}
@ -37,8 +35,9 @@ class Typecho_Http_Client_Adapter_Curl extends Typecho_Http_Client_Adapter
* @access public
* @param string $url 请求地址
* @return string
* @throws Client\Exception
*/
protected function httpSend($url)
protected function httpSend(string $url): string
{
$ch = curl_init();
@ -93,17 +92,21 @@ class Typecho_Http_Client_Adapter_Curl extends Typecho_Http_Client_Adapter
}
/** POST模式 */
if (Typecho_Http_Client::METHOD_POST == $this->method) {
if (Client::METHOD_POST == $this->method) {
if (!isset($this->headers['content-type'])) {
curl_setopt($ch, CURLOPT_POST, true);
}
if (!empty($this->data)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($this->data) ? http_build_query($this->data) : $this->data);
curl_setopt(
$ch,
CURLOPT_POSTFIELDS,
is_array($this->data) ? http_build_query($this->data) : $this->data
);
}
if (!empty($this->files)) {
foreach ($this->files as $key => &$file) {
foreach ($this->files as &$file) {
$file = '@' . $file;
}
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->files);
@ -112,7 +115,7 @@ class Typecho_Http_Client_Adapter_Curl extends Typecho_Http_Client_Adapter
$response = curl_exec($ch);
if (false === $response) {
throw new Typecho_Http_Client_Exception(curl_error($ch), 500);
throw new Client\Exception(curl_error($ch), 500);
}
curl_close($ch);

View File

@ -1,15 +1,13 @@
<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
/**
* Socket适配器
*
* @author qining
* @category typecho
* @package Http
* @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org)
* @license GNU General Public License 2.0
* @version $Id$
*/
namespace Typecho\Http\Client\Adapter;
use Typecho\Http\Client;
use Typecho\Http\Client\Adapter;
if (!defined('__TYPECHO_ROOT_DIR__')) {
exit;
}
/**
* Socket适配器
@ -18,7 +16,7 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit;
* @category typecho
* @package Http
*/
class Typecho_Http_Client_Adapter_Socket extends Typecho_Http_Client_Adapter
class Socket extends Adapter
{
/**
* 判断适配器是否可用
@ -26,7 +24,7 @@ class Typecho_Http_Client_Adapter_Socket extends Typecho_Http_Client_Adapter
* @access public
* @return boolean
*/
public static function isAvailable()
public static function isAvailable(): bool
{
return function_exists("fsockopen");
}
@ -37,7 +35,7 @@ class Typecho_Http_Client_Adapter_Socket extends Typecho_Http_Client_Adapter
* @access public
* @return string
*/
public function getResponseBody()
public function getResponseBody(): string
{
/** 支持chunked编码 */
if ('chunked' == $this->getResponseHeader('Transfer-Encoding')) {
@ -55,10 +53,11 @@ class Typecho_Http_Client_Adapter_Socket extends Typecho_Http_Client_Adapter
* @access public
* @param string $url 请求地址
* @return string
* @throws Client\Exception
*/
protected function httpSend($url)
protected function httpSend(string $url): string
{
$eol = Typecho_Http_Client::EOL;
$eol = Client::EOL;
$request = $this->method . ' ' . $this->path . ' ' . $this->rfc . $eol;
$request .= 'Host: ' . $this->host . $eol;
$request .= 'Accept: */*' . $eol;
@ -73,7 +72,7 @@ class Typecho_Http_Client_Adapter_Socket extends Typecho_Http_Client_Adapter
}
/** 发送POST信息 */
if (Typecho_Http_Client::METHOD_POST == $this->method) {
if (Client::METHOD_POST == $this->method) {
if (empty($this->files)) {
$content = is_array($this->data) ? http_build_query($this->data) : $this->data;
$request .= 'Content-Length: ' . strlen($content) . $eol;
@ -81,9 +80,6 @@ class Typecho_Http_Client_Adapter_Socket extends Typecho_Http_Client_Adapter
if (!isset($this->headers['content-type'])) {
$request .= 'Content-Type: application/x-www-form-urlencoded' . $eol;
}
$request .= $eol;
$request .= $content;
} else {
$boundary = '---------------------------' . substr(md5(uniqid()), 0, 16);
$content = $eol . $boundary;
@ -97,7 +93,8 @@ class Typecho_Http_Client_Adapter_Socket extends Typecho_Http_Client_Adapter
}
foreach ($this->files as $key => $file) {
$content .= $eol . 'Content-Disposition: form-data; name="' . $key . '"; filename="' . $file . '"' . $eol;
$content .= $eol . 'Content-Disposition: form-data; name="' . $key
. '"; filename="' . $file . '"' . $eol;
$content .= 'Content-Type: ' . mime_content_type($file) . $eol . $eol;
$content .= file_get_contents($file) . $eol;
$content .= $boundary;
@ -106,17 +103,18 @@ class Typecho_Http_Client_Adapter_Socket extends Typecho_Http_Client_Adapter
$content .= '--' . $eol;
$request .= 'Content-Length: ' . strlen($content) . $eol;
$request .= 'Content-Type: multipart/form-data; boundary=' . $boundary;
$request .= $eol;
$request .= $content;
}
$request .= $eol;
$request .= $content;
} else {
$request .= $eol;
}
/** 打开连接 */
$socket = @fsockopen($this->ip ? $this->ip : $this->host, $this->port, $errno, $errstr, $this->timeout);
$socket = @fsockopen($this->ip ?: $this->host, $this->port, $errno, $errstr, $this->timeout);
if (false === $socket) {
throw new Typecho_Http_Client_Exception($errno . ':' . $errstr, 500);
throw new Client\Exception($errno . ':' . $errstr, 500);
}
/** 发送数据 */
@ -133,15 +131,24 @@ class Typecho_Http_Client_Adapter_Socket extends Typecho_Http_Client_Adapter
//超时判断
if ($info['timed_out']) {
throw new Typecho_Http_Client_Exception(__CLASS__ . ': timeout reading from ' . $this->host . ':' . $this->port, 500);
throw new Client\Exception(
__CLASS__ . ': timeout reading from ' . $this->host . ':' . $this->port,
500
);
} else {
throw new Typecho_Http_Client_Exception(__CLASS__ . ': could not read from ' . $this->host . ':' . $this->port, 500);
throw new Client\Exception(
__CLASS__ . ': could not read from ' . $this->host . ':' . $this->port,
500
);
}
} elseif (strlen($buf) < 4096) {
$info = stream_get_meta_data($socket);
if ($info['timed_out']) {
throw new Typecho_Http_Client_Exception(__CLASS__ . ': timeout reading from ' . $this->host . ':' . $this->port, 500);
throw new Client\Exception(
__CLASS__ . ': timeout reading from ' . $this->host . ':' . $this->port,
500
);
}
}

View File

@ -1,17 +1,18 @@
<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
/**
* Typecho Blog Platform
*
* @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org)
* @license GNU General Public License 2.0
* @version $Id: DbException.php 52 2008-03-18 08:04:01Z magike.net $
*/
namespace Typecho\Http\Client;
use Typecho\Exception as TypechoException;
if (!defined('__TYPECHO_ROOT_DIR__')) {
exit;
}
/**
* Http客户端异常类
*
* @package Http
*/
class Typecho_Http_Client_Exception extends Typecho_Exception
{}
class Exception extends TypechoException
{
}

View File

@ -20,7 +20,7 @@ class Response
* @access private
* @var array
*/
private static $httpCode = [
private const HTTP_CODE = [
100 => 'Continue',
101 => 'Switching Protocols',
200 => 'OK',
@ -121,9 +121,9 @@ class Response
*/
public static function setStatus(int $code)
{
if (isset(self::$httpCode[$code])) {
if (isset(self::HTTP_CODE[$code])) {
header(($_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.1')
. ' ' . $code . ' ' . self::$httpCode[$code], true, $code);
. ' ' . $code . ' ' . self::HTTP_CODE[$code], true, $code);
}
}