e
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Abstracts;
|
||||
|
||||
|
||||
|
||||
use Swoole\Coroutine;
|
||||
|
||||
abstract class BaseContext
|
||||
{
|
||||
protected static array $pool = [];
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Abstracts;
|
||||
|
||||
|
||||
use Exception;
|
||||
use Kiri\Abstracts\Component;
|
||||
use Kiri\Kiri;
|
||||
|
||||
|
||||
/**
|
||||
* Class HttpService
|
||||
* @package Http\Abstracts
|
||||
*/
|
||||
abstract class HttpService extends Component
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param $message
|
||||
* @param string $category
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function write($message, string $category = 'app')
|
||||
{
|
||||
$logger = Kiri::app()->getLogger();
|
||||
$logger->write($message, $category);
|
||||
$logger->insert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __get($name): mixed
|
||||
{
|
||||
if (method_exists($this, $name)) {
|
||||
return $this->{$name}();
|
||||
}
|
||||
$handler = 'get' . ucfirst($name);
|
||||
if (method_exists($this, $handler)) {
|
||||
return $this->{$handler}();
|
||||
}
|
||||
if (property_exists($this, $name)) {
|
||||
return $this->$name;
|
||||
}
|
||||
$message = sprintf('method %s::%s not exists.', static::class, $name);
|
||||
throw new Exception($message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: whwyy
|
||||
* Date: 2018/5/24 0024
|
||||
* Time: 11:34
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client;
|
||||
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Swoole\Coroutine\Http\Client as SClient;
|
||||
|
||||
/**
|
||||
* Class Client
|
||||
* @package Kiri\Kiri\Http
|
||||
*/
|
||||
class Client extends ClientAbstracts
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param $path
|
||||
* @param array $params
|
||||
* @return array|string|Result
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request(string $method, $path, array $params = []): array|string|Result
|
||||
{
|
||||
return $this->setMethod($method)
|
||||
->coroutine(
|
||||
$this->matchHost($path),
|
||||
$this->paramEncode($params)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @param array|string $data
|
||||
* @return array|string|Result
|
||||
* @throws Exception 使用swoole协程方式请求
|
||||
*/
|
||||
private function coroutine($url, array|string $data = []): array|string|Result
|
||||
{
|
||||
try {
|
||||
$client = $this->generate_client($data, ...$url);
|
||||
$this->setData('');
|
||||
if ($client->statusCode < 0) {
|
||||
throw new Exception($client->errMsg);
|
||||
}
|
||||
$body = $this->resolve($client->getHeaders(), $client->body);
|
||||
if (in_array($client->getStatusCode(), [200, 201])) {
|
||||
return $this->structure($body, $data, $client->getHeaders());
|
||||
}
|
||||
if (is_string($body)) {
|
||||
$message = 'Request error code ' . $client->getStatusCode();
|
||||
} else {
|
||||
$message = $this->searchMessageByData($body);
|
||||
}
|
||||
return $this->fail($client->getStatusCode(), $message, $body, $client->getHeaders());
|
||||
} catch (\Throwable $exception) {
|
||||
$this->addError($exception, 'rpc');
|
||||
return $this->fail(500, $exception->getMessage(), [
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine()
|
||||
], []);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $host
|
||||
* @param $isHttps
|
||||
* @param $path
|
||||
* @return SClient
|
||||
*/
|
||||
private function generate_client($data, $host, $isHttps, $path): SClient
|
||||
{
|
||||
if ($isHttps || $this->isSSL()) {
|
||||
$client = new SClient($host, 443, true);
|
||||
} else {
|
||||
$client = new SClient($host, $this->getPort(), false);
|
||||
}
|
||||
$client->set($this->settings());
|
||||
if (!empty($this->getAgent())) {
|
||||
$this->addHeader('User-Agent', $this->getAgent());
|
||||
}
|
||||
$client->setHeaders($this->getHeader());
|
||||
$client->setMethod(strtoupper($this->getMethod()));
|
||||
$client->execute($this->setParams($client, $path, $data));
|
||||
$client->close();
|
||||
return $client;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param SClient $client
|
||||
* @param $path
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
private function setParams(SClient $client, $path, $data): string
|
||||
{
|
||||
if ($this->isGet()) {
|
||||
if (!empty($data)) $path .= '?' . $data;
|
||||
if (!empty($this->getData())) {
|
||||
$client->setData($this->getData());
|
||||
}
|
||||
} else {
|
||||
if (!empty($this->getData())) {
|
||||
$client->setData($this->getData());
|
||||
} else {
|
||||
$client->setData($this->mergeParams($data));
|
||||
}
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
#[Pure] private function settings(): array
|
||||
{
|
||||
$sslCert = $this->getSslCertFile();
|
||||
$sslKey = $this->getSslKeyFile();
|
||||
$sslCa = $this->getCa();
|
||||
|
||||
$params = [];
|
||||
if ($this->getConnectTimeout() > 0) {
|
||||
$params['timeout'] = $this->getConnectTimeout();
|
||||
}
|
||||
if (empty($sslCert) || empty($sslKey) || empty($sslCa)) {
|
||||
return $params;
|
||||
}
|
||||
|
||||
$params['ssl_host_name'] = $this->getHost();
|
||||
$params['ssl_cert_file'] = $this->getSslCertFile();
|
||||
$params['ssl_key_file'] = $this->getSslKeyFile();
|
||||
$params['ssl_verify_peer'] = true;
|
||||
$params['ssl_cafile'] = $sslCa;
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,806 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Http\Client;
|
||||
|
||||
|
||||
use Closure;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Kiri\Abstracts\Component;
|
||||
use Kiri\Core\Help;
|
||||
use Swoole\Coroutine\System;
|
||||
|
||||
defined('SPLIT_URL') or define('SPLIT_URL', '/(http[s]?:\/\/)?(([\w\-_]+\.)+\w+(:\d+)?)((\/[a-zA-Z0-9\-]+)+[\/]?(\?[a-zA-Z]+=.*)?)?/');
|
||||
|
||||
|
||||
/**
|
||||
* Class ClientAbstracts
|
||||
* @package Http\Client
|
||||
*/
|
||||
abstract class ClientAbstracts extends Component implements IClient
|
||||
{
|
||||
|
||||
const POST = 'post';
|
||||
const UPLOAD = 'upload';
|
||||
const GET = 'get';
|
||||
const DELETE = 'delete';
|
||||
const OPTIONS = 'options';
|
||||
const HEAD = 'head';
|
||||
const PUT = 'put';
|
||||
|
||||
private string $host = '';
|
||||
|
||||
private array $header = [];
|
||||
|
||||
private int $timeout = 0;
|
||||
|
||||
private ?Closure $callback = null;
|
||||
private string $method = 'get';
|
||||
|
||||
private bool $isSSL = false;
|
||||
private string $agent = '';
|
||||
private string $errorCodeField = '';
|
||||
private string $errorMsgField = '';
|
||||
private bool $use_swoole = false;
|
||||
|
||||
private string $ssl_cert_file = '';
|
||||
private string $ssl_key_file = '';
|
||||
private string $ca = '';
|
||||
private int $port = 80;
|
||||
|
||||
/** @var string $_message 错误信息 */
|
||||
private string $_message = '';
|
||||
private string $_data = '';
|
||||
|
||||
private int $connect_timeout = 1;
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
#[Pure] public static function NewRequest(): static
|
||||
{
|
||||
return new static();
|
||||
}
|
||||
|
||||
|
||||
protected function cleanData(): void
|
||||
{
|
||||
$this->_data = '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|int|string|Result
|
||||
*/
|
||||
public function post(string $path, array $params = []): array|int|string|Result
|
||||
{
|
||||
return $this->request(self::POST, $path, $params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|int|string|Result
|
||||
*/
|
||||
public function put(string $path, array $params = []): array|int|string|Result
|
||||
{
|
||||
return $this->request(self::PUT, $path, $params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $contentType
|
||||
*/
|
||||
public function setContentType(string $contentType): void
|
||||
{
|
||||
$this->header['Content-Type'] = $contentType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|int|string|Result
|
||||
*/
|
||||
public function head(string $path, array $params = []): array|int|string|Result
|
||||
{
|
||||
return $this->request(self::HEAD, $path, $params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|int|string|Result
|
||||
*/
|
||||
public function get(string $path, array $params = []): array|int|string|Result
|
||||
{
|
||||
return $this->request(self::GET, $path, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|int|string|Result
|
||||
*/
|
||||
public function option(string $path, array $params = []): array|int|string|Result
|
||||
{
|
||||
return $this->request(self::OPTIONS, $path, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|int|string|Result
|
||||
*/
|
||||
public function delete(string $path, array $params = []): array|int|string|Result
|
||||
{
|
||||
return $this->request(self::DELETE, $path, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|int|string|Result
|
||||
*/
|
||||
public function options(string $path, array $params = []): array|int|string|Result
|
||||
{
|
||||
return $this->request(self::OPTIONS, $path, $params);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|int|string|Result
|
||||
*/
|
||||
public function upload(string $path, array $params = []): array|int|string|Result
|
||||
{
|
||||
return $this->request(self::UPLOAD, $path, $params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
protected function getHostPort(): int
|
||||
{
|
||||
if (!empty($this->getPort())) {
|
||||
return $this->getPort();
|
||||
}
|
||||
$port = 80;
|
||||
if ($this->isSSL()) $port = 443;
|
||||
return $port;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
*/
|
||||
public function setHost(string $host): void
|
||||
{
|
||||
$this->host = $host;
|
||||
if ($this->use_swoole) {
|
||||
$this->host = System::gethostbyname($host);
|
||||
}
|
||||
$this->addHeader('Host', $host);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHeader(): array
|
||||
{
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $header
|
||||
*/
|
||||
public function setHeader(array $header): void
|
||||
{
|
||||
$this->header = $header;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $header
|
||||
* @return array
|
||||
*/
|
||||
public function setHeaders(array $header): array
|
||||
{
|
||||
if (empty($header)) {
|
||||
return [];
|
||||
}
|
||||
foreach ($header as $key => $val) {
|
||||
$this->header[$key] = $val;
|
||||
}
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $value
|
||||
*/
|
||||
public function addHeader($key, $value): void
|
||||
{
|
||||
$this->header[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTimeout(): int
|
||||
{
|
||||
return $this->timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setTimeout(int $value): void
|
||||
{
|
||||
$this->timeout = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Closure|null
|
||||
*/
|
||||
public function getCallback(): ?Closure
|
||||
{
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Closure|null $value
|
||||
*/
|
||||
public function setCallback(?Closure $value): void
|
||||
{
|
||||
$this->callback = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return static
|
||||
*/
|
||||
public function setMethod(string $value): static
|
||||
{
|
||||
$this->method = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSSL(): bool
|
||||
{
|
||||
return $this->isSSL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $isSSL
|
||||
*/
|
||||
public function setIsSSL(bool $isSSL): void
|
||||
{
|
||||
$this->isSSL = $isSSL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAgent(): string
|
||||
{
|
||||
return $this->agent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $agent
|
||||
*/
|
||||
public function setAgent(string $agent): void
|
||||
{
|
||||
$this->agent = $agent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorCodeField(): string
|
||||
{
|
||||
return $this->errorCodeField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $errorCodeField
|
||||
*/
|
||||
public function setErrorCodeField(string $errorCodeField): void
|
||||
{
|
||||
$this->errorCodeField = $errorCodeField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMsgField(): string
|
||||
{
|
||||
return $this->errorMsgField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $errorMsgField
|
||||
*/
|
||||
public function setErrorMsgField(string $errorMsgField): void
|
||||
{
|
||||
$this->errorMsgField = $errorMsgField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isUseSwoole(): bool
|
||||
{
|
||||
return $this->use_swoole;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $use_swoole
|
||||
*/
|
||||
public function setUseSwoole(bool $use_swoole): void
|
||||
{
|
||||
$this->use_swoole = $use_swoole;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSslCertFile(): string
|
||||
{
|
||||
return $this->ssl_cert_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ssl_cert_file
|
||||
*/
|
||||
public function setSslCertFile(string $ssl_cert_file): void
|
||||
{
|
||||
$this->ssl_cert_file = $ssl_cert_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSslKeyFile(): string
|
||||
{
|
||||
return $this->ssl_key_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ssl_key_file
|
||||
*/
|
||||
public function setSslKeyFile(string $ssl_key_file): void
|
||||
{
|
||||
$this->ssl_key_file = $ssl_key_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCa(): string
|
||||
{
|
||||
return $this->ca;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ssl_key_file
|
||||
*/
|
||||
public function setCa(string $ssl_key_file): void
|
||||
{
|
||||
$this->ca = $ssl_key_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPort(): int
|
||||
{
|
||||
if ($this->isSSL()) {
|
||||
return 443;
|
||||
}
|
||||
if (empty($this->port)) {
|
||||
return 80;
|
||||
}
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $port
|
||||
*/
|
||||
public function setPort(int $port): void
|
||||
{
|
||||
$this->port = $port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->_message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
*/
|
||||
public function setMessage(string $message): void
|
||||
{
|
||||
$this->_message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getData(): string
|
||||
{
|
||||
return $this->_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
*/
|
||||
public function setData(string $data): void
|
||||
{
|
||||
$this->_data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getConnectTimeout(): int
|
||||
{
|
||||
return $this->connect_timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $connect_timeout
|
||||
*/
|
||||
public function setConnectTimeout(int $connect_timeout): void
|
||||
{
|
||||
$this->connect_timeout = $connect_timeout;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $host
|
||||
* @return string|string[]
|
||||
*/
|
||||
protected function replaceHost($host): array|string
|
||||
{
|
||||
if ($this->isHttp($host)) {
|
||||
return str_replace('http://', '', $host);
|
||||
}
|
||||
if ($this->isHttps($host)) {
|
||||
return str_replace('https://', '', $host);
|
||||
}
|
||||
return $host;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @return false|int
|
||||
*/
|
||||
protected function checkIsIp($url): bool|int
|
||||
{
|
||||
return preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @return bool
|
||||
*/
|
||||
#[Pure] protected function isHttp($url): bool
|
||||
{
|
||||
return str_starts_with($url, 'http://');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @return bool
|
||||
*/
|
||||
#[Pure] protected function isHttps($url): bool
|
||||
{
|
||||
return str_starts_with($url, 'https://');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $newData
|
||||
* @return string
|
||||
*/
|
||||
protected function mergeParams($newData): string
|
||||
{
|
||||
if (!is_string($newData)) {
|
||||
return $this->toRequest($newData);
|
||||
}
|
||||
return $newData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
protected function toRequest($data): string
|
||||
{
|
||||
if (is_string($data)) {
|
||||
return $data;
|
||||
}
|
||||
$contentType = 'application/x-www-form-urlencoded';
|
||||
if (isset($this->header['Content-Type'])) {
|
||||
$contentType = $this->header['Content-Type'];
|
||||
} else if (isset($this->header['content-type'])) {
|
||||
$contentType = $this->header['content-type'];
|
||||
}
|
||||
if (str_contains($contentType, 'json')) {
|
||||
return Help::toJson($data);
|
||||
} else if (str_contains($contentType, 'xml')) {
|
||||
return Help::toXml($data);
|
||||
} else {
|
||||
return http_build_query($data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $body
|
||||
* @return array|string|null
|
||||
*/
|
||||
protected function resolve($data, $body): array|string|null
|
||||
{
|
||||
if (is_array($body)) {
|
||||
return $body;
|
||||
}
|
||||
$type = $data['content-type'] ?? $data['Content-Type'] ?? 'text/html';
|
||||
if (str_contains($type, 'text/html')) {
|
||||
return $body;
|
||||
} else if (str_contains($type, 'json')) {
|
||||
return json_decode($body, true);
|
||||
} else if (str_contains($type, 'xml')) {
|
||||
return Help::xmlToArray($body);
|
||||
} else if (str_contains($type, 'plain')) {
|
||||
return Help::toArray($body);
|
||||
}
|
||||
return $body;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $body
|
||||
* @param $_data
|
||||
* @param array $header
|
||||
* @param int $statusCode
|
||||
* @return mixed 构建返回体
|
||||
* 构建返回体
|
||||
*/
|
||||
protected function structure($body, $_data, $header = [], $statusCode = 200): mixed
|
||||
{
|
||||
if ($this->callback instanceof Closure) {
|
||||
$result = call_user_func($this->callback, $body, $_data, $header);
|
||||
} else {
|
||||
$result = $this->parseResult($body, $header, $statusCode);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $body
|
||||
* @param $header
|
||||
* @param $statusCode
|
||||
* @return Result
|
||||
*/
|
||||
private function parseResult($body, $header, $statusCode): Result
|
||||
{
|
||||
if (is_string($body)) {
|
||||
$result['code'] = 0;
|
||||
$result['message'] = '';
|
||||
} else {
|
||||
$result['code'] = $body[$this->errorCodeField] ?? 0;
|
||||
$result['message'] = $this->searchMessageByData($body);
|
||||
}
|
||||
$result['data'] = $body;
|
||||
$result['header'] = $header;
|
||||
$result['httpStatus'] = $statusCode;
|
||||
return new Result($result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $body
|
||||
* @return mixed
|
||||
*/
|
||||
protected function searchMessageByData($body): mixed
|
||||
{
|
||||
$parent = [];
|
||||
if (empty($this->errorMsgField)) {
|
||||
return 'system success.';
|
||||
}
|
||||
$explode = explode('.', $this->errorMsgField);
|
||||
if (!isset($body[$explode[0]])) {
|
||||
return 'system success.';
|
||||
}
|
||||
foreach ($explode as $item) {
|
||||
if (empty($item)) {
|
||||
continue;
|
||||
}
|
||||
if (empty($parent)) {
|
||||
$parent = $body[$item];
|
||||
continue;
|
||||
}
|
||||
if (is_string($parent) || !isset($parent[$item])) {
|
||||
break;
|
||||
}
|
||||
$parent = $parent[$item];
|
||||
}
|
||||
return !empty($parent) ? $parent : 'system success.';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* check isPost Request
|
||||
*/
|
||||
#[Pure] public function isPost(): bool
|
||||
{
|
||||
return strtolower($this->method) === self::POST;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* check isPost Request
|
||||
*/
|
||||
#[Pure] public function isUpload(): bool
|
||||
{
|
||||
return strtolower($this->method) === self::UPLOAD;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* check isGet Request
|
||||
*/
|
||||
#[Pure] public function isGet(): bool
|
||||
{
|
||||
return strtolower($this->method) === self::GET;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $arr
|
||||
*
|
||||
* @return array|string
|
||||
* 将请求参数进行编码
|
||||
*/
|
||||
#[Pure] protected function paramEncode($arr): array|string
|
||||
{
|
||||
if (!is_array($arr)) {
|
||||
return $arr;
|
||||
}
|
||||
$_tmp = [];
|
||||
foreach ($arr as $Key => $val) {
|
||||
$_tmp[$Key] = $val;
|
||||
}
|
||||
if ($this->isGet()) {
|
||||
return http_build_query($_tmp);
|
||||
}
|
||||
return $_tmp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
* @return array
|
||||
*/
|
||||
protected function matchHost(string $string): array
|
||||
{
|
||||
if (($parse = isUrl($string, true)) === false) {
|
||||
return $this->defaultString($string);
|
||||
}
|
||||
[$isHttps, $domain, $port, $path] = $parse;
|
||||
if (str_contains($domain, ':' . $port)) {
|
||||
$domain = str_replace(':' . $port, '', $domain);
|
||||
}
|
||||
$this->port = $isHttps ? 443 : $this->port;
|
||||
if (isIp($domain)) {
|
||||
$this->host = $domain;
|
||||
} else if ($this->isUseSwoole()) {
|
||||
$this->host = System::gethostbyname($domain) ?? $domain;
|
||||
} else {
|
||||
$this->host = $domain;
|
||||
}
|
||||
$this->header['Host'] = $domain;
|
||||
if (!str_starts_with($path, '/')) {
|
||||
$path = '/' . $path;
|
||||
}
|
||||
return [$this->host, $isHttps, $path];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return array
|
||||
*/
|
||||
private function defaultString($string): array
|
||||
{
|
||||
$host = $this->getHost();
|
||||
if ($string == '/') {
|
||||
$string = '';
|
||||
} else if (strpos($string, '/') !== 0) {
|
||||
$string = '/' . $string;
|
||||
}
|
||||
return [$host, $this->isSSL(), $string];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param $params
|
||||
* @return string
|
||||
*/
|
||||
#[Pure] protected function joinGetParams($path, $params): string
|
||||
{
|
||||
if (empty($params)) {
|
||||
return $path;
|
||||
}
|
||||
if (!is_string($params)) {
|
||||
$params = http_build_query($params);
|
||||
}
|
||||
if (str_contains($path, '?')) {
|
||||
[$path, $getParams] = explode('?', $path);
|
||||
}
|
||||
if (!isset($getParams) || empty($getParams)) {
|
||||
return $path . '?' . $params;
|
||||
}
|
||||
return $path . '?' . $params . '&' . $getParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $code
|
||||
* @param $message
|
||||
* @param $data
|
||||
* @param $header
|
||||
* @return Result
|
||||
*/
|
||||
protected function fail($code, $message, $data = [], $header = []): Result
|
||||
{
|
||||
return new Result([
|
||||
'code' => $code,
|
||||
'message' => $message,
|
||||
'data' => $data,
|
||||
'header' => $header,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client;
|
||||
|
||||
|
||||
use CurlHandle;
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
|
||||
/**
|
||||
* Class Curl
|
||||
* @package Http\Client
|
||||
*/
|
||||
class Curl extends ClientAbstracts
|
||||
{
|
||||
|
||||
/**
|
||||
* @param $method
|
||||
* @param $path
|
||||
* @param array $params
|
||||
* @return Result|array|string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $path, array $params = []): Result|array|string
|
||||
{
|
||||
if ($method == self::GET) {
|
||||
$path = $this->joinGetParams($path, $params);
|
||||
}
|
||||
return $this->execute($this->getCurlHandler($path, $method, $params));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param $method
|
||||
* @param $params
|
||||
* @return CurlHandle
|
||||
* @throws Exception
|
||||
*/
|
||||
private function getCurlHandler($path, $method, $params): CurlHandle
|
||||
{
|
||||
[$host, $isHttps, $path] = $this->matchHost($path);
|
||||
|
||||
$host = $isHttps ? 'https://' . $host : 'http://' . $host;
|
||||
if ($this->getPort() != 443 && $this->getPort() != 80) {
|
||||
$host .= ':' . $this->getPort();
|
||||
}
|
||||
|
||||
$resource = $this->do(curl_init($host . $path), $host . $path, $method);
|
||||
if ($isHttps !== false) {
|
||||
$this->curlHandlerSslSet($resource);
|
||||
}
|
||||
if (empty($params) && empty($this->getData())) {
|
||||
return $resource;
|
||||
}
|
||||
|
||||
if (!empty($this->getData())) {
|
||||
curl_setopt($resource, CURLOPT_POSTFIELDS, $this->getData());
|
||||
} else if ($method === self::POST) {
|
||||
curl_setopt($resource, CURLOPT_POSTFIELDS, $this->mergeParams($params));
|
||||
} else if ($method === self::UPLOAD) {
|
||||
curl_setopt($resource, CURLOPT_POSTFIELDS, $params);
|
||||
}
|
||||
return $resource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $resource
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
private function curlHandlerSslSet($resource): void
|
||||
{
|
||||
if (!empty($this->ssl_key)) {
|
||||
if (!file_exists($this->ssl_key)) {
|
||||
throw new Exception('SSL protocol certificate not found.');
|
||||
}
|
||||
curl_setopt($resource, CURLOPT_SSLKEY, $this->getSslKeyFile());
|
||||
}
|
||||
if (!empty($this->ssl_cert)) {
|
||||
if (!!file_exists($this->ssl_cert)) {
|
||||
throw new Exception('SSL protocol certificate not found.');
|
||||
}
|
||||
curl_setopt($resource, CURLOPT_SSLCERT, $this->getSslCertFile());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $resource
|
||||
* @param $path
|
||||
* @param $method
|
||||
* @return CurlHandle
|
||||
* @throws Exception
|
||||
*/
|
||||
private function do($resource, $path, $method): CurlHandle
|
||||
{
|
||||
curl_setopt($resource, CURLOPT_URL, $path);
|
||||
curl_setopt($resource, CURLOPT_TIMEOUT, $this->getTimeout()); // 超时设置
|
||||
curl_setopt($resource, CURLOPT_CONNECTTIMEOUT, $this->getConnectTimeout()); // 超时设置
|
||||
|
||||
curl_setopt($resource, CURLOPT_HEADER, true);
|
||||
curl_setopt($resource, CURLOPT_FAILONERROR, true);
|
||||
|
||||
curl_setopt($resource, CURLOPT_HTTPHEADER, $this->parseHeaderMat());
|
||||
if (defined('CURLOPT_SSL_FALSESTART')) {
|
||||
curl_setopt($resource, CURLOPT_SSL_FALSESTART, true);
|
||||
}
|
||||
curl_setopt($resource, CURLOPT_FORBID_REUSE, false);
|
||||
curl_setopt($resource, CURLOPT_FRESH_CONNECT, false);
|
||||
|
||||
if (!empty($this->getAgent())) {
|
||||
curl_setopt($resource, CURLOPT_USERAGENT, $this->getAgent());
|
||||
}
|
||||
|
||||
curl_setopt($resource, CURLOPT_NOBODY, FALSE);
|
||||
curl_setopt($resource, CURLOPT_RETURNTRANSFER, TRUE);//返回内容
|
||||
curl_setopt($resource, CURLOPT_FOLLOWLOCATION, TRUE);// 跟踪重定向
|
||||
curl_setopt($resource, CURLOPT_ENCODING, 'gzip,deflate');
|
||||
if ($method === self::POST || $method == self::UPLOAD) {
|
||||
curl_setopt($resource, CURLOPT_POST, 1);
|
||||
}
|
||||
curl_setopt($resource, CURLOPT_CUSTOMREQUEST, strtoupper($method));
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $curl
|
||||
* @return Result|bool|array|string
|
||||
* @throws Exception
|
||||
*/
|
||||
private function execute($curl): Result|bool|array|string
|
||||
{
|
||||
defer(fn() => $this->cleanData());
|
||||
$output = curl_exec($curl);
|
||||
if ($output === false) {
|
||||
return $this->fail(400, curl_error($curl));
|
||||
}
|
||||
return $this->parseResponse($curl, $output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $curl
|
||||
* @param $output
|
||||
* @param array $params
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
private function parseResponse($curl, $output, array $params = []): mixed
|
||||
{
|
||||
curl_close($curl);
|
||||
if ($output === FALSE) {
|
||||
return $this->fail(500, $output);
|
||||
}
|
||||
[$header, $body, $status] = $this->explode($output);
|
||||
if ($status != 200 && $status != 201) {
|
||||
$data = $this->fail($status, $body, [], $header);
|
||||
} else {
|
||||
$data = $this->structure($body, $params, $header);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $output
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
private function explode($output): array
|
||||
{
|
||||
if (empty($output) || !str_contains($output, "\r\n\r\n")) {
|
||||
throw new Exception('Get data null.');
|
||||
}
|
||||
|
||||
[$header, $body] = explode("\r\n\r\n", $output, 2);
|
||||
if ($header == 'HTTP/1.1 100 Continue') {
|
||||
[$header, $body] = explode("\r\n\r\n", $body, 2);
|
||||
}
|
||||
|
||||
$header = explode("\r\n", $header);
|
||||
|
||||
$status = (int)explode(' ', trim($header[0]))[1];
|
||||
$header = $this->headerFormat($header);
|
||||
|
||||
return [$header, $this->resolve($header, $body), $status];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $headers
|
||||
* @return array
|
||||
*/
|
||||
private function headerFormat($headers): array
|
||||
{
|
||||
$_tmp = [];
|
||||
foreach ($headers as $val) {
|
||||
$trim = explode(': ', trim($val));
|
||||
|
||||
$_tmp[strtolower($trim[0])] = $trim[1] ?? '';
|
||||
}
|
||||
return $_tmp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
#[Pure] private function parseHeaderMat(): array
|
||||
{
|
||||
$headers = [];
|
||||
foreach ($this->getHeader() as $key => $val) {
|
||||
$headers[$key] = $key . ': ' . $val;
|
||||
}
|
||||
return array_values($headers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Http\Client;
|
||||
|
||||
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Kiri\Abstracts\Component;
|
||||
use ReflectionException;
|
||||
use Swoole\Coroutine;
|
||||
|
||||
/**
|
||||
* Class ClientDriver
|
||||
* @package Http\Client
|
||||
* @mixin Client
|
||||
*/
|
||||
class HttpClient extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* @return IClient
|
||||
*/
|
||||
public static function NewRequest(): IClient
|
||||
{
|
||||
if (Coroutine::getCid() > -1) {
|
||||
return Client::NewRequest();
|
||||
}
|
||||
return Curl::NewRequest();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Curl
|
||||
*/
|
||||
#[Pure] public function getCurl(): Curl
|
||||
{
|
||||
return Curl::NewRequest();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Client
|
||||
*/
|
||||
#[Pure] public function getCoroutine(): Client
|
||||
{
|
||||
return Client::NewRequest();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return void
|
||||
*/
|
||||
public function __call(string $name, array $arguments)
|
||||
{
|
||||
if (!method_exists($this, $name)) {
|
||||
return static::NewRequest()->{$name}(...$arguments);
|
||||
}
|
||||
return $this->{$name}(...$arguments);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class HttpParse
|
||||
* @package BeReborn\Http
|
||||
*/
|
||||
class HttpParse
|
||||
{
|
||||
/**
|
||||
* @param mixed ...$object
|
||||
* @return string
|
||||
*/
|
||||
private static function getKey(...$object): string
|
||||
{
|
||||
$first = '';
|
||||
$tp = [];
|
||||
foreach ($object as $key => $value) {
|
||||
if ($value === null) {
|
||||
continue;
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$value = key($value);
|
||||
}
|
||||
if ($first === '') {
|
||||
$first = $value;
|
||||
} else {
|
||||
$tp[] = $value;
|
||||
}
|
||||
}
|
||||
$key = $first . '[' . implode('][', $tp) . ']';
|
||||
if (count($tp) < 1) {
|
||||
$key = $first;
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function parse($data): string
|
||||
{
|
||||
$tmp = [];
|
||||
if (is_string($data)) {
|
||||
return $data;
|
||||
}
|
||||
foreach ($data as $key => $datum) {
|
||||
if ($datum === null) {
|
||||
continue;
|
||||
}
|
||||
$tmp[] = static::ifElse($key, $datum);
|
||||
}
|
||||
return implode('&', $tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $t
|
||||
* @param $qt
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function ifElse($t, $qt): string
|
||||
{
|
||||
if (is_numeric($qt)) {
|
||||
return $t . '=' . $qt;
|
||||
}
|
||||
if (is_string($qt)) {
|
||||
$string = $t . '=' . urlencode($qt);
|
||||
} else {
|
||||
$string = static::encode($t, $qt);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed ...$object
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function encode(...$object): string
|
||||
{
|
||||
$ret = [];
|
||||
|
||||
$data = $object[count($object) - 1];
|
||||
$key = static::getKey(...$object);
|
||||
foreach ($data as $s => $datum) {
|
||||
if (is_array($datum)) {
|
||||
$object[count($object) - 1] = $s;
|
||||
$object[] = $datum;
|
||||
$string = static::encode(...$object);
|
||||
} else {
|
||||
if (is_object($datum)) {
|
||||
throw new Exception('Http body con\'t object.');
|
||||
}
|
||||
$string = $key . '=' . urlencode($datum);
|
||||
}
|
||||
$ret[] = $string;
|
||||
}
|
||||
return implode('&', $ret);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Http\Client;
|
||||
|
||||
|
||||
use Closure;
|
||||
|
||||
interface IClient
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|Result|int|string
|
||||
*/
|
||||
public function get(string $path, array $params = []): Result|int|array|string;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|Result|int|string
|
||||
*/
|
||||
public function post(string $path, array $params = []): Result|int|array|string;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|Result|int|string
|
||||
*/
|
||||
public function delete(string $path, array $params = []): Result|int|array|string;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|Result|int|string
|
||||
*/
|
||||
public function options(string $path, array $params = []): Result|int|array|string;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|Result|int|string
|
||||
*/
|
||||
public function upload(string $path, array $params = []): Result|int|array|string;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|Result|int|string
|
||||
*/
|
||||
public function put(string $path, array $params = []): Result|int|array|string;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|Result|int|string
|
||||
*/
|
||||
public function head(string $path, array $params = []): Result|int|array|string;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $path
|
||||
* @param array $params
|
||||
* @return array|Result|int|string
|
||||
*/
|
||||
public function request(string $method, string $path, array $params = []): Result|array|int|string;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
* @return mixed
|
||||
*/
|
||||
public function setHost(string $host): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param array $header
|
||||
* @return mixed
|
||||
*/
|
||||
public function setHeader(array $header): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param array $header
|
||||
* @return mixed
|
||||
*/
|
||||
public function setHeaders(array $header): array;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function addHeader(string $key, string $value): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function setTimeout(int $value): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param Closure|null $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function setCallback(?Closure $value): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return static
|
||||
*/
|
||||
public function setMethod(string $value): static;
|
||||
|
||||
|
||||
/**
|
||||
* @param bool $isSSL
|
||||
* @return mixed
|
||||
*/
|
||||
public function setIsSSL(bool $isSSL): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $agent
|
||||
* @return mixed
|
||||
*/
|
||||
public function setAgent(string $agent): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $errorCodeField
|
||||
* @return mixed
|
||||
*/
|
||||
public function setErrorCodeField(string $errorCodeField): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $errorMsgField
|
||||
* @return mixed
|
||||
*/
|
||||
public function setErrorMsgField(string $errorMsgField): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param bool $use_swoole
|
||||
* @return mixed
|
||||
*/
|
||||
public function setUseSwoole(bool $use_swoole): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $ssl_cert_file
|
||||
* @return mixed
|
||||
*/
|
||||
public function setSslCertFile(string $ssl_cert_file): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $ssl_key_file
|
||||
* @return mixed
|
||||
*/
|
||||
public function setSslKeyFile(string $ssl_key_file): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $ssl_key_file
|
||||
* @return mixed
|
||||
*/
|
||||
public function setCa(string $ssl_key_file): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param int $port
|
||||
* @return mixed
|
||||
*/
|
||||
public function setPort(int $port): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @return mixed
|
||||
*/
|
||||
public function setMessage(string $message): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function setData(string $data): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param int $connect_timeout
|
||||
* @return mixed
|
||||
*/
|
||||
public function setConnectTimeout(int $connect_timeout): void;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $contentType
|
||||
* @return mixed
|
||||
*/
|
||||
public function setContentType(string $contentType): void;
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Client;
|
||||
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
|
||||
|
||||
/**
|
||||
* Class Result
|
||||
*
|
||||
* @package app\components
|
||||
*
|
||||
* @property $code
|
||||
* @property $message
|
||||
* @property $count
|
||||
* @property $data
|
||||
*/
|
||||
class Result
|
||||
{
|
||||
public int|string $code;
|
||||
public string $message;
|
||||
public int $count = 0;
|
||||
|
||||
public mixed $data;
|
||||
public ?array $header;
|
||||
public int $httpStatus = 200;
|
||||
|
||||
public int $startTime = 0;
|
||||
public int $requestTime = 0;
|
||||
public float $runTime = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Result constructor.
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->setAssignment($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return $this
|
||||
*/
|
||||
public function setAssignment($data): static
|
||||
{
|
||||
foreach ($data as $key => $val) {
|
||||
if (!property_exists($this, $key)) {
|
||||
continue;
|
||||
}
|
||||
$this->$key = $val;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name): mixed
|
||||
{
|
||||
return $this->$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $value
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->$name = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders(): array
|
||||
{
|
||||
$_tmp = [];
|
||||
if (!is_array($this->header)) {
|
||||
return $_tmp;
|
||||
}
|
||||
foreach ($this->header as $key => $val) {
|
||||
if ($key == 0) {
|
||||
$_tmp['pro'] = $val;
|
||||
} else {
|
||||
if (str_contains($val, ': ')) {
|
||||
$trim = explode(': ', $val);
|
||||
|
||||
$_tmp[strtolower($trim[0])] = $trim[1];
|
||||
} else {
|
||||
$_tmp[$key] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $_tmp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTime(): array
|
||||
{
|
||||
return [
|
||||
'startTime' => $this->startTime,
|
||||
'requestTime' => $this->requestTime,
|
||||
'runTime' => $this->runTime,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $data
|
||||
* @return $this
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setAttr($key, $data): static
|
||||
{
|
||||
if (!property_exists($this, $key)) {
|
||||
throw new Exception('未查找到相应对象属性');
|
||||
}
|
||||
$this->$key = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
* @return bool
|
||||
*/
|
||||
#[Pure] public function isResultsOK(int $status = 0): bool
|
||||
{
|
||||
if (!$this->httpIsOk()) {
|
||||
return false;
|
||||
}
|
||||
return $this->code === $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function httpIsOk(): bool
|
||||
{
|
||||
return in_array($this->httpStatus, [100, 101, 200, 201, 202, 203, 204, 205, 206]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBody(): mixed
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|int
|
||||
*/
|
||||
public function getCode(): string|int
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http;
|
||||
|
||||
|
||||
use Exception;
|
||||
use Kiri\Abstracts\Input;
|
||||
use Kiri\Exception\ConfigException;
|
||||
use Kiri\Kiri;
|
||||
|
||||
/**
|
||||
* Class Command
|
||||
* @package Http
|
||||
*/
|
||||
class Command extends \Console\Command
|
||||
{
|
||||
|
||||
public string $command = 'sw:server';
|
||||
|
||||
|
||||
public string $description = 'server start|stop|reload|restart';
|
||||
|
||||
|
||||
const ACTIONS = ['start', 'stop', 'restart'];
|
||||
|
||||
|
||||
/**
|
||||
* @param Input $dtl
|
||||
* @return string
|
||||
* @throws Exception
|
||||
* @throws ConfigException
|
||||
*/
|
||||
public function onHandler(Input $dtl): string
|
||||
{
|
||||
$manager = Kiri::app()->getServer();
|
||||
$manager->setDaemon($dtl->get('daemon', 0));
|
||||
if (!in_array($dtl->get('action'), self::ACTIONS)) {
|
||||
return 'I don\'t know what I want to do.';
|
||||
}
|
||||
|
||||
/** @var Shutdown $shutdown */
|
||||
$shutdown = Kiri::app()->get('shutdown');
|
||||
if ($shutdown->isRunning() && $dtl->get('action') == 'start') {
|
||||
return 'Service is running. Please use restart.';
|
||||
}
|
||||
$shutdown->shutdown();
|
||||
if ($dtl->get('action') == 'stop') {
|
||||
return 'shutdown success.';
|
||||
}
|
||||
|
||||
$this->generate_runtime_builder();
|
||||
return $manager->start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function generate_runtime_builder()
|
||||
{
|
||||
exec(PHP_BINARY . ' ' . APP_PATH . 'snowflake runtime:builder');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http;
|
||||
|
||||
|
||||
use Annotation\Inject;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Kiri\Abstracts\TraitApplication;
|
||||
use Kiri\Application;
|
||||
use Kiri\Di\Container;
|
||||
use Kiri\Di\ContainerInterface;
|
||||
use Kiri\Kiri;
|
||||
use Server\RequestInterface;
|
||||
use Server\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Class WebController
|
||||
* @package Kiri\Kiri\Web
|
||||
* @property Application $container
|
||||
*/
|
||||
class Controller
|
||||
{
|
||||
|
||||
use TraitApplication;
|
||||
|
||||
|
||||
/**
|
||||
* @param Application $application
|
||||
*/
|
||||
#[Pure] public function __construct(protected Application $application)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* inject di container
|
||||
*
|
||||
* @var ContainerInterface|null
|
||||
*/
|
||||
#[Inject(ContainerInterface::class)]
|
||||
public ?ContainerInterface $container = null;
|
||||
|
||||
|
||||
/**
|
||||
* inject request
|
||||
*
|
||||
* @var RequestInterface|null
|
||||
*/
|
||||
#[Inject(RequestInterface::class)]
|
||||
public ?RequestInterface $request = null;
|
||||
|
||||
|
||||
/**
|
||||
* inject response
|
||||
*
|
||||
* @var ResponseInterface|null
|
||||
*/
|
||||
#[Inject(ResponseInterface::class)]
|
||||
public ?ResponseInterface $response = null;
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: whwyy
|
||||
* Date: 2018/5/25 0025
|
||||
* Time: 10:14
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
namespace Http\Exception;
|
||||
|
||||
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class AuthException
|
||||
* @package Kiri\Kiri\Exception
|
||||
*/
|
||||
class AuthException extends \Exception
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* AuthException constructor.
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = NULL)
|
||||
{
|
||||
parent::__construct($message, 7000, $previous);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Exception;
|
||||
|
||||
|
||||
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class ExitException
|
||||
* @package Http\Exception
|
||||
*/
|
||||
class ExitException extends \Exception
|
||||
{
|
||||
|
||||
/**
|
||||
* ExitException constructor.
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Throwable|null $previous
|
||||
*/
|
||||
#[Pure] public function __construct($message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Exception;
|
||||
|
||||
|
||||
/**
|
||||
* Class RequestException
|
||||
* @package Http\Exception
|
||||
*/
|
||||
class RequestException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Http\Http;
|
||||
|
||||
use Http\Abstracts\BaseContext;
|
||||
use Swoole\Coroutine;
|
||||
|
||||
/**
|
||||
* Class Context
|
||||
* @package Yoc\http
|
||||
*/
|
||||
class Context extends BaseContext
|
||||
{
|
||||
|
||||
protected static array $_contents = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param $context
|
||||
* @param null $coroutineId
|
||||
* @return mixed
|
||||
*/
|
||||
public static function setContext($id, $context, $coroutineId = null): mixed
|
||||
{
|
||||
if (Coroutine::getCid() === -1) {
|
||||
return static::$_contents[$id] = $context;
|
||||
}
|
||||
return Coroutine::getContext($coroutineId)[$id] = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param int $value
|
||||
* @param null $coroutineId
|
||||
* @return bool|int
|
||||
*/
|
||||
public static function increment($id, int $value = 1, $coroutineId = null): bool|int
|
||||
{
|
||||
if (!isset(Coroutine::getContext($coroutineId)[$id])) {
|
||||
Coroutine::getContext($coroutineId)[$id] = 0;
|
||||
}
|
||||
return Coroutine::getContext($coroutineId)[$id] += $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param int $value
|
||||
* @param null $coroutineId
|
||||
* @return bool|int
|
||||
*/
|
||||
public static function decrement($id, int $value = 1, $coroutineId = null): bool|int
|
||||
{
|
||||
if (!isset(Coroutine::getContext($coroutineId)[$id])) {
|
||||
Coroutine::getContext($coroutineId)[$id] = 0;
|
||||
}
|
||||
return Coroutine::getContext($coroutineId)[$id] -= $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param null $default
|
||||
* @param null $coroutineId
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getContext($id, $default = null, $coroutineId = null): mixed
|
||||
{
|
||||
if (Coroutine::getCid() === -1) {
|
||||
return static::loadByStatic($id, $default);
|
||||
}
|
||||
return static::loadByContext($id, $default, $coroutineId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param null $default
|
||||
* @param null $coroutineId
|
||||
* @return mixed
|
||||
*/
|
||||
private static function loadByContext($id, $default = null, $coroutineId = null): mixed
|
||||
{
|
||||
$data = Coroutine::getContext($coroutineId)[$id] ?? null;
|
||||
if ($data === null) {
|
||||
return $default;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
private static function loadByStatic($id, $default = null): mixed
|
||||
{
|
||||
$data = static::$_contents[$id] ?? null;
|
||||
if ($data === null) {
|
||||
return $default;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param null $coroutineId
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getAllContext($coroutineId = null): mixed
|
||||
{
|
||||
if (Coroutine::getCid() === -1) {
|
||||
return Coroutine::getContext($coroutineId) ?? [];
|
||||
} else {
|
||||
return static::$_contents ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @param null $coroutineId
|
||||
*/
|
||||
public static function remove(string $id, $coroutineId = null)
|
||||
{
|
||||
if (!static::hasContext($id, $coroutineId)) {
|
||||
return;
|
||||
}
|
||||
if (Coroutine::getCid() === -1) {
|
||||
unset(static::$_contents[$id]);
|
||||
} else {
|
||||
unset(Coroutine::getContext($coroutineId)[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param null $key
|
||||
* @param null $coroutineId
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasContext($id, $key = null, $coroutineId = null): bool
|
||||
{
|
||||
if (Coroutine::getCid() === -1) {
|
||||
return static::searchByStatic($id, $key);
|
||||
}
|
||||
return static::searchByCoroutine($id, $key, $coroutineId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param null $key
|
||||
* @return bool
|
||||
*/
|
||||
private static function searchByStatic($id, $key = null): bool
|
||||
{
|
||||
if (!isset(static::$_contents[$id])) {
|
||||
return false;
|
||||
}
|
||||
if (!empty($key) && !isset(static::$_contents[$id][$key])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @param null $key
|
||||
* @param null $coroutineId
|
||||
* @return bool
|
||||
*/
|
||||
private static function searchByCoroutine($id, $key = null, $coroutineId = null): bool
|
||||
{
|
||||
if (!isset(Coroutine::getContext($coroutineId)[$id])) {
|
||||
return false;
|
||||
}
|
||||
if ($key !== null) {
|
||||
return isset((Coroutine::getContext($coroutineId)[$id] ?? [])[$key]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function inCoroutine(): bool
|
||||
{
|
||||
return Coroutine::getCid() !== -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Http;
|
||||
|
||||
use Exception;
|
||||
use Kiri\Kiri;
|
||||
|
||||
/**
|
||||
* Class File
|
||||
*/
|
||||
class File
|
||||
{
|
||||
|
||||
public string $name = '';
|
||||
public mixed $tmp_name = '';
|
||||
public mixed $error = '';
|
||||
public mixed $type = '';
|
||||
public mixed $size = '';
|
||||
|
||||
|
||||
private string $_content = '';
|
||||
|
||||
private string $newName = '';
|
||||
private array $errorInfo = [
|
||||
0 => 'UPLOAD_ERR_OK.',
|
||||
1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini.',
|
||||
2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
|
||||
3 => 'The uploaded file was only partially uploaded.',
|
||||
4 => 'No file was uploaded.',
|
||||
6 => 'Missing a temporary folder.',
|
||||
7 => 'Failed to write file to disk.',
|
||||
8 => 'A PHP extension stopped the file upload.'
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function saveTo(string $path): bool
|
||||
{
|
||||
if ($this->hasError()) {
|
||||
throw new Exception($this->getErrorInfo());
|
||||
}
|
||||
|
||||
@move_uploaded_file($this->tmp_name, $path);
|
||||
if (!file_exists($path)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function rename(): string
|
||||
{
|
||||
if (!empty($this->newName)) {
|
||||
return $this->newName;
|
||||
}
|
||||
|
||||
if (!file_exists($this->getTmpPath())) {
|
||||
throw new Exception('(' . $this->name . ')Failed to open stream: No such file or directory');
|
||||
}
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getContent(): string
|
||||
{
|
||||
$open = fopen($this->getTmpPath(), 'r');
|
||||
|
||||
// @move_uploaded_file($this->tmp_name, storage($this->name));
|
||||
|
||||
$limit = 1024000;
|
||||
|
||||
$stat = fstat($open);
|
||||
|
||||
$sleep = $offset = 0;
|
||||
$content = '';
|
||||
while ($file = fread($open, $limit)) {
|
||||
$content .= $file;
|
||||
fseek($open, $offset);
|
||||
if ($sleep > 0) {
|
||||
sleep($sleep);
|
||||
}
|
||||
if ($offset >= $stat['size']) {
|
||||
break;
|
||||
}
|
||||
$offset += $limit;
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTmpPath(): string
|
||||
{
|
||||
return $this->tmp_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* check file have error
|
||||
*/
|
||||
public function hasError(): bool
|
||||
{
|
||||
return $this->error !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*
|
||||
* get upload error info
|
||||
*/
|
||||
public function getErrorInfo(): mixed
|
||||
{
|
||||
if (!isset($this->errorInfo[$this->error])) {
|
||||
return 'Unknown upload error.';
|
||||
}
|
||||
return $this->errorInfo[$this->error];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Http\Http\Formatter;
|
||||
|
||||
use Exception;
|
||||
use Http\Abstracts\HttpService;
|
||||
use Http\IInterface\IFormatter;
|
||||
use Swoole\Http\Response;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class FileFormatter extends HttpService implements IFormatter
|
||||
{
|
||||
|
||||
public mixed $data;
|
||||
|
||||
/** @var Response */
|
||||
public Response $status;
|
||||
|
||||
public array $header = [];
|
||||
|
||||
/**
|
||||
* @param $context
|
||||
* @return $this
|
||||
* @throws Exception
|
||||
*/
|
||||
public function send($context): static
|
||||
{
|
||||
$this->data = $context;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData(): mixed
|
||||
{
|
||||
$data = $this->data;
|
||||
$this->clear();
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->data = null;
|
||||
unset($this->data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: whwyy
|
||||
* Date: 2018/4/8 0008
|
||||
* Time: 17:51
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Http\Formatter;
|
||||
|
||||
|
||||
use Exception;
|
||||
use Http\Abstracts\HttpService;
|
||||
use Kiri\Core\Json;
|
||||
use Swoole\Http\Response;
|
||||
use Http\IInterface\IFormatter;
|
||||
|
||||
/**
|
||||
* Class HtmlFormatter
|
||||
* @package Kiri\Kiri\Http\Formatter
|
||||
*/
|
||||
class HtmlFormatter extends HttpService implements IFormatter
|
||||
{
|
||||
|
||||
public mixed $data;
|
||||
|
||||
/** @var Response */
|
||||
public Response $status;
|
||||
|
||||
public array $header = [];
|
||||
|
||||
/**
|
||||
* @param $context
|
||||
* @return $this
|
||||
* @throws Exception
|
||||
*/
|
||||
public function send($context): static
|
||||
{
|
||||
if (!is_string($context)) {
|
||||
$context = Json::encode($context);
|
||||
}
|
||||
$this->data = $context;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData(): mixed
|
||||
{
|
||||
$data = $this->data;
|
||||
$this->clear();
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->data = null;
|
||||
unset($this->data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: whwyy
|
||||
* Date: 2018/4/8 0008
|
||||
* Time: 17:18
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Http\Formatter;
|
||||
|
||||
use Http\Abstracts\HttpService;
|
||||
use Http\IInterface\IFormatter;
|
||||
|
||||
/**
|
||||
* Class JsonFormatter
|
||||
* @package Kiri\Kiri\Http\Formatter
|
||||
*/
|
||||
class JsonFormatter extends HttpService implements IFormatter
|
||||
{
|
||||
public mixed $data;
|
||||
|
||||
public int $status = 200;
|
||||
|
||||
public array $header = [];
|
||||
|
||||
/**
|
||||
* @param $context
|
||||
* @return JsonFormatter
|
||||
*/
|
||||
public function send($context): static
|
||||
{
|
||||
if (!is_string($context)) {
|
||||
$context = json_encode($context);
|
||||
}
|
||||
$this->data = $context;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData(): mixed
|
||||
{
|
||||
$data = $this->data;
|
||||
$this->clear();
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->data = null;
|
||||
unset($this->data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: whwyy
|
||||
* Date: 2018/4/8 0008
|
||||
* Time: 17:29
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Http\Formatter;
|
||||
|
||||
|
||||
use Exception;
|
||||
use Http\Abstracts\HttpService;
|
||||
use SimpleXMLElement;
|
||||
use Swoole\Http\Response;
|
||||
use Http\IInterface\IFormatter;
|
||||
|
||||
|
||||
/**
|
||||
* Class XmlFormatter
|
||||
* @package Kiri\Kiri\Http\Formatter
|
||||
*/
|
||||
class XmlFormatter extends HttpService implements IFormatter
|
||||
{
|
||||
|
||||
public ?string $data = '';
|
||||
|
||||
/** @var Response */
|
||||
public Response $status;
|
||||
|
||||
public array $header = [];
|
||||
|
||||
/**
|
||||
* @param $context
|
||||
* @return $this
|
||||
* @throws Exception
|
||||
*/
|
||||
public function send($context): static
|
||||
{
|
||||
if (!is_string($context)) {
|
||||
// TODO: Implement send() method.
|
||||
$dom = new SimpleXMLElement('<xml/>');
|
||||
|
||||
$this->toXml($dom, $context);
|
||||
|
||||
$this->data = $dom->saveXML();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getData(): ?string
|
||||
{
|
||||
$data = $this->data;
|
||||
$this->clear();
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SimpleXMLElement $dom
|
||||
* @param $data
|
||||
*/
|
||||
public function toXml(SimpleXMLElement $dom, $data)
|
||||
{
|
||||
foreach ($data as $key => $val) {
|
||||
if (is_numeric($key)) {
|
||||
$key = 'item' . $key;
|
||||
}
|
||||
if (is_array($val)) {
|
||||
$node = $dom->addChild($key);
|
||||
$this->toXml($node, $val);
|
||||
} else if (is_object($val)) {
|
||||
$val = get_object_vars($val);
|
||||
$node = $dom->addChild($key);
|
||||
$this->toXml($node, $val);
|
||||
} else {
|
||||
$dom->addChild($key, htmlspecialchars((string)$val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->data = null;
|
||||
unset($this->data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: admin
|
||||
* Date: 2019-03-18
|
||||
* Time: 14:54
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Http;
|
||||
|
||||
/**
|
||||
* Class HttpHeaders
|
||||
* @package Kiri\Kiri\Http
|
||||
*/
|
||||
trait HttpHeaders
|
||||
{
|
||||
|
||||
private array $_headers = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param array $headers
|
||||
*/
|
||||
public function setHeaders(array $headers): void
|
||||
{
|
||||
$this->_headers = $headers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->_headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getHeader($name, $default = null): mixed
|
||||
{
|
||||
return $this->_headers[$name] ?? $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function header($name, $default = null): mixed
|
||||
{
|
||||
return $this->getHeader($name, $default);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType(): string
|
||||
{
|
||||
return $this->_headers['content-type'] ?? '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRequestUri(): ?string
|
||||
{
|
||||
return $this->_headers['request_uri'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRequestMethod(): ?string
|
||||
{
|
||||
return $this->_headers['request_method'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAgent(): mixed
|
||||
{
|
||||
return $this->_headers['user-agent'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($name): bool
|
||||
{
|
||||
return ($this->_headers[$name] ?? null) === null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return $this->_headers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: admin
|
||||
* Date: 2019-03-18
|
||||
* Time: 14:54
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Http;
|
||||
|
||||
use Exception;
|
||||
use Http\Exception\RequestException;
|
||||
use Kiri\Core\Json;
|
||||
use Kiri\Core\Xml;
|
||||
use Kiri\Exception\NotFindClassException;
|
||||
use Kiri\Kiri;
|
||||
use ReflectionException;
|
||||
|
||||
/**
|
||||
* Class HttpParams
|
||||
* @package Kiri\Kiri\Http
|
||||
*/
|
||||
trait HttpParams
|
||||
{
|
||||
|
||||
/** @var array|null */
|
||||
private ?array $_gets = [];
|
||||
|
||||
|
||||
/** @var mixed */
|
||||
private mixed $_posts = [];
|
||||
|
||||
|
||||
/** @var array|null */
|
||||
private ?array $_files = [];
|
||||
|
||||
|
||||
private mixed $_rawContent = '';
|
||||
|
||||
/**
|
||||
* @param array|null $gets
|
||||
*/
|
||||
public function setGets(?array $gets): void
|
||||
{
|
||||
$this->_gets = $gets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $posts
|
||||
*/
|
||||
public function setPosts(mixed $posts): void
|
||||
{
|
||||
$this->_posts = $posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $files
|
||||
*/
|
||||
public function setFiles(?array $files): void
|
||||
{
|
||||
$this->_files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|string $rawContent
|
||||
*/
|
||||
public function setRawContent(mixed $rawContent, string $context_type): void
|
||||
{
|
||||
if (str_contains($context_type, 'json')) {
|
||||
$this->_rawContent = json_decode($rawContent, true);
|
||||
} else if (str_contains($context_type, 'xml')) {
|
||||
$this->_rawContent = Xml::toArray($rawContent);
|
||||
} else {
|
||||
$this->_rawContent = $rawContent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getRawContent(): mixed
|
||||
{
|
||||
return $this->_rawContent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function offset(): int
|
||||
{
|
||||
return ($this->page() - 1) * $this->size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
public function getBody(): ?array
|
||||
{
|
||||
return $this->_posts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
private function page(): int
|
||||
{
|
||||
return (int)$this->query('page', 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function query($name = null, $default = null): mixed
|
||||
{
|
||||
if (!empty($name)) {
|
||||
return $this->_gets[$name] ?? $default;
|
||||
}
|
||||
return $this->_gets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function size(): int
|
||||
{
|
||||
return (int)$this->query('size', 20);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param null $defaultValue
|
||||
* @return mixed
|
||||
*/
|
||||
public function post($name, $defaultValue = null): mixed
|
||||
{
|
||||
return $this->_posts[$name] ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return bool|string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function json($name): bool|string
|
||||
{
|
||||
$data = $this->array($name);
|
||||
if (empty($data)) {
|
||||
return Json::encode([]);
|
||||
} else if (!is_array($data)) {
|
||||
return Json::encode([]);
|
||||
}
|
||||
return Json::encode($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function gets(): array
|
||||
{
|
||||
return $this->_gets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function params(): array
|
||||
{
|
||||
return array_merge($this->_gets, $this->_posts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function load(): array
|
||||
{
|
||||
return array_merge($this->_files, $this->_gets, $this->_posts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param array $defaultValue
|
||||
* @return mixed
|
||||
*/
|
||||
public function array($name, array $defaultValue = []): mixed
|
||||
{
|
||||
return $this->_posts[$name] = $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return File|null
|
||||
* @throws ReflectionException
|
||||
* @throws NotFindClassException
|
||||
*/
|
||||
public function file($name): File|null
|
||||
{
|
||||
$param = $this->_files[$name] ?? null;
|
||||
if (!empty($param)) {
|
||||
$param['class'] = File::class;
|
||||
return Kiri::createObject($param);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param bool $isNeed
|
||||
* @return mixed
|
||||
* @throws RequestException
|
||||
*/
|
||||
private function required(string $name, bool $isNeed = false): mixed
|
||||
{
|
||||
$int = $this->_posts[$name] ?? null;
|
||||
if (is_null($int) && $isNeed === true) {
|
||||
throw new RequestException("You need to add request parameter $name");
|
||||
}
|
||||
return $int;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param bool $isNeed
|
||||
* @param array|int|null $min
|
||||
* @param int|null $max
|
||||
* @return int|null
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function int(string $name, bool $isNeed = FALSE, array|int|null $min = NULL, int|null $max = NULL): ?int
|
||||
{
|
||||
return (int)$this->required($name, $isNeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param bool $isNeed
|
||||
* @param int $round
|
||||
* @return float|null
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function float(string $name, bool $isNeed = FALSE, int $round = 0): ?float
|
||||
{
|
||||
return (float)$this->required($name, $isNeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param bool $isNeed
|
||||
* @param int|array|null $length
|
||||
*
|
||||
* @return string|null
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function string(string $name, bool $isNeed = FALSE, int|array|null $length = NULL): ?string
|
||||
{
|
||||
return (string)$this->required($name, $isNeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param bool $isNeed
|
||||
*
|
||||
* @return string|null
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function email(string $name, bool $isNeed = FALSE): ?string
|
||||
{
|
||||
$email = $this->required($name, $isNeed);
|
||||
if ($email === null) {
|
||||
return null;
|
||||
}
|
||||
if (!preg_match('/^\w+([.-_]\w+)+@\w+(\.\w+)+$/', $email)) {
|
||||
throw new RequestException("Request parameter $name is in the wrong format", 4001);
|
||||
}
|
||||
return $email;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param bool $isNeed
|
||||
*
|
||||
* @return bool
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function bool(string $name, bool $isNeed = FALSE): bool
|
||||
{
|
||||
return (boolean)$this->required($name, $isNeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int|null $default
|
||||
*
|
||||
* @return int|string|null
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function timestamp(string $name, int|null $default = NULL): null|int|string
|
||||
{
|
||||
$value = $this->required($name, false);
|
||||
if ($value === null) {
|
||||
return $default;
|
||||
}
|
||||
if (!is_numeric($value)) {
|
||||
throw new RequestException('The request param :attribute not is a timestamp value');
|
||||
}
|
||||
if (strlen((string)$value) != 10) {
|
||||
throw new RequestException('The request param :attribute not is a timestamp value');
|
||||
}
|
||||
if (!date('YmdHis', $value)) {
|
||||
throw new RequestException('The request param :attribute format error', 4001);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string|null $default
|
||||
*
|
||||
* @return string|null
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function datetime(string $name, string $default = NULL): string|null
|
||||
{
|
||||
$value = $this->required($name, false);
|
||||
if ($value === null) {
|
||||
return $default;
|
||||
}
|
||||
$match = '/^\d{4}.*?([1-12]).*([1-31]).*?[0-23].*?[0-59].*?[0-59].*?$/';
|
||||
$match = preg_match($match, $value, $result);
|
||||
if (!$match || $result[0] != $value) {
|
||||
throw new RequestException('The request param :attribute format error', 4001);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string|null $default
|
||||
*
|
||||
* @return string|null
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function date(string $name, string $default = NULL): string|null
|
||||
{
|
||||
$value = $this->required($name, false);
|
||||
if ($value === null) {
|
||||
return $default;
|
||||
}
|
||||
$match = '/^\d{4}.*?([1-12]).*([1-31])$/';
|
||||
$match = preg_match($match, $value, $result);
|
||||
if (!$match || $result[0] != $value) {
|
||||
throw new RequestException('The request param :attribute format error', 4001);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string|null $default
|
||||
* @return string|null
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function ip(string $name, string $default = NULL): string|null
|
||||
{
|
||||
$value = $this->required($name, false);
|
||||
if ($value == NULL) {
|
||||
return $default;
|
||||
}
|
||||
$match = preg_match('/^\d{1,3}(\.\d{1,3}){3}$/', $value, $result);
|
||||
if (!$match || $result[0] != $value) {
|
||||
throw new RequestException('The request param :attribute format error', 4001);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Http;
|
||||
|
||||
use Annotation\Inject;
|
||||
use Exception;
|
||||
use Http\Abstracts\HttpService;
|
||||
use Http\IInterface\AuthIdentity;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Server\RequestInterface;
|
||||
use Server\ServerManager;
|
||||
use Kiri\Core\Json;
|
||||
|
||||
defined('REQUEST_OK') or define('REQUEST_OK', 0);
|
||||
defined('REQUEST_FAIL') or define('REQUEST_FAIL', 500);
|
||||
|
||||
/**
|
||||
* Class HttpRequest
|
||||
*
|
||||
* @package Kiri\Kiri\HttpRequest
|
||||
*
|
||||
* @property-read $isPost
|
||||
* @property-read $isGet
|
||||
* @property-read $isOption
|
||||
* @property-read $isDelete
|
||||
* @property-read $isHttp
|
||||
* @property-read $method
|
||||
* @property-read $identity
|
||||
* @property-read $isPackage
|
||||
* @property-read $isReceive
|
||||
*/
|
||||
class Request extends HttpService implements RequestInterface
|
||||
{
|
||||
|
||||
|
||||
use HttpHeaders, HttpParams;
|
||||
|
||||
private array $_explode = [];
|
||||
private string $_uri = '';
|
||||
private int $fd = 0;
|
||||
|
||||
private bool $isCli = FALSE;
|
||||
|
||||
private float $startTime;
|
||||
|
||||
private int $statusCode = 200;
|
||||
|
||||
private int $_clientId = 0;
|
||||
|
||||
/** @var string[] */
|
||||
private array $explode = [];
|
||||
|
||||
const PLATFORM_MAC_OX = 'mac';
|
||||
const PLATFORM_IPHONE = 'iphone';
|
||||
const PLATFORM_ANDROID = 'android';
|
||||
const PLATFORM_WINDOWS = 'windows';
|
||||
|
||||
|
||||
private string $_platform = '';
|
||||
|
||||
|
||||
/**
|
||||
* @var AuthIdentity|null
|
||||
*/
|
||||
private ?AuthIdentity $_grant = null;
|
||||
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
*/
|
||||
public function setClientId($id)
|
||||
{
|
||||
$this->_clientId = $id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $request_uri
|
||||
*/
|
||||
public function setUri(string $request_uri)
|
||||
{
|
||||
$request_uri = array_filter(explode('/', $request_uri));
|
||||
|
||||
$this->_explode = $request_uri;
|
||||
$this->_uri = '/' . implode('/', $request_uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getConnectInfo(): array|null
|
||||
{
|
||||
$server = ServerManager::getContext()->getServer();
|
||||
|
||||
return $server->getClientInfo($this->getClientId());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getStartTime(): mixed
|
||||
{
|
||||
return $this->getHeader('request_time_float');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getClientId(): int
|
||||
{
|
||||
return $this->_clientId ?? 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isFavicon(): bool
|
||||
{
|
||||
return $this->getRequestUri() === 'favicon.ico';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AuthIdentity|null
|
||||
*/
|
||||
public function getIdentity(): ?AuthIdentity
|
||||
{
|
||||
return $this->_grant;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isHead(): bool
|
||||
{
|
||||
$result = $this->getRequestMethod() == 'HEAD';
|
||||
if ($result) {
|
||||
$this->setStatus(101);
|
||||
} else {
|
||||
$this->setStatus(200);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $status
|
||||
* @return mixed
|
||||
*/
|
||||
public function setStatus($status): mixed
|
||||
{
|
||||
return $this->statusCode = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getStatus(): int
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsPackage(): bool
|
||||
{
|
||||
return $this->getRequestMethod() == 'package';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsReceive(): bool
|
||||
{
|
||||
return $this->getRequestMethod() == 'receive';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*/
|
||||
public function setGrantAuthorization($value)
|
||||
{
|
||||
return $this->_grant = $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasGrant(): bool
|
||||
{
|
||||
return $this->_grant !== null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getExplode(): array
|
||||
{
|
||||
return $this->_explode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
#[Pure] public function getCurrent(): string
|
||||
{
|
||||
return current($this->explode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUri(): string
|
||||
{
|
||||
return $this->_uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPlatform(): ?string
|
||||
{
|
||||
if (!empty($this->_platform)) {
|
||||
return $this->_platform;
|
||||
}
|
||||
|
||||
$user = $this->getAgent();
|
||||
$match = preg_match('/\(.*\)?/', $user, $output);
|
||||
if (!$match || count($output) < 1) {
|
||||
return $this->_platform = 'unknown';
|
||||
}
|
||||
$output = strtolower(array_shift($output));
|
||||
if (strpos('mac', $output)) {
|
||||
return $this->_platform = 'mac';
|
||||
} else if (strpos('iphone', $output)) {
|
||||
return $this->_platform = 'iphone';
|
||||
} else if (strpos('android', $output)) {
|
||||
return $this->_platform = 'android';
|
||||
} else if (strpos('windows', $output)) {
|
||||
return $this->_platform = 'windows';
|
||||
} else {
|
||||
return $this->_platform = 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isIos(): bool
|
||||
{
|
||||
return $this->getPlatform() == static::PLATFORM_IPHONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isAndroid(): bool
|
||||
{
|
||||
return $this->getPlatform() == static::PLATFORM_ANDROID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isMacOs(): bool
|
||||
{
|
||||
return $this->getPlatform() == static::PLATFORM_MAC_OX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isWindows(): bool
|
||||
{
|
||||
return $this->getPlatform() == static::PLATFORM_WINDOWS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsPost(): bool
|
||||
{
|
||||
return $this->getRequestMethod() == 'POST';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getIsHttp(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsOption(): bool
|
||||
{
|
||||
return $this->getRequestMethod() == 'OPTIONS';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsGet(): bool
|
||||
{
|
||||
return $this->getRequestMethod() == 'GET';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsDelete(): bool
|
||||
{
|
||||
return $this->getRequestMethod() == 'DELETE';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* 获取请求类型
|
||||
*/
|
||||
public function getMethod(): string
|
||||
{
|
||||
$method = $this->getRequestMethod();
|
||||
if (empty($method)) {
|
||||
return 'GET';
|
||||
}
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsCli(): bool
|
||||
{
|
||||
return $this->isCli === TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $value
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$method = 'set' . ucfirst($name);
|
||||
if (method_exists($this, $method)) {
|
||||
$this->$method($value);
|
||||
} else {
|
||||
parent::__set($name, $value); // TODO: Change the autogenerated stub
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|null
|
||||
*/
|
||||
#[Pure] public function getIp(): string|null
|
||||
{
|
||||
$headers = $this->getHeaders();
|
||||
if (!empty($headers['remoteip'])) return $headers['remoteip'];
|
||||
if (!empty($headers['x-forwarded-for'])) return $headers['x-forwarded-for'];
|
||||
if (!empty($headers['request-ip'])) return $headers['request-ip'];
|
||||
if (!empty($headers['remote_addr'])) return $headers['remote_addr'];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
#[Pure] public function getRuntime(): string
|
||||
{
|
||||
return sprintf('%.5f', microtime(TRUE) - $this->startTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDebug(): string
|
||||
{
|
||||
$mainstay = sprintf("%.6f", microtime(true)); // 带毫秒的时间戳
|
||||
|
||||
$timestamp = floatval($mainstay); // 时间戳
|
||||
$milliseconds = round(($mainstay - $timestamp) * 1000); // 毫秒
|
||||
|
||||
$datetime = date("Y-m-d H:i:s", (int)$timestamp) . '.' . $milliseconds;
|
||||
|
||||
$tmp = [
|
||||
'[Debug ' . $datetime . '] ',
|
||||
$this->getIp(),
|
||||
$this->getUri(),
|
||||
'`' . $this->getAgent() . '`',
|
||||
$this->getRuntime()
|
||||
];
|
||||
return implode(' ', $tmp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $router
|
||||
* @return bool
|
||||
*/
|
||||
public function is($router): bool
|
||||
{
|
||||
return $this->getRequestUri() == $router;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotFound(): bool
|
||||
{
|
||||
return Json::to(404, 'Page ' . $this->getRequestUri() . ' not found.');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: whwyy
|
||||
* Date: 2018/4/24 0024
|
||||
* Time: 19:39
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Http;
|
||||
|
||||
use Exception;
|
||||
use Http\Abstracts\HttpService;
|
||||
use Http\Http\Formatter\FileFormatter;
|
||||
use Http\Http\Formatter\HtmlFormatter;
|
||||
use Http\Http\Formatter\JsonFormatter;
|
||||
use Http\Http\Formatter\XmlFormatter;
|
||||
use Http\IInterface\IFormatter;
|
||||
use Kiri\Exception\NotFindClassException;
|
||||
use Server\ResponseInterface;
|
||||
use Server\ServerManager;
|
||||
use Swoole\Http\Response as SResponse;
|
||||
|
||||
/**
|
||||
* Class Response
|
||||
* @package Kiri\Kiri\Http
|
||||
*/
|
||||
class Response extends HttpService implements ResponseInterface
|
||||
{
|
||||
|
||||
const JSON = 'json';
|
||||
const XML = 'xml';
|
||||
const HTML = 'html';
|
||||
const FILE = 'file';
|
||||
|
||||
/** @var ?string */
|
||||
private ?string $format = null;
|
||||
|
||||
/** @var int */
|
||||
public int $statusCode = 200;
|
||||
|
||||
public array $headers = [];
|
||||
public array $cookies = [];
|
||||
|
||||
private float $startTime = 0;
|
||||
|
||||
private mixed $endData;
|
||||
|
||||
private array $_clientInfo = [];
|
||||
|
||||
const FORMAT_MAPS = [
|
||||
self::JSON => JsonFormatter::class,
|
||||
self::XML => XmlFormatter::class,
|
||||
self::HTML => HtmlFormatter::class,
|
||||
self::FILE => FileFormatter::class,
|
||||
];
|
||||
|
||||
public int $fd = 0;
|
||||
private int $clientId = 0;
|
||||
private int $reactorId = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @param int $int
|
||||
* @param int $reID
|
||||
*/
|
||||
public function setClientId(int $int, int $reID)
|
||||
{
|
||||
$this->clientId = $int;
|
||||
$this->reactorId = $reID;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $clientInfo
|
||||
*/
|
||||
public function setClientInfo(array $clientInfo)
|
||||
{
|
||||
$this->_clientInfo = $clientInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getClientInfo(): mixed
|
||||
{
|
||||
if (!empty($this->_clientInfo)) {
|
||||
return $this->_clientInfo;
|
||||
}
|
||||
$server = ServerManager::getContext()->getServer();
|
||||
return $server->getClientInfo($this->clientId, $this->reactorId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFormat(): string
|
||||
{
|
||||
return $this->format;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getClientId(): int
|
||||
{
|
||||
return $this->clientId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $format
|
||||
* @return $this
|
||||
*/
|
||||
public function setFormat($format): static
|
||||
{
|
||||
if (empty($format)) {
|
||||
return $this;
|
||||
}
|
||||
$this->format = $format;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $content
|
||||
* @return Response
|
||||
*/
|
||||
public function toHtml($content): static
|
||||
{
|
||||
$this->format = self::HTML;
|
||||
$this->endData = (string)$content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $content
|
||||
* @return Response
|
||||
*/
|
||||
public function toJson($content): static
|
||||
{
|
||||
$this->format = self::JSON;
|
||||
$this->endData = json_encode($content, JSON_UNESCAPED_UNICODE);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $content
|
||||
* @return mixed
|
||||
*/
|
||||
public function toXml($content): static
|
||||
{
|
||||
$this->format = self::XML;
|
||||
$this->endData = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param bool $isChunk
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
* @return static
|
||||
* @throws Exception
|
||||
*/
|
||||
public function sendFile(string $path, bool $isChunk = false, int $offset = 0, int $limit = 10240): static
|
||||
{
|
||||
$this->format = self::FILE;
|
||||
if (!file_exists($path)) {
|
||||
throw new Exception('File `' . $path . '` not exists.');
|
||||
}
|
||||
$this->endData = ['path' => $path, 'isChunk' => $isChunk, 'limit' => $limit, 'offset' => $offset];
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @return Response
|
||||
*/
|
||||
public function addHeader($key, $value): static
|
||||
{
|
||||
$this->headers[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param null $value
|
||||
* @param null $expires
|
||||
* @param null $path
|
||||
* @param null $domain
|
||||
* @param null $secure
|
||||
* @param null $httponly
|
||||
* @param null $samesite
|
||||
* @param null $priority
|
||||
* @return Response
|
||||
*/
|
||||
public function addCookie($name, $value = null, $expires = null, $path = null, $domain = null, $secure = null, $httponly = null, $samesite = null, $priority = null): static
|
||||
{
|
||||
$this->cookies[] = func_get_args();
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $statusCode
|
||||
* @return Response
|
||||
*/
|
||||
public function setStatusCode($statusCode): static
|
||||
{
|
||||
$this->statusCode = $statusCode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getResponseFormat(): string
|
||||
{
|
||||
return match ($this->format) {
|
||||
Response::HTML => 'text/html;charset=utf-8',
|
||||
Response::FILE => 'application/octet-stream',
|
||||
Response::XML => 'application/xml;charset=utf-8',
|
||||
default => 'application/json;charset=utf-8',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
* @param SResponse|null $response
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getBuilder(mixed $data, SResponse $response = null): ResponseInterface
|
||||
{
|
||||
if ($response != null) {
|
||||
$this->configure($response);
|
||||
}
|
||||
return $this->setContent($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param SResponse $response
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function configure(SResponse $response): static
|
||||
{
|
||||
$response->setStatusCode($this->statusCode);
|
||||
$response->header('Run-Time', $this->getRuntime());
|
||||
if (!empty($this->headers)) {
|
||||
foreach ($this->headers as $name => $header) {
|
||||
$response->header($name, $header);
|
||||
}
|
||||
}
|
||||
if (!empty($this->cookies)) {
|
||||
foreach ($this->cookies as $header) {
|
||||
$response->setCookie(...$header);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $content
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function setContent(mixed $content): ResponseInterface
|
||||
{
|
||||
$this->endData = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return IFormatter
|
||||
* @throws NotFindClassException
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function getContent(): IFormatter
|
||||
{
|
||||
$class = Response::FORMAT_MAPS[$this->format] ?? HtmlFormatter::class;
|
||||
return \di($class)->send($this->endData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @param array $param
|
||||
* @return int
|
||||
*/
|
||||
public function redirect($url, array $param = []): mixed
|
||||
{
|
||||
if (!empty($param)) {
|
||||
$url .= '?' . http_build_query($param);
|
||||
}
|
||||
$url = ltrim($url, '/');
|
||||
if (!preg_match('/^http/', $url)) {
|
||||
$url = '/' . $url;
|
||||
}
|
||||
/** @var SResponse $response */
|
||||
$response = Context::getContext('response');
|
||||
if (!empty($response)) {
|
||||
return $response->redirect($url, 302);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getRuntime(): string
|
||||
{
|
||||
return sprintf('%.5f', microtime(TRUE) - request()->getStartTime());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Http;
|
||||
|
||||
|
||||
use Exception;
|
||||
use Kiri\Abstracts\BaseObject;
|
||||
use validator\Validator;
|
||||
|
||||
/**
|
||||
* Class HttpFilter
|
||||
* @package Http\Http
|
||||
*/
|
||||
class HttpFilter extends BaseObject
|
||||
{
|
||||
|
||||
|
||||
private array $hash = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
* @param string $method
|
||||
* @param array $rules
|
||||
* @return $this
|
||||
*/
|
||||
public function register(string $className, string $method, array $rules): HttpFilter
|
||||
{
|
||||
$this->hash[$className . '::' . $method] = $rules;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
* @param string $method
|
||||
* @return array
|
||||
*/
|
||||
public function getRules(string $className, string $method): array
|
||||
{
|
||||
return $this->hash[$className . '::' . $method] ?? [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $rules
|
||||
* @return bool|Validator
|
||||
* @throws Exception
|
||||
*/
|
||||
public function check(array $rules): bool|Validator
|
||||
{
|
||||
if (empty($rules)) {
|
||||
return true;
|
||||
}
|
||||
$validator = Validator::getInstance();
|
||||
$validator->setParams(Input()->load());
|
||||
foreach ($rules as $val) {
|
||||
$field = array_shift($val);
|
||||
if (empty($val)) {
|
||||
continue;
|
||||
}
|
||||
$validator->make($field, $val);
|
||||
}
|
||||
return $validator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace Http\IInterface;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Http\Http\Request;
|
||||
|
||||
interface After
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param $params
|
||||
* @return mixed
|
||||
*/
|
||||
public function onHandler(Request $request, $params): void;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace Http\IInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Interface AuthIdentity
|
||||
* @package Kiri\Kiri\Http
|
||||
*/
|
||||
interface AuthIdentity
|
||||
{
|
||||
|
||||
|
||||
public function getIdentity();
|
||||
|
||||
|
||||
/**
|
||||
* @return string|int
|
||||
* 获取唯一识别码
|
||||
*/
|
||||
public function getUniqueId(): string|int;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: whwyy
|
||||
* Date: 2018/4/8 0008
|
||||
* Time: 17:29
|
||||
*/
|
||||
|
||||
namespace Http\IInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Interface IFormatter
|
||||
* @package Kiri\Kiri\Http\Formatter
|
||||
*/
|
||||
interface IFormatter
|
||||
{
|
||||
|
||||
/**
|
||||
* @param $context
|
||||
* @return static
|
||||
*/
|
||||
public function send($context): static;
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData(): mixed;
|
||||
|
||||
public function clear(): void;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace Http\IInterface;
|
||||
|
||||
|
||||
use Http\Http\Request;
|
||||
use Closure;
|
||||
|
||||
interface Interceptor
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Closure $closure
|
||||
* @return mixed
|
||||
*/
|
||||
public function Interceptor(Request $request, Closure $closure): mixed;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace Http\IInterface;
|
||||
|
||||
|
||||
use Http\Http\Request;
|
||||
use Closure;
|
||||
|
||||
interface Limits
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Closure $closure
|
||||
* @return mixed
|
||||
*/
|
||||
public function next(Request $request, Closure $closure): mixed;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\IInterface;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Http\Http\Request;
|
||||
|
||||
/**
|
||||
* Interface IMiddleware
|
||||
* @package Kiri\Kiri\Route
|
||||
*/
|
||||
interface Middleware
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function onHandler(Request $request, Closure $next): mixed;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\IInterface;
|
||||
|
||||
|
||||
interface RouterInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace Http\IInterface;
|
||||
|
||||
|
||||
interface Service
|
||||
{
|
||||
|
||||
|
||||
public function onInit();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace Http\IInterface;
|
||||
|
||||
|
||||
interface Task
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getParams(): array;
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @return $this
|
||||
*/
|
||||
public function setParams(array $params): static;
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function onHandler(): mixed;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace Http\Route;
|
||||
|
||||
|
||||
/**
|
||||
* Class Any
|
||||
* @package Kiri\Kiri\Route
|
||||
*/
|
||||
class Any
|
||||
{
|
||||
|
||||
private array $nodes = [];
|
||||
|
||||
/**
|
||||
* Any constructor.
|
||||
* @param array $nodes
|
||||
*/
|
||||
public function __construct(array $nodes)
|
||||
{
|
||||
$this->nodes = $nodes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $arguments
|
||||
* @return $this
|
||||
*/
|
||||
public function __call($name, $arguments): static
|
||||
{
|
||||
foreach ($this->nodes as $node) {
|
||||
$node->{$name}(...$arguments);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace Http\Route;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Http\Http\Context;
|
||||
use Http\Http\Request;
|
||||
use Http\Http\Response;
|
||||
use Http\IInterface\Middleware;
|
||||
use Server\RequestInterface;
|
||||
use Kiri\Kiri;
|
||||
|
||||
/**
|
||||
* Class CoreMiddleware
|
||||
* @package Kiri\Kiri\Route
|
||||
* 跨域中间件
|
||||
*/
|
||||
class CoreMiddleware implements Middleware
|
||||
{
|
||||
|
||||
|
||||
/** @var int */
|
||||
public int $zOrder = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function onHandler(RequestInterface $request, Closure $next): mixed
|
||||
{
|
||||
/** @var Response $response */
|
||||
$response = \response();
|
||||
$response->addHeader('Access-Control-Allow-Origin', '*');
|
||||
$response->addHeader('Access-Control-Allow-Headers', $request->header('access-control-request-headers'));
|
||||
$response->addHeader('Access-Control-Request-Method', $request->header('access-control-request-method'));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Http\Route;
|
||||
|
||||
use Kiri\Abstracts\BaseObject;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class HandlerProviders extends BaseObject
|
||||
{
|
||||
|
||||
private array $handlers = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param $method
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($path, $method): mixed
|
||||
{
|
||||
return $this->handlers[$method][$path] ?? null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $method
|
||||
* @param $path
|
||||
* @param $handler
|
||||
*/
|
||||
public function add($method, $path, $handler)
|
||||
{
|
||||
$this->handlers[$method][$path] = $handler;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Http\Route;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Http\IInterface\Middleware;
|
||||
use Kiri\Abstracts\BaseObject;
|
||||
|
||||
|
||||
/**
|
||||
* Class MiddlewareManager
|
||||
* @package Http\Route
|
||||
*/
|
||||
class MiddlewareManager extends BaseObject
|
||||
{
|
||||
|
||||
private static array $_middlewares = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
* @param $method
|
||||
* @param array|string $middlewares
|
||||
*/
|
||||
public function addMiddlewares($class, $method, array|string $middlewares)
|
||||
{
|
||||
if (is_object($class)) {
|
||||
$class = $class::class;
|
||||
}
|
||||
if (!isset(static::$_middlewares[$class . '::' . $method])) {
|
||||
static::$_middlewares[$class . '::' . $method] = [];
|
||||
}
|
||||
|
||||
if (is_string($middlewares) && !in_array($middlewares, static::$_middlewares[$class . '::' . $method])) {
|
||||
static::$_middlewares[$class . '::' . $method][] = $middlewares;
|
||||
return;
|
||||
}
|
||||
foreach ($middlewares as $middleware) {
|
||||
if (in_array($middlewares, static::$_middlewares[$class . '::' . $method])) {
|
||||
continue;
|
||||
}
|
||||
static::$_middlewares[$class . '::' . $method][] = $middleware;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
* @param $method
|
||||
* @return bool
|
||||
*/
|
||||
public function hasMiddleware($class, $method): bool
|
||||
{
|
||||
if (is_object($class)) {
|
||||
$class = $class::class;
|
||||
}
|
||||
return isset(static::$_middlewares[$class . '::' . $method]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
* @param $method
|
||||
* @param $caller
|
||||
* @return mixed
|
||||
*/
|
||||
public function callerMiddlewares($class, $method, $caller): mixed
|
||||
{
|
||||
if (is_object($class)) {
|
||||
$class = $class::class;
|
||||
}
|
||||
$middlewares = static::$_middlewares[$class . '::' . $method] ?? [];
|
||||
if (empty($middlewares)) {
|
||||
return $caller;
|
||||
}
|
||||
return array_reduce(array_reverse($middlewares), function ($stack, $pipe) {
|
||||
return function ($passable) use ($stack, $pipe) {
|
||||
if ($pipe instanceof Middleware) {
|
||||
return $pipe->onHandler($passable, $stack);
|
||||
}
|
||||
return call_user_func($pipe, $passable, $stack);
|
||||
};
|
||||
}, $caller);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $middlewares
|
||||
* @param Closure $caller
|
||||
* @return Closure
|
||||
*/
|
||||
public function closureMiddlewares($middlewares, Closure $caller): Closure
|
||||
{
|
||||
return array_reduce(array_reverse($middlewares), function ($stack, $pipe) {
|
||||
return function ($passable) use ($stack, $pipe) {
|
||||
if ($pipe instanceof Middleware) {
|
||||
return $pipe->onHandler($passable, $stack);
|
||||
}
|
||||
return call_user_func($pipe, $passable, $stack);
|
||||
};
|
||||
}, $caller);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace Http\Route;
|
||||
|
||||
|
||||
use Annotation\Aspect;
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Http\Exception\RequestException;
|
||||
use Http\Http\Request;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use ReflectionException;
|
||||
use Server\Events\OnAfterWorkerStart;
|
||||
use Kiri\Events\EventProvider;
|
||||
use Kiri\Exception\NotFindClassException;
|
||||
use Kiri\IAspect;
|
||||
use Kiri\Kiri;
|
||||
|
||||
/**
|
||||
* Class Node
|
||||
* @package Kiri\Kiri\Route
|
||||
*/
|
||||
class Node
|
||||
{
|
||||
|
||||
public string $path = '';
|
||||
public int $index = 0;
|
||||
public string $method = '';
|
||||
|
||||
/** @var Node[] $childes */
|
||||
public array $childes = [];
|
||||
|
||||
public array $group = [];
|
||||
|
||||
private string $_error = '';
|
||||
|
||||
private string $_dataType = '';
|
||||
|
||||
private array|Closure|null $_handler = null;
|
||||
|
||||
public string $htmlSuffix = '.html';
|
||||
public bool $enableHtmlSuffix = false;
|
||||
public array $namespace = [];
|
||||
public array $middleware = [];
|
||||
|
||||
public string $sourcePath = '';
|
||||
|
||||
/** @var array|Closure */
|
||||
public Closure|array $callback = [];
|
||||
|
||||
private string $_alias = '';
|
||||
|
||||
|
||||
/**
|
||||
* @param string $dataType
|
||||
*/
|
||||
public function setDataType(string $dataType)
|
||||
{
|
||||
$this->_dataType = $dataType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$eventDispatcher = di(EventProvider::class);
|
||||
$eventDispatcher->on(OnAfterWorkerStart::class, [$this, 'setParameters']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function unpack(string $data): mixed
|
||||
{
|
||||
// if ($this->_dataType == RpcProducer::PROTOCOL_JSON) {
|
||||
// return json_decode($data, true);
|
||||
// }
|
||||
// if ($this->_dataType == RpcProducer::PROTOCOL_SERIALIZE) {
|
||||
// return unserialize($data);
|
||||
// }
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $handler
|
||||
* @param $path
|
||||
* @return Node
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function setHandler($handler, $path): static
|
||||
{
|
||||
$this->sourcePath = $path;
|
||||
if (is_string($handler) && str_contains($handler, '@')) {
|
||||
$handler = $this->splitHandler($handler);
|
||||
} else if ($handler != null && !is_callable($handler, true)) {
|
||||
$this->_error = 'Controller is con\'t exec.';
|
||||
}
|
||||
$this->_handler = $handler;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $handler
|
||||
* @return array
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function splitHandler(string $handler): array
|
||||
{
|
||||
list($controller, $action) = explode('@', $handler);
|
||||
if (!class_exists($controller) && !empty($this->namespace)) {
|
||||
$controller = implode('\\', $this->namespace) . '\\' . $controller;
|
||||
}
|
||||
return [Kiri::getDi()->get($controller), $action];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function injectMiddleware($handler): static
|
||||
{
|
||||
$manager = di(MiddlewareManager::class);
|
||||
if (!($handler instanceof Closure)) {
|
||||
$callback = $this->injectControllerMiddleware($manager, $handler);
|
||||
} else {
|
||||
$callback = $this->injectClosureMiddleware($manager, $handler);
|
||||
}
|
||||
$this->getHandlerProviders()->add($this->method, $this->sourcePath, $callback);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return HandlerProviders
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function getHandlerProviders(): HandlerProviders
|
||||
{
|
||||
return Kiri::getDi()->get(HandlerProviders::class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $manager
|
||||
* @param $handler
|
||||
* @return mixed
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function injectControllerMiddleware($manager, $handler): mixed
|
||||
{
|
||||
$manager->addMiddlewares($handler[0], $handler[1], $this->middleware);
|
||||
return $manager->callerMiddlewares(
|
||||
$handler[0], $handler[1], $this->aopHandler($this->getAop($handler), $handler)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $manager
|
||||
* @param $handler
|
||||
* @return mixed
|
||||
*/
|
||||
private function injectClosureMiddleware($manager, $handler): mixed
|
||||
{
|
||||
if (!empty($this->middleware)) {
|
||||
return $manager->closureMiddlewares($this->middleware, $this->normalHandler($handler));
|
||||
} else {
|
||||
return $this->normalHandler($handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ?array $_injectParameters = [];
|
||||
|
||||
|
||||
/**
|
||||
* @throws ReflectionException
|
||||
* @throws NotFindClassException
|
||||
*/
|
||||
public function setParameters(): static
|
||||
{
|
||||
$container = Kiri::getDi();
|
||||
if (empty($this->_handler)) {
|
||||
return $this;
|
||||
}
|
||||
$dispatcher = $this->_handler;
|
||||
if ($dispatcher instanceof Closure) {
|
||||
$this->_injectParameters = $container->resolveFunctionParameters($dispatcher);
|
||||
} else {
|
||||
[$controller, $action] = $dispatcher;
|
||||
if (is_object($controller)) {
|
||||
$controller = get_class($controller);
|
||||
}
|
||||
$this->_injectParameters = $container->getMethodParameters($controller, $action);
|
||||
}
|
||||
return $this->injectMiddleware($dispatcher);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param IAspect|null $reflect
|
||||
* @param $handler
|
||||
* @return Closure
|
||||
*/
|
||||
#[Pure] private function aopHandler(?IAspect $reflect, $handler): Closure
|
||||
{
|
||||
if (is_null($reflect)) {
|
||||
return $this->normalHandler($handler);
|
||||
}
|
||||
|
||||
$params = $this->_injectParameters;
|
||||
return static function () use ($reflect, $handler, $params) {
|
||||
return $reflect->invoke($handler, $params);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws ReflectionException|NotFindClassException
|
||||
*/
|
||||
private function getAop($handler): ?IAspect
|
||||
{
|
||||
[$controller, $action] = $handler;
|
||||
$aspect = Kiri::getDi()->getMethodAttribute($controller::class, $action);
|
||||
if (empty($aspect)) {
|
||||
return null;
|
||||
}
|
||||
foreach ($aspect as $value) {
|
||||
if ($value instanceof Aspect) {
|
||||
return di($value->aspect);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $handler
|
||||
* @return Closure
|
||||
*/
|
||||
private function normalHandler($handler): Closure
|
||||
{
|
||||
$params = $this->_injectParameters;
|
||||
return static function () use ($handler, $params) {
|
||||
return call_user_func($handler, ...$params);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
#[Pure] protected function annotation(): array
|
||||
{
|
||||
return $this->getMiddleWares();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function methodAllow(Request $request): bool
|
||||
{
|
||||
if ($this->method == $request->getMethod()) {
|
||||
return true;
|
||||
}
|
||||
return $this->method == 'any';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function checkSuffix(): bool
|
||||
{
|
||||
if ($this->enableHtmlSuffix) {
|
||||
$url = request()->getUri();
|
||||
$nowLength = strlen($this->htmlSuffix);
|
||||
if (strpos($url, $this->htmlSuffix) !== strlen($url) - $nowLength) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* 错误信息
|
||||
*/
|
||||
public function getError(): string
|
||||
{
|
||||
return $this->_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
* @return Node
|
||||
*/
|
||||
public function addChild(Node $node): Node
|
||||
{
|
||||
$this->childes[] = $node;
|
||||
return $node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $search
|
||||
* @return Node|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function findNode(string $search): ?Node
|
||||
{
|
||||
if (empty($this->childes)) {
|
||||
return null;
|
||||
}
|
||||
foreach ($this->childes as $val) {
|
||||
if ($search == $val->path) {
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $alias
|
||||
* @return $this
|
||||
* 别称
|
||||
*/
|
||||
public function alias(string $alias): static
|
||||
{
|
||||
$this->_alias = $alias;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAlias(): string
|
||||
{
|
||||
return $this->_alias;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Closure|array $class
|
||||
* @return $this
|
||||
*/
|
||||
public function addMiddleware(Closure|array $class): static
|
||||
{
|
||||
if (empty($class)) return $this;
|
||||
foreach ($class as $closure) {
|
||||
if (in_array($closure, $this->middleware)) {
|
||||
continue;
|
||||
}
|
||||
$this->middleware[] = $closure;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMiddleWares(): array
|
||||
{
|
||||
return $this->middleware;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dispatch(): mixed
|
||||
{
|
||||
$handlerProviders = $this->getHandlerProviders()->get($this->sourcePath, $this->method);
|
||||
if (empty($handlerProviders)) {
|
||||
throw new RequestException('<h2>HTTP 404 Not Found</h2><hr><i>Powered by Swoole</i>', 404);
|
||||
}
|
||||
return call_user_func($handlerProviders, \request());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,603 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http\Route;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Http\Abstracts\HttpService;
|
||||
use Http\Controller;
|
||||
use Http\Exception\RequestException;
|
||||
use Http\Http\Request;
|
||||
use Http\Http\Response;
|
||||
use Http\IInterface\Middleware;
|
||||
use Http\IInterface\RouterInterface;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use ReflectionException;
|
||||
use Server\RequestInterface;
|
||||
use Kiri\Abstracts\Config;
|
||||
use Kiri\Exception\ConfigException;
|
||||
use Kiri\Exception\NotFindClassException;
|
||||
use Kiri\Kiri;
|
||||
|
||||
defined('ROUTER_TREE') or define('ROUTER_TREE', 1);
|
||||
defined('ROUTER_HASH') or define('ROUTER_HASH', 2);
|
||||
|
||||
/**
|
||||
* Class Router
|
||||
* @package Kiri\Kiri\Route
|
||||
*/
|
||||
class Router extends HttpService implements RouterInterface
|
||||
{
|
||||
/** @var Node[] $nodes */
|
||||
public array $nodes = [];
|
||||
public array $groupTacks = [];
|
||||
public ?string $dir = 'App\\Http\\Controllers';
|
||||
|
||||
const NOT_FOUND = 'Page not found or method not allowed.';
|
||||
|
||||
/** @var string[] */
|
||||
public array $methods = ['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE', 'RECEIVE', 'HEAD'];
|
||||
|
||||
|
||||
public ?Closure $middleware = null;
|
||||
|
||||
public int $useTree = ROUTER_TREE;
|
||||
|
||||
public ?Response $response = null;
|
||||
|
||||
|
||||
/**
|
||||
* @param Closure $middleware
|
||||
*/
|
||||
public function setMiddleware(\Closure $middleware): void
|
||||
{
|
||||
$this->middleware = $middleware;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws ConfigException
|
||||
* @throws Exception
|
||||
* 初始化函数路径
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->dir = Config::get('http.namespace', $this->dir);
|
||||
|
||||
$this->response = Kiri::app()->get('response');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param bool $useTree
|
||||
*/
|
||||
public function setUseTree(bool $useTree): void
|
||||
{
|
||||
$this->useTree = $useTree ? ROUTER_TREE : ROUTER_HASH;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param $handler
|
||||
* @param string $method
|
||||
* @return ?Node
|
||||
* @throws Exception
|
||||
*/
|
||||
public function addRoute($path, $handler, string $method = 'any'): ?Node
|
||||
{
|
||||
if (!isset($this->nodes[$method])) {
|
||||
$this->nodes[$method] = [];
|
||||
}
|
||||
if ($handler instanceof Closure) {
|
||||
$handler = Closure::bind($handler, di(Controller::class));
|
||||
}
|
||||
return $this->tree($path, $handler, $method);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param $handler
|
||||
* @param string $method
|
||||
* @return ?Node
|
||||
*/
|
||||
private function hash($path, $handler, string $method = 'any'): ?Node
|
||||
{
|
||||
$path = $this->resolve($path);
|
||||
$this->nodes[$method][$path] = $this->NodeInstance($path, 0, $method);
|
||||
|
||||
return $this->nodes[$method][$path]->setHandler($handler);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @return string
|
||||
*/
|
||||
#[Pure] private function resolve($path): string
|
||||
{
|
||||
$paths = array_column($this->groupTacks, 'prefix');
|
||||
if (empty($paths)) {
|
||||
return '/' . ltrim($path, '/');
|
||||
}
|
||||
$paths = '/' . implode('/', $paths);
|
||||
if ($path != '/') {
|
||||
return $paths . '/' . ltrim($path, '/');
|
||||
}
|
||||
return $paths . '/';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param $handler
|
||||
* @param string $method
|
||||
* @return Node
|
||||
* @throws Exception
|
||||
*/
|
||||
private function tree($path, $handler, string $method = 'any'): Node
|
||||
{
|
||||
$explode = $this->split($path);
|
||||
if (!isset($this->nodes[$method]['/'])) {
|
||||
$this->nodes[$method]['/'] = $this->NodeInstance('/', 0, $method);
|
||||
}
|
||||
$parent = $this->nodes[$method]['/'];
|
||||
if (!empty($explode)) {
|
||||
$parent = $this->bindNode($parent, $explode, $method);
|
||||
}
|
||||
return $parent->setHandler($handler, $path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Node $parent
|
||||
* @param array $explode
|
||||
* @param $method
|
||||
* @return Node
|
||||
* @throws Exception
|
||||
*/
|
||||
private function bindNode(Node $parent, array $explode, $method): Node
|
||||
{
|
||||
$a = 0;
|
||||
foreach ($explode as $value) {
|
||||
++$a;
|
||||
$search = $parent->findNode($value);
|
||||
if ($search === null) {
|
||||
$parent = $parent->addChild($this->NodeInstance($value, $a, $method));
|
||||
} else {
|
||||
$parent = $search;
|
||||
}
|
||||
}
|
||||
return $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $route
|
||||
* @param $handler
|
||||
* @return Node|null
|
||||
* @throws
|
||||
*/
|
||||
public function socket($route, $handler): ?Node
|
||||
{
|
||||
return $this->addRoute($route, $handler, 'socket');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $route
|
||||
* @param $handler
|
||||
* @return Node|null
|
||||
* @throws
|
||||
*/
|
||||
public function post($route, $handler): ?Node
|
||||
{
|
||||
return $this->addRoute($route, $handler, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $route
|
||||
* @param $handler
|
||||
* @return Node|null
|
||||
* @throws
|
||||
*/
|
||||
public function get($route, $handler): ?Node
|
||||
{
|
||||
return $this->addRoute($route, $handler, 'GET');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $port
|
||||
* @param callable $callback
|
||||
* @return mixed
|
||||
* @throws
|
||||
*/
|
||||
public function addRpcService($port, callable $callback): mixed
|
||||
{
|
||||
return call_user_func($callback, new Actuator($port));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $route
|
||||
* @param $handler
|
||||
* @return Node|null
|
||||
* @throws
|
||||
*/
|
||||
public function options($route, $handler): ?Node
|
||||
{
|
||||
return $this->addRoute($route, $handler, 'OPTIONS');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $route
|
||||
* @param $handler
|
||||
* @return Any
|
||||
* @throws
|
||||
*/
|
||||
public function any($route, $handler): Any
|
||||
{
|
||||
$nodes = [];
|
||||
foreach ($this->methods as $method) {
|
||||
$nodes[] = $this->addRoute($route, $handler, $method);
|
||||
}
|
||||
return new Any($nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $route
|
||||
* @param $handler
|
||||
* @return Node|null
|
||||
* @throws
|
||||
*/
|
||||
public function delete($route, $handler): ?Node
|
||||
{
|
||||
return $this->addRoute($route, $handler, 'DELETE');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $route
|
||||
* @param $handler
|
||||
* @return Node|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function head($route, $handler): ?Node
|
||||
{
|
||||
return $this->addRoute($route, $handler, 'HEAD');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $route
|
||||
* @param $handler
|
||||
* @return Node|null
|
||||
* @throws
|
||||
*/
|
||||
public function put($route, $handler): ?Node
|
||||
{
|
||||
return $this->addRoute($route, $handler, 'PUT');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @param int $index
|
||||
* @param string $method
|
||||
* @return Node
|
||||
* @throws
|
||||
*/
|
||||
public function NodeInstance($value, int $index = 0, string $method = 'GET'): Node
|
||||
{
|
||||
$node = new Node();
|
||||
$node->childes = [];
|
||||
$node->path = $value;
|
||||
$node->index = $index;
|
||||
$node->method = $method;
|
||||
$node->namespace = $this->loadNamespace($method);
|
||||
|
||||
$name = array_column($this->groupTacks, 'middleware');
|
||||
if ($this->middleware instanceof \Closure) {
|
||||
$node->addMiddleware([$this->middleware]);
|
||||
}
|
||||
if (is_array($name)) {
|
||||
$node->addMiddleware($this->resolve_middleware($name));
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|array $middleware
|
||||
* @return array
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function resolve_middleware(string|array $middleware): array
|
||||
{
|
||||
if (is_string($middleware)) {
|
||||
$middleware = [$middleware];
|
||||
}
|
||||
|
||||
$array = [];
|
||||
foreach ($middleware as $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $item) {
|
||||
$array[] = $this->getMiddlewareInstance($item);
|
||||
}
|
||||
} else {
|
||||
$array[] = $this->getMiddlewareInstance($value);
|
||||
}
|
||||
}
|
||||
return array_filter($array);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @return Closure|array|null
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function getMiddlewareInstance($value): null|Closure|array
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
$value = Kiri::createObject($value);
|
||||
if (!($value instanceof Middleware)) {
|
||||
return null;
|
||||
}
|
||||
return [$value, 'onHandler'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $method
|
||||
* @return array
|
||||
*/
|
||||
private function loadNamespace($method): array
|
||||
{
|
||||
$name = array_column($this->groupTacks, 'namespace');
|
||||
array_unshift($name, $this->dir);
|
||||
return array_filter($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
* @param callable $callback
|
||||
* 路由分组
|
||||
* @param null $stdClass
|
||||
*/
|
||||
public function group(array $config, callable $callback, $stdClass = null)
|
||||
{
|
||||
$this->groupTacks[] = $config;
|
||||
if ($stdClass) {
|
||||
$callback($stdClass);
|
||||
} else {
|
||||
$callback($this);
|
||||
}
|
||||
array_pop($this->groupTacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function addPrefix(): string
|
||||
{
|
||||
$prefix = array_column($this->groupTacks, 'prefix');
|
||||
|
||||
$prefix = array_filter($prefix);
|
||||
|
||||
if (empty($prefix)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '/' . implode('/', $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $explode
|
||||
* @param $method
|
||||
* @return Node|null
|
||||
* 查找指定路由
|
||||
*/
|
||||
public function tree_search(?array $explode, $method): ?Node
|
||||
{
|
||||
if (!isset($this->nodes[$method])) {
|
||||
return null;
|
||||
}
|
||||
$parent = $this->nodes[$method]['/'];
|
||||
while ($value = array_shift($explode)) {
|
||||
$node = $parent->findNode($value);
|
||||
if (!$node) {
|
||||
return null;
|
||||
}
|
||||
$parent = $node;
|
||||
}
|
||||
return $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @return array|null
|
||||
*/
|
||||
public function split($path): ?array
|
||||
{
|
||||
$path = $this->addPrefix() . '/' . ltrim($path, '/');
|
||||
if ($path === '/') {
|
||||
return [];
|
||||
}
|
||||
$filter = array_filter(explode('/', $path));
|
||||
if (!empty($filter)) {
|
||||
return $filter;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function each(): array
|
||||
{
|
||||
$paths = [];
|
||||
foreach ($this->nodes as $node) {
|
||||
/** @var Node[] $node */
|
||||
foreach ($node as $path => $_node) {
|
||||
$paths[] = strtoupper($_node->method) . ' : ' . $path;
|
||||
}
|
||||
}
|
||||
return $paths;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $exception
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function exception($exception): mixed
|
||||
{
|
||||
return Kiri::app()->getLogger()->exception($exception);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return Node|null 树干搜索
|
||||
* 树干搜索
|
||||
* @throws Exception
|
||||
*/
|
||||
public function find_path(Request $request): ?Node
|
||||
{
|
||||
$method = $request->getMethod();
|
||||
|
||||
$methods = $this->nodes[$method][\request()->getUri()] ?? null;
|
||||
if (!is_null($methods)) {
|
||||
return $methods;
|
||||
}
|
||||
if ($request->isOption) {
|
||||
return $this->nodes[$method]['*'] ?? null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $uri
|
||||
* @param $method
|
||||
* @return Node|null
|
||||
*/
|
||||
public function search($uri, $method): Node|null
|
||||
{
|
||||
if (!isset($this->nodes[$method])) {
|
||||
return null;
|
||||
}
|
||||
$methods = $this->nodes[$method];
|
||||
if (isset($methods[$uri])) {
|
||||
return $methods[$uri];
|
||||
}
|
||||
return $methods['/'] ?? null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $request
|
||||
* @return Node|null
|
||||
*/
|
||||
private function search_options($request): ?Node
|
||||
{
|
||||
$method = $request->getMethod();
|
||||
if (!isset($this->nodes[$method])) {
|
||||
return null;
|
||||
}
|
||||
if (!isset($this->nodes[$method]['*'])) {
|
||||
return null;
|
||||
}
|
||||
return $this->nodes[$method]['*'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @return Node|null
|
||||
* 树杈搜索
|
||||
*/
|
||||
public function Branch_search(RequestInterface $request): ?Node
|
||||
{
|
||||
$node = $this->tree_search($request->getExplode(), $request->getMethod());
|
||||
if ($node instanceof Node) {
|
||||
return $node;
|
||||
}
|
||||
if (!$request->isOption) {
|
||||
return null;
|
||||
}
|
||||
$node = $this->tree_search(['*'], $request->getMethod());
|
||||
if (!($node instanceof Node)) {
|
||||
return null;
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws
|
||||
*/
|
||||
public function _loader()
|
||||
{
|
||||
$this->loadRouteDir(APP_PATH . 'routes');
|
||||
// $classes = Kiri::getAnnotation()->runtime(CONTROLLER_PATH);
|
||||
//
|
||||
// $di = Kiri::getDi();
|
||||
// foreach ($classes as $class) {
|
||||
// $methods = $di->getMethodAttribute($class);
|
||||
// foreach ($methods as $method => $attribute) {
|
||||
// if (empty($attribute)) {
|
||||
// continue;
|
||||
// }
|
||||
// foreach ($attribute as $item) {
|
||||
// $item->execute($class, $method);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @throws Exception
|
||||
* 加载目录下的路由文件
|
||||
*/
|
||||
private function loadRouteDir($path)
|
||||
{
|
||||
$files = glob($path . '/*');
|
||||
for ($i = 0; $i < count($files); $i++) {
|
||||
if (is_dir($files[$i])) {
|
||||
$this->loadRouteDir($files[$i]);
|
||||
} else {
|
||||
$this->loadRouterFile($files[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $files
|
||||
* @throws Exception
|
||||
*/
|
||||
private function loadRouterFile($files)
|
||||
{
|
||||
try {
|
||||
$router = $this;
|
||||
include_once "{$files}";
|
||||
} catch (\Throwable $exception) {
|
||||
$this->addError($exception, 'throwable');
|
||||
} finally {
|
||||
if (isset($exception)) {
|
||||
unset($exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Http;
|
||||
|
||||
use Exception;
|
||||
use Http\Abstracts\HttpService;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use ReflectionException;
|
||||
use Rpc\Service;
|
||||
use Server\Constant;
|
||||
use Server\ServerManager;
|
||||
use Kiri\Abstracts\Config;
|
||||
use Kiri\Error\LoggerProcess;
|
||||
use Kiri\Exception\ConfigException;
|
||||
use Kiri\Exception\NotFindClassException;
|
||||
use Kiri\Process\Biomonitoring;
|
||||
use Kiri\Kiri;
|
||||
use Swoole\Runtime;
|
||||
|
||||
|
||||
defined('PID_PATH') or define('PID_PATH', APP_PATH . 'storage/server.pid');
|
||||
|
||||
/**
|
||||
* Class Server
|
||||
* @package Http
|
||||
*/
|
||||
class Server extends HttpService
|
||||
{
|
||||
|
||||
private array $process = [
|
||||
Biomonitoring::class,
|
||||
LoggerProcess::class
|
||||
];
|
||||
|
||||
|
||||
private ServerManager $manager;
|
||||
private mixed $daemon = 0;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->manager = ServerManager::getContext();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $process
|
||||
*/
|
||||
public function addProcess($process)
|
||||
{
|
||||
$this->process[] = $process;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string start server
|
||||
*
|
||||
* start server
|
||||
* @throws ConfigException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function start(): string
|
||||
{
|
||||
$this->manager->initBaseServer(Config::get('server', [], true), $this->daemon);
|
||||
|
||||
$rpcService = Config::get('rpc', []);
|
||||
if (!empty($rpcService)) {
|
||||
$this->rpcListener($rpcService);
|
||||
}
|
||||
|
||||
$processes = array_merge($this->process, Config::get('processes', []));
|
||||
foreach ($processes as $process) {
|
||||
$this->manager->addProcess($process);
|
||||
}
|
||||
Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_BLOCKING_FUNCTION);
|
||||
|
||||
return $this->manager->getServer()->start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $rpcService
|
||||
* @throws ReflectionException
|
||||
* @throws NotFindClassException
|
||||
* @throws ConfigException
|
||||
*/
|
||||
private function rpcListener($rpcService)
|
||||
{
|
||||
if (in_array($rpcService['mode'], [SWOOLE_SOCK_UDP, SWOOLE_UDP, SWOOLE_UDP6, SWOOLE_SOCK_UDP6])) {
|
||||
$rpcService['events'][Constant::PACKET] = [Service::class, 'onPacket'];
|
||||
} else {
|
||||
$rpcService['events'][Constant::RECEIVE] = [Service::class, 'onReceive'];
|
||||
$rpcService['events'][Constant::CONNECT] = [Service::class, 'onConnect'];
|
||||
$rpcService['events'][Constant::DISCONNECT] = [Service::class, 'onDisconnect'];
|
||||
$rpcService['events'][Constant::CLOSE] = [Service::class, 'onClose'];
|
||||
}
|
||||
$rpcService['settings']['enable_unsafe_event'] = true;
|
||||
$this->addRpcListener($rpcService);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $rpcService
|
||||
* @throws ConfigException
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function addRpcListener($rpcService)
|
||||
{
|
||||
$this->manager->addListener($rpcService['type'], $rpcService['host'], $rpcService['port'], $rpcService['mode'], $rpcService);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*
|
||||
* start server
|
||||
* @throws Exception
|
||||
*/
|
||||
public function shutdown()
|
||||
{
|
||||
$this->manager->stopServer(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $daemon
|
||||
* @return Server
|
||||
*/
|
||||
public function setDaemon($daemon): static
|
||||
{
|
||||
if (!in_array($daemon, [0, 1])) {
|
||||
return $this;
|
||||
}
|
||||
$this->daemon = $daemon;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return \Swoole\Http\Server|\Swoole\Server|\Swoole\WebSocket\Server|null
|
||||
*/
|
||||
#[Pure] public function getServer(): \Swoole\Http\Server|\Swoole\Server|\Swoole\WebSocket\Server|null
|
||||
{
|
||||
return $this->manager->getServer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Http;
|
||||
|
||||
|
||||
use Console\Console;
|
||||
use Exception;
|
||||
use Kiri\Abstracts\Providers;
|
||||
use Kiri\Application;
|
||||
|
||||
/**
|
||||
* Class DatabasesProviders
|
||||
* @package Database
|
||||
*/
|
||||
class ServerProviders extends Providers
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param Application $application
|
||||
* @throws Exception
|
||||
*/
|
||||
public function onImport(Application $application)
|
||||
{
|
||||
$application->set('server', ['class' => Server::class]);
|
||||
|
||||
/** @var Console $console */
|
||||
$console = $application->get('console');
|
||||
$console->register(Command::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Http;
|
||||
|
||||
|
||||
use Exception;
|
||||
use Server\ServerManager;
|
||||
use Kiri\Abstracts\Component;
|
||||
|
||||
|
||||
/**
|
||||
* Class Shutdown
|
||||
* @package Http
|
||||
*/
|
||||
class Shutdown extends Component
|
||||
{
|
||||
|
||||
|
||||
private string $taskDirectory;
|
||||
private string $workerDirectory;
|
||||
private string $managerDirectory;
|
||||
private string $processDirectory;
|
||||
|
||||
|
||||
private array $_pids = [];
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->taskDirectory = storage(null, 'pid/task');
|
||||
$this->workerDirectory = storage(null, 'pid/worker');
|
||||
$this->managerDirectory = storage(null, 'pid/manager');
|
||||
$this->processDirectory = storage(null, 'pid/process');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function shutdown(): void
|
||||
{
|
||||
clearstatcache(storage());
|
||||
$output = shell_exec('[ -f /.dockerenv ] && echo yes || echo no');
|
||||
if (trim($output) === 'yes') {
|
||||
return;
|
||||
}
|
||||
|
||||
$server = ServerManager::getContext()->getServer();
|
||||
$master_pid = $server->setting['pid_file'] ?? PID_PATH;
|
||||
if (file_exists($master_pid)) {
|
||||
$this->close(file_get_contents($master_pid));
|
||||
}
|
||||
$this->closeOther();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 关闭其他进程
|
||||
*/
|
||||
private function closeOther(): void
|
||||
{
|
||||
$this->directoryCheck($this->managerDirectory);
|
||||
$this->directoryCheck($this->taskDirectory);
|
||||
$this->directoryCheck($this->workerDirectory);
|
||||
$this->directoryCheck($this->processDirectory);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
* check server is running.
|
||||
*/
|
||||
public function isRunning(): bool
|
||||
{
|
||||
$server = ServerManager::getContext()->getServer();
|
||||
$master_pid = $server->setting['pid_file'] ?? PID_PATH;
|
||||
|
||||
if (!file_exists($master_pid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->pidIsExists(file_get_contents($master_pid));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $content
|
||||
* @return bool
|
||||
*/
|
||||
public function pidIsExists($content): bool
|
||||
{
|
||||
if (intval($content) < 1) {
|
||||
return false;
|
||||
}
|
||||
exec('ps -eo pid', $output);
|
||||
$output = array_filter($output, function ($value) {
|
||||
return intval($value);
|
||||
});
|
||||
return in_array(intval($content), $output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public function directoryCheck(string $path): bool
|
||||
{
|
||||
$values = $this->getProcessPidS($path);
|
||||
if (empty($values)) return false;
|
||||
|
||||
$diff = array_diff($values, $this->getPidS());
|
||||
foreach ($diff as $value) {
|
||||
$this->pidIsExists($value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @return array|bool
|
||||
*/
|
||||
private function getProcessPidS($path): bool|array
|
||||
{
|
||||
$values = [];
|
||||
$dir = new \DirectoryIterator($path);
|
||||
if ($dir->getSize() < 1) {
|
||||
return $values;
|
||||
}
|
||||
foreach ($dir as $value) {
|
||||
if ($value->isDot()) continue;
|
||||
|
||||
if (!$value->valid()) continue;
|
||||
|
||||
$_value = file_get_contents($value->getRealPath());
|
||||
if (empty($_value)) {
|
||||
continue;
|
||||
}
|
||||
$values[] = intval($_value);
|
||||
|
||||
@unlink($value->getRealPath());
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getPidS(): array
|
||||
{
|
||||
exec('ps -eo pid', $output);
|
||||
return array_filter($output, function ($value) {
|
||||
return intval($value);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function close(mixed $value)
|
||||
{
|
||||
while ($this->pidIsExists($value)) {
|
||||
exec('kill -15 ' . $value);
|
||||
usleep(100);
|
||||
}
|
||||
clearstatcache($value);
|
||||
if (file_exists($value)) {
|
||||
@unlink($value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user