This commit is contained in:
2021-08-17 16:43:50 +08:00
parent 539ba488e9
commit 5b1e28c323
80 changed files with 232 additions and 396 deletions
+13
View File
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Http\Abstracts;
use Swoole\Coroutine;
abstract class BaseContext
{
protected static array $pool = [];
}
+53
View File
@@ -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);
}
}
+148
View File
@@ -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;
}
}
+806
View File
@@ -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,
]);
}
}
+222
View File
@@ -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);
}
}
+64
View File
@@ -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);
}
}
+109
View File
@@ -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);
}
}
+217
View File
@@ -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;
}
+175
View File
@@ -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;
}
}
+65
View File
@@ -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');
}
}
+64
View File
@@ -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;
}
+34
View File
@@ -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);
}
}
+29
View File
@@ -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
{
}
+197
View File
@@ -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;
}
}
+133
View File
@@ -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);
}
}
+115
View File
@@ -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;
}
}
+388
View File
@@ -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;
}
}
+430
View File
@@ -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.');
}
}
+332
View File
@@ -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());
}
}
+68
View File
@@ -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;
}
}
+22
View File
@@ -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;
}
+25
View File
@@ -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;
}
+33
View File
@@ -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;
}
+22
View File
@@ -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;
}
+21
View File
@@ -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;
}
+26
View File
@@ -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
{
}
+14
View File
@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Http\IInterface;
interface Service
{
public function onInit();
}
+28
View File
@@ -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;
}
+40
View File
@@ -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;
}
}
+47
View File
@@ -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);
}
}
+38
View File
@@ -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;
}
}
+106
View File
@@ -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);
}
}
+401
View File
@@ -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());
}
}
+603
View File
@@ -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);
}
}
}
}
+152
View File
@@ -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();
}
}
+32
View File
@@ -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);
}
}
+181
View File
@@ -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);
}
}
}