This commit is contained in:
as2252258@163.com
2021-09-10 03:33:45 +08:00
parent 9915452311
commit a61e663cf0
9 changed files with 1192 additions and 140 deletions
+195
View File
@@ -0,0 +1,195 @@
<?php
namespace Protocol\Message;
use JetBrains\PhpStorm\Pure;
use Kiri\Core\Xml;
use Psr\Http\Message\StreamInterface;
/**
*
*/
trait Message
{
/**
* @var string
*/
protected string $version;
/**
* @var \Psr\Http\Message\StreamInterface
*/
protected StreamInterface $stream;
/**
* @var array
*/
protected array $headers = [];
/**
* @return string
*/
public function getProtocolVersion(): string
{
return $this->version;
}
/**
* @param $version
* @return static
*/
public function withProtocolVersion($version): static
{
$this->version = $version;
return $this;
}
/**
* @return array
*/
public function getHeaders(): array
{
return $this->headers;
}
/**
* @param $name
* @return bool
*/
public function hasHeader($name): bool
{
return array_key_exists($name, $this->headers);
}
/**
* @param $name
* @return string|array|null
*/
#[Pure] public function getHeader($name): string|null|array
{
if (!$this->hasHeader($name)) {
return null;
}
return $this->headers[$name];
}
/**
* @param \Swoole\Http\Request $request
* @return static
*/
public function parseRequestHeaders(\Swoole\Http\Request $request): static
{
$index = strpos($request->getData(), "\r\n\r\n");
$headers = explode("\r\n", substr($request->getData(), 0, $index));
array_shift($headers);
foreach ($headers as $header) {
[$key, $value] = explode(': ', $header);
$this->addRequestHeader($key, $value);
}
return $this;
}
/**
* @param $key
* @param $value
*/
private function addRequestHeader($key, $value)
{
$this->headers[$key] = [$value];
}
/**
* @param $name
* @return string|null
*/
#[Pure] public function getHeaderLine($name): string|null
{
if ($this->hasHeader($name)) {
return implode(';', $this->headers[$name]);
}
return null;
}
/**
* @return string|null
*/
#[Pure] public function getContentType(): ?string
{
return $this->getHeaderLine('Content-Type');
}
/**
* @param $name
* @param $value
* @return static
*/
public function withHeader($name, $value): static
{
if (!is_array($value)) {
$value = [$value];
}
$this->headers[$name] = $value;
return $this;
}
/**
* @param $name
* @param $value
* @return static
* @throws
*/
public function withAddedHeader($name, $value): static
{
if (!array_key_exists($name, $this->headers)) {
throw new \Exception('Headers `' . $name . '` not exists.');
}
$this->headers[$name][] = $value;
return $this;
}
/**
* @param $name
* @return static
*/
public function withoutHeader($name): static
{
unset($this->headers[$name]);
return $this;
}
/**
* @return string
*/
public function getBody(): string
{
return $this->stream->getContents();
}
/**
* @param StreamInterface $body
* @return static
*/
public function withBody(StreamInterface $body): static
{
$this->stream = $body;
return $this;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Protocol\Message;
use Kiri\Core\Xml;
class Parse
{
/**
* @param $content
* @param $contentType
* @return mixed
*/
public static function data($content, $contentType): mixed
{
if (str_contains($contentType, 'json')) {
return json_encode($contentType);
}
if (str_contains($contentType, 'xml')) {
return Xml::toArray($contentType);
}
if (str_contains($contentType, 'x-www-form-urlencoded')) {
parse_str($content, $array);
return $array;
}
if (str_contains($contentType, 'serialize')) {
return unserialize($content);
}
return $content;
}
}
@@ -1,21 +1,32 @@
<?php
namespace Server\Constrict;
namespace Protocol\Message;
use BadMethodCallException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;
use Server\Message\Message;
class TRequest implements RequestInterface
/**
*
*/
abstract class Request implements RequestInterface
{
use Message;
private UriInterface $uri;
/**
* @var \Psr\Http\Message\UriInterface
*/
protected UriInterface $uriInterface;
private string $method;
/**
* @var string
*/
protected string $method;
/**
@@ -23,7 +34,7 @@ class TRequest implements RequestInterface
*/
public function getRequestTarget(): string
{
throw new \BadMethodCallException('Not Accomplish Method.');
throw new BadMethodCallException('Not Accomplish Method.');
}
@@ -33,7 +44,7 @@ class TRequest implements RequestInterface
*/
public function withRequestTarget($requestTarget): static
{
throw new \BadMethodCallException('Not Accomplish Method.');
throw new BadMethodCallException('Not Accomplish Method.');
}
@@ -42,7 +53,6 @@ class TRequest implements RequestInterface
*/
public function getMethod(): string
{
// TODO: Implement getMethod() method.
return $this->method;
}
@@ -53,7 +63,6 @@ class TRequest implements RequestInterface
*/
public function withMethod($method): RequestInterface
{
// TODO: Implement withMethod() method.
$this->method = $method;
return $this;
}
@@ -64,19 +73,18 @@ class TRequest implements RequestInterface
*/
public function getUri(): UriInterface
{
// TODO: Implement getUri() method.
return $this->uri;
return $this->uriInterface;
}
/**
* @param UriInterface $uri
* @param false $preserveHost
* @return $this|TRequest
* @return $this|Request
*/
public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface
{
$this->uri = $uri;
$this->uriInterface = $uri;
return $this;
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
namespace Protocol\Message;
use JetBrains\PhpStorm\Pure;
use Psr\Http\Message\ResponseInterface;
class Response implements ResponseInterface
{
use Message;
protected int $statusCode = 200;
protected string $reasonPhrase = '';
/**
* @return int
*/
public function getStatusCode(): int
{
return $this->statusCode;
}
/**
* @param int $code
* @param string $reasonPhrase
* @return $this|\Protocol\Message\Response
*/
public function withStatus($code, $reasonPhrase = ''): static
{
$this->statusCode = $code;
$this->reasonPhrase = $reasonPhrase;
return $this;
}
/**
* @return string
*/
public function getReasonPhrase(): string
{
return $this->reasonPhrase;
}
/**
* @return string|null
*/
#[Pure] public function getAccessControlAllowOrigin(): ?string
{
return $this->getHeaderLine('Access-Control-Allow-Origin');
}
/**
* @return string|null
*/
#[Pure] public function getAccessControlAllowHeaders(): ?string
{
return $this->getHeaderLine('Access-Control-Allow-Headers');
}
/**
* @return string|null
*/
#[Pure] public function getAccessControlRequestMethod(): ?string
{
return $this->getHeaderLine('Access-Control-Request-Method');
}
}
+270
View File
@@ -0,0 +1,270 @@
<?php
namespace Protocol\Message;
use Http\Context\Context;
use Kiri\Core\Xml;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileInterface;
/**
*
*/
class ServerRequest extends Request implements ServerRequestInterface
{
/**
* @var mixed
*/
protected ?array $parsedBody = null;
/**
* @var array|null
*/
protected ?array $serverParams;
/**
* @var array|null
*/
protected ?array $cookieParams = [];
/**
* @var array|null
*/
protected ?array $queryParams;
/**
* @var array|null
*/
protected ?array $uploadedFiles;
protected \Swoole\Http\Request $serverTarget;
/**
* @param array $server
* @return static
*/
public function withServerParams(array $server): static
{
$this->serverParams = $server;
return $this;
}
/**
* @param \Swoole\Http\Request $server
* @return static
*/
public function withServerTarget(\Swoole\Http\Request $server): static
{
$this->serverTarget = $server;
return $this;
}
/**
* @param \Swoole\Http\Request $request
* @return static
*/
public static function createServerRequest(\Swoole\Http\Request $request): static
{
return (new static())->withServerParams($request->server)
->withServerTarget($request)
->withCookieParams($request->cookie)
->withUri(Uri::parseUri($request))
->withQueryParams($request->get ?? [])
->withUploadedFiles($request->files ?? [])
->withMethod($request->getMethod())
->parseRequestHeaders($request)
->withParsedBody(function (StreamInterface $stream, ?array $posts) {
try {
$content = $stream->getContents();
if (!empty($content)) {
return Parse::data($content, $this->getContentType());
}
return $posts;
} catch (\Throwable $throwable) {
return $posts;
}
});
}
/**
* @param string $name
* @param mixed|null $default
* @return mixed
*/
public function post(string $name, mixed $default = null): mixed
{
return $this->parsedBody[$name] ?? $default;
}
/**
* @param string $name
* @param string|null $default
* @return string|null
*/
public function query(string $name, ?string $default = null): ?string
{
return $this->queryParams[$name] ?? $default;
}
/**
* @param string $name
* @return \Psr\Http\Message\UploadedFileInterface|null
*/
public function file(string $name): ?UploadedFileInterface
{
if (isset($this->parsedBody[$name])) {
$files = $this->parsedBody[$name];
return new Uploaded($files['tmp_name'], $files['name'], $files['type'], $files['size'], $files['error']);
}
return null;
}
/**
* @return null|array
*/
public function getServerParams(): ?array
{
return $this->serverParams;
}
/**
* @return null|array
*/
public function getCookieParams(): ?array
{
return $this->cookieParams;
}
/**
* @param array $cookies
* @return $this|ServerRequest
*/
public function withCookieParams(array $cookies): ServerRequestInterface
{
$this->cookieParams = $cookies;
return $this;
}
/**
* @return array|null
*/
public function getQueryParams(): ?array
{
return $this->queryParams;
}
/**
* @param array $query
* @return \Psr\Http\Message\ServerRequestInterface
*/
public function withQueryParams(array $query): ServerRequestInterface
{
$this->queryParams = $query;
return $this;
}
/**
* @return array|null
*/
public function getUploadedFiles(): ?array
{
return $this->uploadedFiles;
}
/**
* @param array $uploadedFiles
* @return \Psr\Http\Message\ServerRequestInterface
*/
public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
{
$this->uploadedFiles = $uploadedFiles;
return $this;
}
/**
* @return array|object|null
*/
public function getParsedBody(): object|array|null
{
if (empty($this->parsedBody)) {
$callback = Context::getContext('with.parsed.body.callback');
$this->parsedBody = $callback($this->getBody());
}
return $this->parsedBody;
}
/**
* @param array|object|null $data
* @return ServerRequestInterface
*/
public function withParsedBody($data): ServerRequestInterface
{
Context::setContext('with.parsed.body.callback', $data);
return $this;
}
/**
* @return array
*/
public function getAttributes(): array
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @param string $name
* @param null $default
* @return mixed
*/
public function getAttribute($name, $default = null): mixed
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @param string $name
* @param mixed $value
* @return ServerRequestInterface
*/
public function withAttribute($name, $value): ServerRequestInterface
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @param string $name
* @return ServerRequestInterface
*/
public function withoutAttribute($name): ServerRequestInterface
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
}
+237
View File
@@ -0,0 +1,237 @@
<?php
namespace Protocol\Message;
use JetBrains\PhpStorm\ArrayShape;
use Psr\Http\Message\StreamInterface;
class Stream implements StreamInterface
{
/**
* @var string|resource
*/
private mixed $content = '';
/**
* @var int
*/
private int $size = 0;
/**
* @param mixed $stream
*/
public function __construct(mixed $stream = '')
{
$this->content = $stream;
if (!is_resource($stream)) {
$this->size = strlen($stream);
} else {
$state = fstat($this->content);
if ($state) {
$this->size = $state['size'];
}
}
}
/**
* @return string
*/
public function __toString()
{
return $this->content;
}
/**
*
*/
public function close()
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @return resource|null
*/
public function detach()
{
if (!is_resource($this->content)) {
throw new \BadMethodCallException('Not Accomplish Method.');
}
$steam = stream_context_create();
stream_copy_to_stream($this->content, $steam);
return $steam;
}
/**
* @return int
*/
public function getSize(): int
{
return $this->size;
}
/**
* @return bool|int
*/
public function tell(): bool|int
{
if (!is_resource($this->content)) {
throw new \BadMethodCallException('Not Accomplish Method.');
}
return ftell($this->content);
}
/**
* @return bool
*/
public function eof(): bool
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @return bool
*/
public function isSeekable(): bool
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @param int $offset
* @param int $whence
*/
public function seek($offset, $whence = SEEK_SET)
{
if (!is_resource($this->content)) {
throw new \BadMethodCallException('Not Accomplish Method.');
}
fseek($this->content, $offset, $whence);
}
/**
*
*/
public function rewind()
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @return bool
*/
public function isWritable(): bool
{
if (!is_resource($this->content)) {
return true;
}
if (is_writable($this->content)) {
return true;
}
return false;
}
/**
* @param string $string
* @return int
*/
public function write($string): int
{
if (is_resource($this->content)) {
fwrite($this->content, $string);
$state = fstat($this->content);
if ($state) {
$this->size = $state['size'];
}
} else {
$this->content = $string;
$this->size = strlen($string);
}
return $this->size;
}
/**
* @return bool
*/
public function isReadable(): bool
{
if (!is_resource($this->content)) {
return true;
}
if (is_readable($this->content)) {
return true;
}
return false;
}
/**
* @param int $length
* @return false|string
*/
public function read($length): bool|string
{
if (is_resource($this->content)) {
return fread($this->content, $length);
} else {
return $this->content;
}
}
/**
* @return string|bool
*/
public function getContents(): string|bool
{
if (is_resource($this->content)) {
return stream_get_contents($this->content);
} else {
return $this->content;
}
}
/**
* @param null $key
* @return array
*/
#[ArrayShape([
"timed_out" => "bool",
"blocked" => "bool",
"eof" => "bool",
"unread_bytes" => "int",
"stream_type" => "string",
"wrapper_type" => "string",
"wrapper_data" => "mixed",
"mode" => "string",
"seekable" => "bool",
"uri" => "string",
"crypto" => "array",
"mediatype" => "string",
])]
public function getMetadata($key = null): array
{
if (is_resource($this->content)) {
return stream_get_meta_data($this->content);
}
throw new \BadMethodCallException('Not Accomplish Method.');
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
namespace Protocol\Message;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileInterface;
class Uploaded implements UploadedFileInterface
{
const ERROR = [
0 => "There is no error, the file uploaded with success",
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"
];
/**
* @param string $tmp_name
* @param string $name
* @param string $type
* @param int $size
* @param int $error
*/
public function __construct(
public string $tmp_name,
public string $name,
public string $type,
public int $size,
public int $error
)
{
}
/**
* @return StreamInterface
* @throws \Exception
*/
public function getStream(): StreamInterface
{
if (!fopen($this->tmp_name, 'r')) {
throw new \Exception('The file "' . $this->name . '" con\'t readable.');
}
return new Stream(fopen($this->tmp_name, 'r'));
}
/**
* @param string $targetPath
* @return \Psr\Http\Message\StreamInterface
* @throws \Exception
*/
public function moveTo($targetPath): StreamInterface
{
@move_uploaded_file($this->tmp_name, $targetPath);
if (!file_exists($targetPath)) {
throw new \Exception('File save fail.');
}
$this->tmp_name = $targetPath;
return $this->getStream();
}
/**
* @return int
*/
public function getSize(): int
{
return $this->size;
}
/**
* @return int
*/
public function getError(): int
{
return $this->error;
}
/**
* @return string
*/
public function getClientFilename(): string
{
return $this->name;
}
/**
* @return string
*/
public function getClientMediaType(): string
{
return $this->type;
}
}
+255
View File
@@ -0,0 +1,255 @@
<?php
namespace Protocol\Message;
use Psr\Http\Message\UriInterface;
use Swoole\Http\Request;
class Uri implements UriInterface
{
protected string $scheme = '';
protected string $host = '';
protected string $path = '';
protected string $fragment = '';
protected int $port = 80;
protected string $queryString = '';
/**
* @return string
*/
public function getScheme(): string
{
return $this->scheme;
}
/**
* @return string
*/
public function getAuthority(): string
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @return string
*/
public function getUserInfo(): string
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @return string
*/
public function getHost(): string
{
return $this->host;
}
/**
* @return int|null
*/
public function getPort(): ?int
{
return $this->port;
}
/**
* @return string
*/
public function getPath(): string
{
return $this->path;
}
/**
* @return string
*/
public function getQuery(): string
{
return $this->queryString;
}
/**
* @return string
*/
public function getFragment(): string
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @param string $scheme
* @return $this|\Protocol\Message\Uri
*/
public function withScheme($scheme): UriInterface
{
$this->scheme = $scheme;
return $this;
}
/**
* @param string $user
* @param null $password
* @return \Protocol\Message\Uri
*/
public function withUserInfo($user, $password = null): UriInterface
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @param string $host
* @return $this|\Protocol\Message\Uri
*/
public function withHost($host): UriInterface
{
$this->host = $host;
return $this;
}
/**
* @param int|null $port
* @return $this|\Protocol\Message\Uri
*/
public function withPort($port): UriInterface
{
$this->port = $port;
return $this;
}
/**
* @param string $path
* @return $this|\Protocol\Message\Uri
*/
public function withPath($path): UriInterface
{
$this->path = $path;
return $this;
}
/**
* @param string $query
* @return $this|\Protocol\Message\Uri
*/
public function withQuery($query): UriInterface
{
$this->queryString = $query;
return $this;
}
/**
* @param string $fragment
* @return \Protocol\Message\Uri
*/
public function withFragment($fragment): UriInterface
{
throw new \BadMethodCallException('Not Accomplish Method.');
}
/**
* @return string
*/
public function __toString(): string
{
$domain = sprintf('%s://%s', $this->scheme, $this->host);
if (!in_array($this->port, [80, 443])) {
$domain .= ':' . $this->port;
}
if (empty($this->query) && empty($this->fragment)) {
return $domain . $this->path;
}
return sprintf('%s?%s#%s', $domain . $this->path,
$this->queryString, $this->fragment);
}
/**
* @return int
*/
public function getDefaultPort(): int
{
return $this->scheme == 'https' ? 443 : 80;
}
/**
* @param \Swoole\Http\Request $request
* @return UriInterface
*/
public static function parseUri(Request $request): UriInterface
{
$server = $request->server;
$header = $request->header;
$uri = new static();
$uri = $uri->withScheme(!empty($server['https']) && $server['https'] !== 'off' ? 'https' : 'http');
if (isset($request->header['x-forwarded-proto'])) {
$uri->withScheme($request->header['x-forwarded-proto'])->withPort(443);
}
$hasPort = false;
if (isset($server['http_host'])) {
$hostHeaderParts = explode(':', $server['http_host']);
$uri = $uri->withHost($hostHeaderParts[0]);
if (isset($hostHeaderParts[1])) {
$hasPort = true;
$uri = $uri->withPort($hostHeaderParts[1]);
}
} elseif (isset($server['server_name'])) {
$uri = $uri->withHost($server['server_name']);
} elseif (isset($server['server_addr'])) {
$uri = $uri->withHost($server['server_addr']);
} elseif (isset($header['host'])) {
$hasPort = true;
if (strpos($header['host'], ':')) {
[$host, $port] = explode(':', $header['host'], 2);
if ($port != $uri->getDefaultPort()) {
$uri = $uri->withPort($port);
}
} else {
$host = $header['host'];
}
$uri = $uri->withHost($host);
}
if (!$hasPort && isset($server['server_port'])) {
$uri = $uri->withPort($server['server_port']);
}
$hasQuery = false;
if (isset($server['request_uri'])) {
$requestUriParts = explode('?', $server['request_uri']);
$uri = $uri->withPath($requestUriParts[0]);
if (isset($requestUriParts[1])) {
$hasQuery = true;
$uri = $uri->withQuery($requestUriParts[1]);
}
}
if (!$hasQuery && isset($server['query_string'])) {
$uri = $uri->withQuery($server['query_string']);
}
return $uri;
}
}
-127
View File
@@ -1,127 +0,0 @@
<?php
namespace Server\Constrict;
use Psr\Http\Message\ServerRequestInterface;
/**
*
*/
class ServerRequest extends TRequest implements ServerRequestInterface
{
/**
* @return array
*/
public function getServerParams(): array
{
return [];
}
/**
* @return array
*/
public function getCookieParams(): array
{
return $this->cookies;
}
/**
* @param array $cookies
* @return $this|ServerRequest
*/
public function withCookieParams(array $cookies): ServerRequestInterface
{
$this->cookies = $cookies;
return $this;
}
/**
* @return array
*/
public function getQueryParams(): array
{
return [];
}
public function withQueryParams(array $query): ServerRequestInterface
{
return $this;
}
public function getUploadedFiles()
{
// TODO: Implement getUploadedFiles() method.
}
public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
{
return $this;
}
/**
* @return array|object|null
*/
public function getParsedBody(): object|array|null
{
return null;
}
/**
* @param array|object|null $data
* @return ServerRequestInterface
*/
public function withParsedBody($data): ServerRequestInterface
{
return $this;
}
/**
* @return array
*/
public function getAttributes(): array
{
return [];
}
/**
* @param string $name
* @param null $default
* @return mixed|null
*/
public function getAttribute($name, $default = null)
{
return $default;
}
/**
* @param string $name
* @param mixed $value
* @return ServerRequestInterface
*/
public function withAttribute($name, $value): ServerRequestInterface
{
return $this;
}
/**
* @param string $name
* @return ServerRequestInterface
*/
public function withoutAttribute($name): ServerRequestInterface
{
// TODO: Implement withoutAttribute() method.
return $this;
}
}