From e4f01d949922b16e9d5a63cf49c5f6a8b910c9f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mr=C2=B7x?= Date: Mon, 31 Aug 2020 01:27:08 +0800 Subject: [PATCH] e --- error.php | 31 + function.php | 145 +++ http-server/Abstracts/BaseContext.php | 49 + http-server/Abstracts/HttpService.php | 14 + http-server/Abstracts/MiddlewareHandler.php | 15 + http-server/Abstracts/ServerBase.php | 41 + http-server/Application.php | 51 + http-server/Client/Client.php | 1014 +++++++++++++++++ http-server/Client/Result.php | 187 +++ http-server/Controller.php | 105 ++ http-server/Events/Callback.php | 94 ++ http-server/Events/Http.php | 118 ++ http-server/Events/Packet.php | 44 + http-server/Events/Receive.php | 46 + http-server/Events/Service.php | 128 +++ http-server/Events/Trigger/OnAfterReload.php | 18 + http-server/Events/Trigger/OnBeforeReload.php | 17 + http-server/Events/Trigger/OnClose.php | 17 + http-server/Events/Trigger/OnConnect.php | 38 + http-server/Events/Trigger/OnFinish.php | 26 + http-server/Events/Trigger/OnManagerStart.php | 33 + http-server/Events/Trigger/OnManagerStop.php | 41 + http-server/Events/Trigger/OnPipeMessage.php | 17 + http-server/Events/Trigger/OnShutdown.php | 26 + http-server/Events/Trigger/OnStart.php | 30 + http-server/Events/Trigger/OnTask.php | 140 +++ http-server/Events/Trigger/OnWorkerError.php | 22 + http-server/Events/Trigger/OnWorkerExit.php | 22 + http-server/Events/Trigger/OnWorkerStart.php | 75 ++ http-server/Events/Trigger/OnWorkerStop.php | 23 + http-server/Events/WebSocket.php | 182 +++ http-server/Exception/AuthException.php | 26 + http-server/Exception/RequestException.php | 10 + http-server/Http/Context.php | 196 ++++ http-server/Http/File.php | 94 ++ http-server/Http/Formatter/HtmlFormatter.php | 59 + http-server/Http/Formatter/JsonFormatter.php | 56 + http-server/Http/Formatter/XmlFormatter.php | 87 ++ http-server/Http/HttpHeaders.php | 136 +++ http-server/Http/HttpParams.php | 404 +++++++ http-server/Http/Request.php | 424 +++++++ http-server/Http/Response.php | 226 ++++ http-server/IInterface/AuthIdentity.php | 18 + http-server/IInterface/IFormatter.php | 28 + http-server/IInterface/IMiddleware.php | 18 + http-server/IInterface/RouterInterface.php | 10 + http-server/IInterface/Task.php | 14 + http-server/Route/Any.php | 39 + http-server/Route/CoreMiddleware.php | 39 + http-server/Route/Dispatch/Dispatch.php | 74 ++ http-server/Route/Filter.php | 129 +++ http-server/Route/Filter/BodyFilter.php | 25 + http-server/Route/Filter/Filter.php | 46 + http-server/Route/Filter/FilterException.php | 17 + http-server/Route/Filter/HeaderFilter.php | 25 + http-server/Route/Filter/QueryFilter.php | 25 + http-server/Route/Handler.php | 51 + http-server/Route/Limits.php | 69 ++ http-server/Route/Middleware.php | 77 ++ http-server/Route/Node.php | 313 +++++ http-server/Route/Router.php | 504 ++++++++ http-server/Server.php | 127 +++ http-server/ServerManager.php | 186 +++ http-server/config.php | 111 ++ p.php | 75 ++ socket.html | 55 + system/Abstracts/BaseAnnotation.php | 20 + system/Abstracts/BaseApplication.php | 251 ++++ system/Abstracts/BaseObject.php | 211 ++++ system/Abstracts/Component.php | 145 +++ system/Abstracts/Config.php | 85 ++ system/Abstracts/Configure.php | 18 + system/Abstracts/Pool.php | 156 +++ system/Annotation/Annotation.php | 149 +++ system/Annotation/Definition/Websocket.php | 66 ++ system/Application.php | 71 ++ system/Cache/File.php | 147 +++ system/Cache/ICache.php | 64 ++ system/Cache/Memcached.php | 117 ++ system/Cache/Redis.php | 348 ++++++ system/Console/AbstractConsole.php | 139 +++ system/Console/Application.php | 91 ++ system/Console/Command.php | 38 + system/Console/CommandInterface.php | 15 + system/Console/Console.php | 13 + system/Console/DefaultCommand.php | 38 + system/Console/Dtl.php | 132 +++ system/Console/ICommand.php | 12 + system/Console/Kernel.php | 12 + system/Core/ArrayAccess.php | 99 ++ system/Core/DateFormat.php | 103 ++ system/Core/Help.php | 182 +++ system/Core/JSON.php | 119 ++ system/Core/Reader.php | 171 +++ system/Core/Str.php | 267 +++++ system/Core/Xml.php | 49 + system/Di/Container.php | 232 ++++ system/Di/Service.php | 142 +++ system/Error/ErrorHandler.php | 159 +++ system/Error/ErrorInterface.php | 27 + system/Error/Logger.php | 223 ++++ system/Event.php | 199 ++++ system/Exception/ComponentException.php | 26 + system/Exception/ConfigException.php | 10 + system/Exception/InitException.php | 10 + system/Exception/NotFindClassException.php | 27 + system/Pool/Connection.php | 375 ++++++ system/Pool/Pool.php | 34 + system/Pool/RedisClient.php | 182 +++ system/Process/ISystem.php | 12 + system/Process/Leafleting.php | 30 + system/Process/PrintIn.php | 20 + system/Process/Process.php | 49 + system/Process/ServerInotify.php | 195 ++++ system/Process/System.php | 12 + system/Process/config.php | 13 + system/Processes.php | 119 ++ system/Snowflake.php | 191 ++++ test.php | 15 + 119 files changed, 12232 insertions(+) create mode 100644 error.php create mode 100644 function.php create mode 100644 http-server/Abstracts/BaseContext.php create mode 100644 http-server/Abstracts/HttpService.php create mode 100644 http-server/Abstracts/MiddlewareHandler.php create mode 100644 http-server/Abstracts/ServerBase.php create mode 100644 http-server/Application.php create mode 100644 http-server/Client/Client.php create mode 100644 http-server/Client/Result.php create mode 100644 http-server/Controller.php create mode 100644 http-server/Events/Callback.php create mode 100644 http-server/Events/Http.php create mode 100644 http-server/Events/Packet.php create mode 100644 http-server/Events/Receive.php create mode 100644 http-server/Events/Service.php create mode 100644 http-server/Events/Trigger/OnAfterReload.php create mode 100644 http-server/Events/Trigger/OnBeforeReload.php create mode 100644 http-server/Events/Trigger/OnClose.php create mode 100644 http-server/Events/Trigger/OnConnect.php create mode 100644 http-server/Events/Trigger/OnFinish.php create mode 100644 http-server/Events/Trigger/OnManagerStart.php create mode 100644 http-server/Events/Trigger/OnManagerStop.php create mode 100644 http-server/Events/Trigger/OnPipeMessage.php create mode 100644 http-server/Events/Trigger/OnShutdown.php create mode 100644 http-server/Events/Trigger/OnStart.php create mode 100644 http-server/Events/Trigger/OnTask.php create mode 100644 http-server/Events/Trigger/OnWorkerError.php create mode 100644 http-server/Events/Trigger/OnWorkerExit.php create mode 100644 http-server/Events/Trigger/OnWorkerStart.php create mode 100644 http-server/Events/Trigger/OnWorkerStop.php create mode 100644 http-server/Events/WebSocket.php create mode 100644 http-server/Exception/AuthException.php create mode 100644 http-server/Exception/RequestException.php create mode 100644 http-server/Http/Context.php create mode 100644 http-server/Http/File.php create mode 100644 http-server/Http/Formatter/HtmlFormatter.php create mode 100644 http-server/Http/Formatter/JsonFormatter.php create mode 100644 http-server/Http/Formatter/XmlFormatter.php create mode 100644 http-server/Http/HttpHeaders.php create mode 100644 http-server/Http/HttpParams.php create mode 100644 http-server/Http/Request.php create mode 100644 http-server/Http/Response.php create mode 100644 http-server/IInterface/AuthIdentity.php create mode 100644 http-server/IInterface/IFormatter.php create mode 100644 http-server/IInterface/IMiddleware.php create mode 100644 http-server/IInterface/RouterInterface.php create mode 100644 http-server/IInterface/Task.php create mode 100644 http-server/Route/Any.php create mode 100644 http-server/Route/CoreMiddleware.php create mode 100644 http-server/Route/Dispatch/Dispatch.php create mode 100644 http-server/Route/Filter.php create mode 100644 http-server/Route/Filter/BodyFilter.php create mode 100644 http-server/Route/Filter/Filter.php create mode 100644 http-server/Route/Filter/FilterException.php create mode 100644 http-server/Route/Filter/HeaderFilter.php create mode 100644 http-server/Route/Filter/QueryFilter.php create mode 100644 http-server/Route/Handler.php create mode 100644 http-server/Route/Limits.php create mode 100644 http-server/Route/Middleware.php create mode 100644 http-server/Route/Node.php create mode 100644 http-server/Route/Router.php create mode 100644 http-server/Server.php create mode 100644 http-server/ServerManager.php create mode 100644 http-server/config.php create mode 100644 p.php create mode 100644 socket.html create mode 100644 system/Abstracts/BaseAnnotation.php create mode 100644 system/Abstracts/BaseApplication.php create mode 100644 system/Abstracts/BaseObject.php create mode 100644 system/Abstracts/Component.php create mode 100644 system/Abstracts/Config.php create mode 100644 system/Abstracts/Configure.php create mode 100644 system/Abstracts/Pool.php create mode 100644 system/Annotation/Annotation.php create mode 100644 system/Annotation/Definition/Websocket.php create mode 100644 system/Application.php create mode 100644 system/Cache/File.php create mode 100644 system/Cache/ICache.php create mode 100644 system/Cache/Memcached.php create mode 100644 system/Cache/Redis.php create mode 100644 system/Console/AbstractConsole.php create mode 100644 system/Console/Application.php create mode 100644 system/Console/Command.php create mode 100644 system/Console/CommandInterface.php create mode 100644 system/Console/Console.php create mode 100644 system/Console/DefaultCommand.php create mode 100644 system/Console/Dtl.php create mode 100644 system/Console/ICommand.php create mode 100644 system/Console/Kernel.php create mode 100644 system/Core/ArrayAccess.php create mode 100644 system/Core/DateFormat.php create mode 100644 system/Core/Help.php create mode 100644 system/Core/JSON.php create mode 100644 system/Core/Reader.php create mode 100644 system/Core/Str.php create mode 100644 system/Core/Xml.php create mode 100644 system/Di/Container.php create mode 100644 system/Di/Service.php create mode 100644 system/Error/ErrorHandler.php create mode 100644 system/Error/ErrorInterface.php create mode 100644 system/Error/Logger.php create mode 100644 system/Event.php create mode 100644 system/Exception/ComponentException.php create mode 100644 system/Exception/ConfigException.php create mode 100644 system/Exception/InitException.php create mode 100644 system/Exception/NotFindClassException.php create mode 100644 system/Pool/Connection.php create mode 100644 system/Pool/Pool.php create mode 100644 system/Pool/RedisClient.php create mode 100644 system/Process/ISystem.php create mode 100644 system/Process/Leafleting.php create mode 100644 system/Process/PrintIn.php create mode 100644 system/Process/Process.php create mode 100644 system/Process/ServerInotify.php create mode 100644 system/Process/System.php create mode 100644 system/Process/config.php create mode 100644 system/Processes.php create mode 100644 system/Snowflake.php create mode 100644 test.php diff --git a/error.php b/error.php new file mode 100644 index 00000000..22330053 --- /dev/null +++ b/error.php @@ -0,0 +1,31 @@ + 'ok', + NO_AUTH => '' +]); + +if (!function_exists('message')) { + + /** + * @param $code + * @param $replace + * @param string $default + * @return mixed|string + */ + function message($code, $replace, $default = '') + { + if (!isset(ERROR_MESSAGES[$code])) { + if (!empty($default)) { + return $default; + } + return 'unknown error'; + } + return sprintf(ERROR_MESSAGES[$code], $replace); + } + + +} diff --git a/function.php b/function.php new file mode 100644 index 00000000..c1aebbe2 --- /dev/null +++ b/function.php @@ -0,0 +1,145 @@ +$name; + } else if (Snowflake::has($default)) { + $class = Snowflake::get()->$default; + } else { + $class = Snowflake::createObject($default); + Snowflake::setAlias($name, $default); + } + return $class; + } + + +} + + +if (!function_exists('storage')) { + + /** + * @param string $fileName + * @param string $path + * @return string + * @throws Exception + */ + function storage($fileName = '', $path = '') + { + $basePath = Snowflake::getStoragePath(); +// if (empty($path)) { +// return $basePath . '/' . $fileName; +// } else if (empty($fileName)) { +// return initDir($basePath, $path); +// } + return initDir($basePath, $path) . $fileName; + } + + + /** + * @param $basePath + * @param $path + * @return false|string + * @throws Exception + */ + function initDir($basePath, $path) + { + $explode = array_filter(explode('/', $path)); + foreach ($explode as $value) { + $path .= $value . '/'; + if (!is_dir($basePath . $path)) { +// mkdir($basePath . $path); + } + if (!is_dir($basePath . $path)) { +// throw new Exception('System error, directory ' . $basePath . $path . ' is not writable'); + } + } + return realpath($basePath . $path); + } + + +} + + +if (!function_exists('alias')) { + + /** + * @param $class + * @param $name + */ + function alias($class, $name) + { + Snowflake::setAlias($class, $name); + } + +} + + +if (!function_exists('name')) { + + function name($name) + { + swoole_set_process_name($name); + } + +} + +if (!function_exists('response')) { + + /** + * @return Response|stdClass + * @throws + */ + function response() + { + if (!Snowflake::has('response')) { + return make('response', Response::class); + } + return Snowflake::get()->response; + } + +} + +if (!function_exists('redirect')) { + + function redirect($url) + { + return response()->redirect($url); + } + +} + + + +if (!function_exists('env')) { + + /** + * @param $key + * @param null $default + * @return array|false|string|null + */ + function env($key, $default = null) + { + $env = getenv($key); + if ($env === false) { + return $default; + } + return $env; + } + +} diff --git a/http-server/Abstracts/BaseContext.php b/http-server/Abstracts/BaseContext.php new file mode 100644 index 00000000..c1f51642 --- /dev/null +++ b/http-server/Abstracts/BaseContext.php @@ -0,0 +1,49 @@ + 0) + { + self::$pool[$cid][$key] = $item; + } + + } + + static function delete($key = null) + { + $cid = Coroutine::getuid(); + if ($cid > 0) + { + if($key){ + unset(self::$pool[$cid][$key]); + }else{ + unset(self::$pool[$cid]); + } + } + } +} diff --git a/http-server/Abstracts/HttpService.php b/http-server/Abstracts/HttpService.php new file mode 100644 index 00000000..4d117340 --- /dev/null +++ b/http-server/Abstracts/HttpService.php @@ -0,0 +1,14 @@ +server; + } + + /** + * @param $server + */ + public function setServer($server) + { + $this->server = $server; + } + +} diff --git a/http-server/Application.php b/http-server/Application.php new file mode 100644 index 00000000..7ec41a2c --- /dev/null +++ b/http-server/Application.php @@ -0,0 +1,51 @@ +logger; + $logger->write($message, $category); + $logger->insert(); + } + + /** + * @param $methods + * @return mixed + * @throws Exception + */ + public function __get($methods) + { + if (method_exists($this, $methods)) { + return $this->{$methods}(); + } + $handler = 'get' . ucfirst($methods); + if (method_exists($this, $handler)) { + return $this->{$handler}(); + } + if (property_exists($this, $methods)) { + return $this->$methods; + } + $message = sprintf('method %s::%s not exists.', get_called_class(), $methods); + throw new Exception($message); + } + +} diff --git a/http-server/Client/Client.php b/http-server/Client/Client.php new file mode 100644 index 00000000..10fe3b31 --- /dev/null +++ b/http-server/Client/Client.php @@ -0,0 +1,1014 @@ +ca; + } + + /** + * @param string $ca + */ + public function setCa(string $ca): void + { + $this->ca = $ca; + } + + + /** + * @return string + */ + public function getPort(): string + { + return $this->port; + } + + /** + * @param string $port + */ + public function setPort(string $port): void + { + $this->port = $port; + } + + const POST = 'post'; + const GET = 'get'; + const PUT = 'put'; + const DELETE = 'delete'; + const OPTIONS = 'option'; + + /** + * HttpClient constructor. + */ + private function __construct() + { + } + + /** + * @param $data + */ + public function setData($data) + { + $this->_data = $data; + } + + /** + * @return string + */ + public function getSslCertFile(): string + { + return $this->ssl_cert_file; + } + + /** + * @return string + */ + public function hasSslCertFile(): string + { + return !empty($this->ssl_cert_file) && file_exists($this->ssl_cert_file); + } + + /** + * @return string + */ + public function hasSslKeyFile(): string + { + return !empty($this->ssl_key_file) && file_exists($this->ssl_key_file); + } + + /** + * @param string $ssl_cert_file + */ + public function setSslCertFile(string $ssl_cert_file) + { + $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) + { + $this->ssl_key_file = $ssl_key_file; + } + + /** + */ + public static function NewRequest() + { + return new Client(); + } + + /** + * @param string $name + * @return $this + */ + public function setErrorField(string $name) + { + $this->errorCodeField = $name; + return $this; + } + + /** + * @param $bool + * @return $this + */ + public function setUseSwoole($bool) + { + $this->use_swoole = $bool; + if ($this->use_swoole) { + function_exists('setCli') && setCli(true); + } + return $this; + } + + /** + * @param string $name + * @return $this + */ + public function setErrorMsgField(string $name) + { + $this->errorMsgField = $name; + return $this; + } + + /** + * @param string $host + */ + public function setHost(string $host) + { + $this->host = $this->replaceHost($host); + $match_quest = '/^[a-zA-Z\-]+(\.[a-zA-Z\-])+/'; + if (preg_match($match_quest, $this->host)) { + $this->addHeader('Host', $this->host); + } + } + + + /** + * @param $path + * @param array $data + * @param int $type + * @return Result + */ + public function sendTo($path, array $data, $type = SWOOLE_TCP) + { + $client = new \Swoole\Coroutine\Client($type); + if (empty($this->host) || empty($this->port)) { + return new Result(['code' => 500, 'message' => 'Host and port is null']); + } + if (!$client->connect($this->host, $this->port)) { + return new Result(['code' => 500, 'message' => $client->errMsg]); + } + + $path = '/' . $this->port . '/' . ltrim($path, '/'); + + $params['body'] = $data; + $params['path'] = $path; + $params['header']['request_uri'] = $path; + $params['header']['request_method'] = 'receive'; + + if ($client->send(serialize($params))) { + $recv = $this->timeout > 0 ? $client->recv($this->timeout) : $client->recv(); + $param = ['code' => 0, 'message' => Help::toArray($recv)]; + } else { + $param = ['code' => 500, 'message' => $client->errMsg]; + } + $client->close(); + return new Result($param); + + } + + /** + * @param int $sec + * 设置超时时间 + */ + public function setTimeout(int $sec) + { + $this->timeout = $sec; + } + + + /** + * @param $key + * @param $value + */ + public function setHeader($key, $value) + { + $this->header[$key] = $value; + } + + /** + * @param $key + * @param $value + */ + public function addHeader($key, $value) + { + $this->header[$key] = $value; + } + + /** + * @param null $callback + */ + public function setCallback($callback) + { + $this->callback = $callback; + } + + /** + * @param string $method + */ + public function setMethod(string $method) + { + $this->method = $method; + } + + /** + * @param string $agent + */ + public function setAgent(string $agent) + { + $this->agent = $agent; + } + + /** + * @param bool $isSSL + */ + public function setIsSSL(bool $isSSL) + { + $this->isSSL = $isSSL; + if ($this->isSSL) { + $this->port = 443; + } + } + + /** + * @return bool + */ + public function getIsSSL() + { + return $this->isSSL; + } + + /** + * @param $url + * @param array $data + * @return array|mixed|Result + * @throws Exception + */ + private function request($url, $data = []) + { + $data = $this->paramEncode($data); + if ($this->use_swoole) { + return $this->coroutine($this->matchHost($url), $data); + } else { + return $this->useCurl($url, $data); + } + } + + /** + * @return bool + */ + private function isCli() + { + return function_exists('getIsCli') && getIsCli(); + } + + /** + * @param string $string + * @return bool|string + * @throws Exception + */ + private function matchHost($string = '') + { + if (empty($string)) { + return false; + } + + if ($this->isHttp($string)) { + $string = str_replace('http://', '', $string); + $hostAndUrls = explode('/', $string); + + $this->host = array_shift($hostAndUrls); + $string = implode('/', $hostAndUrls); + } else if ($this->isHttps($string)) { + $string = str_replace('https://', '', $string); + $this->setIsSSL(true); + + $hostAndUrls = explode('/', $string); + + $this->host = array_shift($hostAndUrls); + $string = implode('/', $hostAndUrls); + } else if (empty($this->host)) { + $hostAndUrls = explode('/', $string); + $this->host = array_shift($hostAndUrls); + + $string = implode('/', $hostAndUrls); + } + + if (strpos($this->host, ':') !== false) { + [$this->host, $this->port] = explode(':', $this->host); + } + + if (!$this->checkIsIp($this->host) && Coroutine::getuid() > 0) { + $this->host = System::gethostbyname($this->host); + } + + if (!$this->checkIsIp($this->host) && !$this->isDomainName($this->host)) { + throw new Exception('Client Host error.'); + } + + return $string; + } + + /** + * @param $name + * @return bool|mixed + */ + private function isDomainName($name) + { + if (!preg_match('/^[a-zA-Z\-0-9]+(\.[a-zA-Z\-0-9]+)+[^\/]?/', $name, $out)) { + return false; + } + return $out[0]; + } + + /** + * @param $url + * @param $data + * @return array|Result|mixed + * @throws + */ + private function useCurl($url, $data) + { + if ($this->isHttp($url) || $this->isHttps($url)) { + return $this->curl($url, $data); + } + $url = $this->matchHost(ltrim($url, '/')); + if (!empty($this->port)) { + $this->host .= ':' . $this->port; + } + if ($this->isSSL) { + return $this->curl('https://' . $this->host . '/' . $url, $data); + } else { + return $this->curl('http://' . $this->host . '/' . $url, $data); + } + } + + /** + * @param $host + * @return string|string[] + */ + private function replaceHost($host) + { + 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 + */ + private function checkIsIp($url) + { + return preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $url); + } + + /** + * @param $url + * @return bool + */ + private function isHttp($url) + { + return strpos($url, 'http://') === 0; + } + + /** + * @param $url + * @return bool + */ + private function isHttps($url) + { + return strpos($url, 'https://') === 0; + } + + + /** + * @param $url + * @param array $data + * @return array|mixed|Result + * @throws Exception + * 使用swoole协程方式请求 + */ + private function coroutine($url, $data = []) + { + try { + $client = $this->generate_client($this->host, $url, $data); + if ($client->statusCode < 0) { + throw new Exception($client->errMsg); + } + unset($this->_data); + + $body = $this->resolve($client->getHeaders(), $client->body); + if (!in_array($client->getStatusCode(), [200, 201])) { + if (is_string($body)) { + $message = 'Request error code ' . $client->getStatusCode(); + } else { + $message = $this->searchMessageByData($body); + } + $response['code'] = $client->getStatusCode(); + $response['message'] = $message; + $response['data'] = $body; + $response['header'] = $client->getHeaders(); + + $response = new Result($response); + } else { + $response = $this->structure($body, $data, $client->getHeaders()); + } + } catch (\Throwable $exception) { + $response['code'] = 500; + $response['message'] = $exception->getMessage(); + $response['data'] = array_slice($exception->getTrace(), 0, 6); + $response['header'] = []; + + $response = new Result($response); + } + return $response; + } + + /** + * @return int + */ + private function getHostPort() + { + if (!empty($this->port)) { + return $this->port; + } + $port = 80; + if ($this->isSSL) $port = 443; + return $port; + } + + /** + * @param $host + * @param $url + * @param $data + * @return SClient + */ + private function generate_client($host, $url, $data = []) + { + $client = new SClient($host, $this->getHostPort(), $this->isSSL); + if (strpos($url, '/') !== 0) { + $url = '/' . $url; + } + + $client->set($this->settings()); + if (!empty($this->agent)) { + $this->header['User-Agent'] = $this->agent; + } + if (!empty($this->header)) { + $client->setHeaders($this->header); + } + $client->setMethod(strtoupper($this->method)); + if (strtolower($this->method) == self::GET && !empty($data)) { + $url .= '?' . $data; + } else { + $this->_data = $this->mergeParams($data); + } + + if (!empty($this->_data)) { + $client->setData($this->_data); + } + $client->execute($url); + $client->close(); + return $client; + } + + /** + * @param $newData + * @return mixed + */ + private function mergeParams($newData) + { + if (empty($this->_data)) { + return $this->toRequest($newData); + } else if (empty($newData)) { + return $this->toRequest($this->_data); + } + + $newData = Help::toArray($newData); + $array = Help::toArray($this->_data); + + $params = array_merge($array, $newData); + + return $this->toRequest($params); + } + + + /** + * @param $data + * @return false|mixed|string + */ + private function toRequest($data) + { + 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 (strpos($contentType, 'json') !== false) { + return Help::toJson($data); + } else if (strpos($contentType, 'xml') !== false) { + return Help::toXml($data); + } else { + return http_build_query($data); + } + } + + /** + * @return array + */ + private function settings() + { + $sslCert = $this->getSslCertFile(); + $sslKey = $this->getSslKeyFile(); + $sslCa = $this->getCa(); + + $params = []; + if ($this->timeout > 0) { + $params['timeout'] = $this->timeout; + } + if (empty($sslCert) || empty($sslKey) || empty($sslCa)) { + return $params; + } + + $params['ssl_host_name'] = $this->host; + $params['ssl_cert_file'] = $this->getSslCertFile(); + $params['ssl_key_file'] = $this->getSslKeyFile(); + $params['ssl_verify_peer'] = true; + $params['ssl_cafile'] = $sslCa; + + return $params; + } + + /** + * @param $url + * @param array $data + * @return array|mixed|Result + */ + private function curl($url, $data = []) + { + try { + $output = $this->curlParse($url, $this->mergeParams($data)); + if ($output === FALSE) { + return new Result(['code' => 500, 'message' => $output]); + } + [$header, $body, $status] = $this->explode($output); + if (!in_array($status, [200, 201])) { + $data = new Result(['code' => $status, 'message' => $body, 'header' => $header]); + } else { + $data = $this->structure($body, $data, $header); + } + return $data; + } catch (\Throwable $exception) { + $response['code'] = 500; + $response['message'] = $exception->getMessage(); + $response['data'] = array_slice($exception->getTrace(), 0, 6); + $response['header'] = []; + return new Result($response); + } + } + + /** + * @param $url + * @param $data + * @return bool|string + * @throws Exception + */ + private function curlParse($url, $data) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $this->createRequestUrl($url, $data)); + if ($this->timeout > 0) { + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); // 超时设置 + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout); // 超时设置 + } + curl_setopt($ch, CURLOPT_HEADER, true); + + if ($headers = $this->parseHeaderMat()) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + if (!empty($this->agent)) { + curl_setopt($ch, CURLOPT_USERAGENT, $this->agent); + } + if (file_exists($cert = $this->getSslCertFile())) { + curl_setopt($ch, CURLOPT_SSLCERT, $cert); + } + if (file_exists($key = $this->getSslKeyFile())) { + curl_setopt($ch, CURLOPT_SSLKEY, $key); + } + + curl_setopt($ch, CURLOPT_NOBODY, FALSE); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);//返回内容 + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);// 跟踪重定向 + curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); + + if ($this->method == self::POST) { + curl_setopt($ch, CURLOPT_POST, 1); + } + + if ($this->method != self::GET) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + } + + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($this->method)); + $output = curl_exec($ch); + if ($output === false) { + throw new Exception(curl_error($ch)); + } + curl_close($ch); + return $output; + } + + + /** + * @param $url + * @param $params + * @return array|mixed|Result + * 上传文件 + */ + public function upload($url, $params) + { + try { + $this->method = self::POST; + $output = $this->curlParse($url, $params); + if ($output === FALSE) { + return new Result(['code' => 500, 'message' => $output]); + } + [$header, $body, $status] = $this->explode($output); + if ($status != 200 && $status != 201) { + $data = new Result(['code' => $status, 'message' => $body, 'header' => $header]); + } else { + $data = $this->structure($body, $params, $header); + } + return $data; + } catch (\Throwable $exception) { + $response['code'] = 500; + $response['message'] = $exception->getMessage(); + $response['data'] = array_slice($exception->getTrace(), 0, 6); + $response['header'] = []; + return new Result($response); + } + } + + + /** + * @param $output + * @return array + */ + private function explode($output) + { + [$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); + } else if (strpos($body, "\r\n\r\n") !== false) { + [$header, $body] = explode("\r\n\r\n", $body, 2); + } + $header = explode("\r\n", $header); + + unset($output); + + $status = (int)explode(' ', trim($header[0]))[1]; + $header = $this->headerFormat($header); + + return [$header, $this->resolve($header, $body), $status]; + } + + /** + * @param $url + * @param $data + * @return string + */ + private function createRequestUrl($url, $data) + { + if ($this->isGet()) { + return $url . '?' . $data; + } + return $url; + } + + /** + * @param $data + * @param $body + * @return mixed + */ + private function resolve($data, $body) + { + if (is_array($body)) { + return $body; + } + $type = $data['content-type'] ?? $data['Content-Type'] ?? 'text/html'; + if (strpos($type, 'text/html') !== false) { + return $body; + } else if (strpos($type, 'json') !== false) { + return json_decode($body, true); + } else if (strpos($type, 'xml') !== false) { + return Help::xmlToArray($body); + } else if (strpos($type, 'plain') !== false) { + return Help::toArray($body); + } + return $body; + } + + /** + * @param $headers + * @return array + */ + private function headerFormat($headers) + { + $_tmp = []; + foreach ($headers as $key => $val) { + $trim = explode(': ', trim($val)); + + $_tmp[strtolower($trim[0])] = $trim[1] ?? ''; + } + return $_tmp; + } + + /** + * @param $body + * @param $_data + * @param $header + * @param $statusCode + * @return array|mixed|Result + * 构建返回体 + */ + private function structure($body, $_data, $header = [], $statusCode = 200) + { + $this->setIsSSL(false); + $this->setHeaders([]); + + if ($this->callback !== NULL) { + $result = call_user_func($this->callback, $body, $_data, $header); + $this->setCallback(null); + + return $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 array|mixed|string + */ + private function searchMessageByData($body) + { + $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 + */ + public function isPost() + { + return strtolower($this->method) === self::POST; + } + + /** + * @return bool + * + * check isGet Request + */ + public function isGet() + { + return strtolower($this->method) === self::GET; + } + + /** + * @param $arr + * + * @return array|string + * 将请求参数进行编码 + */ + private function paramEncode($arr) + { + 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 $url + * @param array $data + * @return array|mixed|Result + * @throws + */ + public function post($url, $data = []) + { + $this->setMethod(self::POST); + return $this->request($url, $data); + } + + + /** + * @param $url + * @param array $data + * @return array|mixed|Result + * @throws + */ + public function put($url, $data = []) + { + $this->setMethod(self::PUT); + return $this->request($url, $data); + } + + /** + * @param $url + * @param array $data + * @return array|mixed|Result + * @throws + */ + public function get($url, $data = []) + { + $this->setMethod(self::GET); + return $this->request($url, $data); + } + + /** + * @param $url + * @param array $data + * @return array|mixed|Result + * @throws Exception + */ + public function option($url, $data = []) + { + $this->setMethod(self::OPTIONS); + return $this->request($url, $data); + } + + /** + * @param $url + * @param array $data + * @return array|mixed|Result + * @throws Exception + */ + public function delete($url, $data = []) + { + $this->setMethod(self::DELETE); + return $this->request($url, $data); + } + + /** + * @param $url + * @param array $data + * @return array|mixed|Result + * @throws Exception + */ + public function send($url, $data = []) + { + return $this->request($url, $data); + } + + /** + * @return array + */ + private function parseHeaderMat() + { + if ($this->use_swoole) { + return $this->header; + } + $headers = []; + foreach ($this->header as $key => $val) { + $header = $key . ':' . $val; + if (in_array($header, $headers)) { + continue; + } + $headers[] = $header; + } + $this->header = []; + return $headers; + } + + /** + * @param array $headers + * @return array + */ + public function setHeaders(array $headers) + { + if (empty($headers)) { + return []; + } + foreach ($headers as $key => $val) { + $this->header[$key] = $val; + } + return $this->header; + } +} diff --git a/http-server/Client/Result.php b/http-server/Client/Result.php new file mode 100644 index 00000000..10cea0df --- /dev/null +++ b/http-server/Client/Result.php @@ -0,0 +1,187 @@ + $val) { + $this->$key = $val; + } + } + + /** + * @param $name + * @return mixed|null + */ + public function __get($name) + { + return $this->$name; + } + + /** + * @param $name + * @param $value + * @return $this|void + */ + public function __set($name, $value) + { + $this->$name = $value; + + return $this; + } + + /** + * @return array + */ + public function getHeaders() + { + $_tmp = []; + if (!is_array($this->header)) { + return $_tmp; + } + foreach ($this->header as $key => $val) { + if ($key == 0) { + $_tmp['pro'] = $val; + } else { + $trim = explode(': ', $val); + + $_tmp[strtolower($trim[0])] = $trim[1]; + } + } + return $_tmp; + } + + + /** + * @return array + */ + public function getTime() + { + return [ + 'startTime' => $this->startTime, + 'requestTime' => $this->requestTime, + 'runTime' => $this->runTime, + ]; + } + + /** + * @param $key + * @param $data + * @return $this + * @throws Exception + */ + public function setAttr($key, $data) + { + if (!property_exists($this, $key)) { + throw new Exception('未查找到相应对象属性'); + } + $this->$key = $data; + return $this; + } + + /** + * @param int $status + * @return bool + */ + public function isResultsOK($status = 0) + { + if (!$this->httpIsOk()) { + return false; + } + return $this->code === $status; + } + + /** + * @return bool + */ + public function httpIsOk() + { + return in_array($this->httpStatus, $this->statusCode); + } + + /** + * @return mixed + */ + public function getResponse() + { + $headers = $this->getHeaders(); + if (!isset($headers['content-type'])) { + return $this->data; + } + if (!is_string($this->data)) { + return $this->data; + } + switch (trim($headers['content-type'])) { + case 'application/json; encoding=utf-8'; + case 'application/json;'; + case 'application/json'; + case 'text/plain'; + return json_decode($this->data, true); + break; + } + return $this->data; + } + + /** + * @param $key + * @param $data + * @return $this + */ + public function append($key, $data) + { + $this->data[$key] = $data; + return $this; + } + + /** + * @return mixed + */ + public function getMessage() + { + return $this->message; + } + + /** + * @return mixed + */ + public function getCode() + { + return $this->code; + } +} diff --git a/http-server/Controller.php b/http-server/Controller.php new file mode 100644 index 00000000..a11ec809 --- /dev/null +++ b/http-server/Controller.php @@ -0,0 +1,105 @@ +input = $input; + } + + /** + * @param HttpHeaders $headers + */ + public function setHeaders(HttpHeaders $headers): void + { + $this->headers = $headers; + } + + /** + * @param Request $request + */ + public function setRequest(Request $request): void + { + $this->request = $request; + } + + /** + * @return HttpParams + * @throws Exception + */ + public function getInput(): HttpParams + { + if (!$this->input) { + $this->input = $this->getRequest()->params; + } + return $this->input; + } + + /** + * @return HttpHeaders + * @throws Exception + */ + public function getHeaders(): HttpHeaders + { + if (!$this->headers) { + $this->headers = $this->getRequest()->headers; + } + return $this->headers; + } + + /** + * @return Request + * @throws Exception + */ + public function getRequest(): Request + { + if (!$this->request) { + $this->request = Snowflake::get()->request; + } + return $this->request; + } + + /** + * @param $name + * @return mixed|null + * @throws Exception + */ + public function __get($name) + { + $method = 'get' . ucfirst($name); + if (method_exists($this, $method)) { + return $this->$method(); + } + return parent::__get($name); + } + +} diff --git a/http-server/Events/Callback.php b/http-server/Events/Callback.php new file mode 100644 index 00000000..01e92413 --- /dev/null +++ b/http-server/Events/Callback.php @@ -0,0 +1,94 @@ +container = $container; + } + + + + + /** + * @param $server + * @param $worker_id + * @param $message + * @throws Exception + */ + protected function clear($server, $worker_id, $message) + { + Timer::clearAll(); + $event = Snowflake::get()->event; + + $event->offName(Event::EVENT_AFTER_REQUEST); + $event->offName(Event::EVENT_BEFORE_REQUEST); + $this->eventNotify($message, $event); + + Snowflake::clearProcessId($server->worker_pid); + Logger::write($this->_MESSAGE[$message] . $worker_id); + Logger::clear(); + } + + + + const EVENT_ERROR = 'WORKER:ERROR'; + const EVENT_STOP = 'WORKER:STOP'; + const EVENT_EXIT = 'WORKER:EXIT'; + + + private $_MESSAGE = [ + self::EVENT_ERROR => 'The server error. at No.', + self::EVENT_STOP => 'The server stop. at No.', + self::EVENT_EXIT => 'The server exit. at No.', + ]; + + /** + * @param $message + * @param $event + */ + private function eventNotify($message, $event) + { + switch ($message) { + case self::EVENT_ERROR: + if (!$event->exists(Event::SERVER_WORKER_ERROR)) { + return; + } + $event->trigger(Event::SERVER_WORKER_ERROR); + break; + case self::EVENT_EXIT: + if (!$event->exists(Event::SERVER_WORKER_EXIT)) { + return; + } + $event->trigger(Event::SERVER_WORKER_EXIT); + break; + case self::EVENT_STOP: + if (!$event->exists(Event::SERVER_WORKER_STOP)) { + return; + } + $event->trigger(Event::SERVER_WORKER_STOP); + break; + } + } + +} diff --git a/http-server/Events/Http.php b/http-server/Events/Http.php new file mode 100644 index 00000000..7447041f --- /dev/null +++ b/http-server/Events/Http.php @@ -0,0 +1,118 @@ +application = $application; + } + + + /** + * @param array $settings + * @param array $events + * @param array $config + * @return mixed|void + * @throws NotFindClassException + * @throws ReflectionException + */ + public function set(array $settings, $events = [], $config = []) + { + parent::set($settings); + ServerManager::set($this, $settings, $this->application, $events, $config); + } + + + /** + * @param Request $request + * @param Response $response + * @throws \Exception + */ + public function onHandler(Request $request, Response $response) + { + try { + [$sRequest, $sResponse] = static::setContext($request, $response); + $sResponse->send(Snowflake::get()->router->dispatch(), 200); + } catch (Error | \Throwable $exception) { + if (!isset($sResponse)) { + $response->status(200); + $response->end($exception->getMessage()); + } else { + $sResponse->send($this->format($exception), 200); + } + } finally { + $dividing_line = str_pad('', 100, '-'); + $this->application->debug($dividing_line, 'app'); + } + } + + + /** + * @param $exception + * @return false|int|mixed|string + * @throws Exception + */ + public function format($exception) + { + $errorInfo = [ + 'message' => $exception->getMessage(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine() + ]; + $this->application->error(var_export($errorInfo, true)); + + $code = $exception->getCode() ?? 500; + $trance = array_slice($exception->getTrace(), 0, 10); + Snowflake::get()->logger->write(print_r($trance, true), 'exception'); + + return JSON::to($code, $errorInfo['message']); + } + + + /** + * @param $request + * @param $response + * @return array + * @throws Exception + */ + public static function setContext($request, $response): array + { + $request = Context::setContext('request', HRequest::create($request)); + $response = Context::setContext('response', HResponse::create($response)); + return [$request, $response]; + } + +} diff --git a/http-server/Events/Packet.php b/http-server/Events/Packet.php new file mode 100644 index 00000000..6f901591 --- /dev/null +++ b/http-server/Events/Packet.php @@ -0,0 +1,44 @@ +unpack($data))) { + throw new Exception('Format error.'); + } + $client[] = $this->pack($data); + return $server->sendto(...$client); + } catch (\Throwable $exception) { + $client[] = $this->pack(['message' => $exception->getMessage()]); + return $server->sendto(...$client); + } finally { + $event = Snowflake::get()->event; + $event->trigger(Event::SERVER_WORKER_STOP); + } + } + +} diff --git a/http-server/Events/Receive.php b/http-server/Events/Receive.php new file mode 100644 index 00000000..02a9fc91 --- /dev/null +++ b/http-server/Events/Receive.php @@ -0,0 +1,46 @@ +unpack($data))) { + throw new Exception('Format error.'); + } + $client[] = $this->pack($data); + return $server->send(...$client); + } catch (\Throwable $exception) { + $client[] = $this->pack(['message' => $exception->getMessage()]); + return $server->send(...$client); + } finally { + $event = Snowflake::get()->event; + $event->trigger(Event::SERVER_WORKER_STOP); + } + } + +} diff --git a/http-server/Events/Service.php b/http-server/Events/Service.php new file mode 100644 index 00000000..fc39711a --- /dev/null +++ b/http-server/Events/Service.php @@ -0,0 +1,128 @@ +application = $application; + } + + + /** + * @param array $settings + * @param array $events + * @param array $config + * @return mixed|void + * @throws NotFindClassException + * @throws ReflectionException + */ + public function set(array $settings, $events = [], $config = []) + { + parent::set($settings); + ServerManager::set($this, $settings, $this->application, $events, $config); + } + + + /** + * @param $callbacks + */ + protected function bindCallback($callbacks) + { + if (empty($callbacks) || !is_array($callbacks)) { + return; + } + foreach ($callbacks as $callback) { + $this->on($callback[0], [$this, $callback[1][1]]); + } + } + + + /** + * @param $eventName + * @return array + * @throws NotFindClassException + * @throws ReflectionException + * @throws Exception + */ + protected function createHandler($eventName) + { + $classPrefix = 'HttpServer\Events\Trigger\On' . ucfirst($eventName); + if (!class_exists($classPrefix)) { + throw new Exception('class not found.'); + } + $class = Snowflake::createObject($classPrefix, [Snowflake::get()]); + return [$class, 'onHandler']; + } + + + /** + * @param $data + * @return mixed + * @throws Exception + */ + public function pack($data) + { + $callback = $this->pack; + if (is_callable($callback, true)) { + return $callback($data); + } + return JSON::encode($data); + } + + + /** + * @param $data + * @return mixed + */ + public function unpack($data) + { + $callback = $this->unpack; + if (is_callable($callback, true)) { + return $callback($data); + } + return JSON::decode($data); + } + +} diff --git a/http-server/Events/Trigger/OnAfterReload.php b/http-server/Events/Trigger/OnAfterReload.php new file mode 100644 index 00000000..5e275ba4 --- /dev/null +++ b/http-server/Events/Trigger/OnAfterReload.php @@ -0,0 +1,18 @@ +event; + if (!$event->exists(Event::RECEIVE_CONNECTION)) { + return; + } + $event->trigger(Event::RECEIVE_CONNECTION, [$server, $fd, $reactorId]); + } + + +} diff --git a/http-server/Events/Trigger/OnFinish.php b/http-server/Events/Trigger/OnFinish.php new file mode 100644 index 00000000..6c88e6b3 --- /dev/null +++ b/http-server/Events/Trigger/OnFinish.php @@ -0,0 +1,26 @@ +write(var_export($data, true), 'Task'); + } + +} diff --git a/http-server/Events/Trigger/OnManagerStart.php b/http-server/Events/Trigger/OnManagerStart.php new file mode 100644 index 00000000..b87246fd --- /dev/null +++ b/http-server/Events/Trigger/OnManagerStart.php @@ -0,0 +1,33 @@ +debug('manager start.'); + Snowflake::setProcessId($server->manager_pid); + + $events = Snowflake::get()->event; + if ($events->exists(Event::SERVER_MANAGER_START)) { + $events->trigger(Event::SERVER_MANAGER_START, null, $server); + } + if (Snowflake::isLinux()) { + name('Server Manager.'); + } + } + +} diff --git a/http-server/Events/Trigger/OnManagerStop.php b/http-server/Events/Trigger/OnManagerStop.php new file mode 100644 index 00000000..d8c34406 --- /dev/null +++ b/http-server/Events/Trigger/OnManagerStop.php @@ -0,0 +1,41 @@ +warning('manager stop.'); + + $events = Snowflake::get()->event; + if ($events->exists(Event::SERVER_MANAGER_STOP)) { + $events->trigger(Event::SERVER_MANAGER_STOP, [$server]); + } + +// $runPath = storage(null, 'workerIds'); +// foreach (glob($runPath . '/*') as $item) { +// if (!file_exists($item)) { +// continue; +// } +// @unlink($item); +// } + } + +} diff --git a/http-server/Events/Trigger/OnPipeMessage.php b/http-server/Events/Trigger/OnPipeMessage.php new file mode 100644 index 00000000..9230c145 --- /dev/null +++ b/http-server/Events/Trigger/OnPipeMessage.php @@ -0,0 +1,17 @@ +master_pid); + + $event = Snowflake::get()->event; + if ($event->exists(Event::SERVER_EVENT_START)) { + $event->trigger(Event::SERVER_EVENT_START, null, $server); + } + } + +} diff --git a/http-server/Events/Trigger/OnTask.php b/http-server/Events/Trigger/OnTask.php new file mode 100644 index 00000000..28943c66 --- /dev/null +++ b/http-server/Events/Trigger/OnTask.php @@ -0,0 +1,140 @@ +onContinueTask(...func_get_args()); + } else { + $this->onTask(...func_get_args()); + } + } + + + /** + * @param Server $server + * @param int $task_id + * @param int $from_id + * @param string $data + * + * @return mixed|void + * @throws Exception + * 异步任务 + */ + public function onTask(Server $server, $task_id, $from_id, $data) + { + $time = microtime(TRUE); + if (empty($data)) { + return $server->finish('null data'); + } + $finish = $this->runTaskHandler($data); + if (!$finish) { + $finish = []; + } + $finish['runTime'] = [ + 'startTime' => $time, + 'runTime' => microtime(TRUE) - $time, + 'endTime' => microtime(TRUE), + ]; + $server->finish(json_encode($finish)); + } + + /** + * @param Server $server + * @param Server\Task $task + * @return mixed|void + * @throws Exception + * 异步任务 + */ + public function onContinueTask(Server $server, Server\Task $task) + { + $time = microtime(TRUE); + if (empty($task->data)) { + return $task->finish('null data'); + } + $finish = $this->runTaskHandler($task->data); + if (!$finish) { + $finish = []; + } + $finish['runTime'] = [ + 'startTime' => $time, + 'runTime' => microtime(TRUE) - $time, + 'endTime' => microtime(TRUE), + ]; + $task->finish(json_encode($finish)); + } + + /** + * @param $data + * @return array|null + * @throws Exception + */ + private function runTaskHandler($data) + { + $serialize = $this->before($data); + try { + $params = $serialize->getParams(); + if (is_object($params)) { + $params = get_object_vars($params); + } + $finish['class'] = get_class($serialize); + $finish['params'] = $params; + $finish['status'] = 'success'; + $finish['info'] = $serialize->handler(); + } catch (\Throwable $exception) { + $finish['status'] = 'error'; + $finish['info'] = $this->format($exception); + $this->error($exception, 'Task'); + } finally { + $event = Snowflake::get()->event; + $event->trigger(Event::RELEASE_ALL); + + $this->endCoroutine(); + Timer::clearAll(); + } + return $finish; + } + + /** + * @param $data + * @return ITask|null + */ + protected function before($data) + { + if (empty($serialize = unserialize($data))) { + return null; + } + if (!($serialize instanceof ITask)) { + return null; + } + return $serialize; + } + + /** + * @param $exception + * @return string + */ + private function format($exception) + { + return $exception->getMessage() . " on line " . $exception->getLine() . " at file " . $exception->getFile(); + } + +} diff --git a/http-server/Events/Trigger/OnWorkerError.php b/http-server/Events/Trigger/OnWorkerError.php new file mode 100644 index 00000000..93cb8e5e --- /dev/null +++ b/http-server/Events/Trigger/OnWorkerError.php @@ -0,0 +1,22 @@ +clear($server, $worker_id, self::EVENT_ERROR); + } + +} diff --git a/http-server/Events/Trigger/OnWorkerExit.php b/http-server/Events/Trigger/OnWorkerExit.php new file mode 100644 index 00000000..f1b0fde8 --- /dev/null +++ b/http-server/Events/Trigger/OnWorkerExit.php @@ -0,0 +1,22 @@ +clear($server, $worker_id, self::EVENT_EXIT); + } + +} diff --git a/http-server/Events/Trigger/OnWorkerStart.php b/http-server/Events/Trigger/OnWorkerStart.php new file mode 100644 index 00000000..03c9586f --- /dev/null +++ b/http-server/Events/Trigger/OnWorkerStart.php @@ -0,0 +1,75 @@ +worker_pid); + + $get_name = $this->get_process_name($server, $worker_id); + if (!empty($get_name) && !Snowflake::isMac()) { + swoole_set_process_name($get_name); + } + $this->setWorkerAction($server, $worker_id); + } + + /** + * @param $worker_id + * @param $socket + * @throws Exception + */ + private function setWorkerAction($socket, $worker_id) + { + try { + $event = Snowflake::get()->event; + if ($event->exists(Event::SERVER_WORKER_START)) { + $event->trigger(Event::SERVER_WORKER_START); + } + } catch (\Throwable $exception) { + Logger::write($exception->getMessage(), 'worker'); + } + } + + /** + * @param $socket + * @param $worker_id + * @return string + */ + private function get_process_name($socket, $worker_id) + { + $prefix = 'system:'; + if ($worker_id >= $socket->setting['worker_num']) { + return $prefix . ': Task: No.' . $worker_id; + } else { + return $prefix . ': worker: No.' . $worker_id; + } + } + + +} diff --git a/http-server/Events/Trigger/OnWorkerStop.php b/http-server/Events/Trigger/OnWorkerStop.php new file mode 100644 index 00000000..96c94901 --- /dev/null +++ b/http-server/Events/Trigger/OnWorkerStop.php @@ -0,0 +1,23 @@ +clear($server, $worker_id, self::EVENT_STOP); + } + +} diff --git a/http-server/Events/WebSocket.php b/http-server/Events/WebSocket.php new file mode 100644 index 00000000..8aab923f --- /dev/null +++ b/http-server/Events/WebSocket.php @@ -0,0 +1,182 @@ +application = $application; + parent::__construct($host, $port, $mode, $sock_type); + } + + + /** + * @param array $settings + * @param array $events + * @param $config + * @return mixed|void + * @throws \ReflectionException + * @throws NotFindClassException + */ + public function set(array $settings, $events = [], $config = []) + { + parent::set($settings); + ServerManager::set($this, $settings, $this->application, $events, $config); + } + + + /** + * @param Server $server + * @param Frame $frame + * @throws + */ + public function onMessage(Server $server, Frame $frame) + { + try { + $event = Snowflake::get()->event; + if ($event->exists(Event::SERVER_MESSAGE)) { + $event->trigger(Event::SERVER_MESSAGE, [$server, $frame]); + return; + } + if ($frame->opcode == 0x08) { + return; + } + $json = json_decode($frame->data, true); + + $manager = Snowflake::get()->annotation; + $manager->runWith($this->getName($json), [$frame->fd, $server]); + } catch (Exception $exception) { +// $this->error($exception->getMessage(), __METHOD__, __FILE__); +// $this->addError($exception->getMessage()); + } finally { + $event = Snowflake::get()->event; + $event->trigger(Event::EVENT_AFTER_REQUEST); + Logger::insert(); + } + } + + /** + * @param $json + * @return string + */ + private function getName($json) + { + return 'WEBSOCKET:MESSAGE:' . $json['route']; + } + + /** + * @param SRequest $request + * @param SResponse $response + * @return bool + * @throws Exception + */ + protected function connect($request, $response) + { + $manager = Snowflake::get()->event; + if ($manager->exists(Event::SERVER_HANDSHAKE)) { + return $manager->trigger(Event::SERVER_HANDSHAKE, [$request, $response]); + } + $response->status(502); + $response->end(); + return true; + } + + /** + * @param SRequest $request + * @param SResponse $response + * @return bool|string + * @throws Exception + */ + public function onHandshake(SRequest $request, SResponse $response) + { + /** @var Server $server */ + $secWebSocketKey = $request->header['sec-websocket-key']; + $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; + if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { + return false; + } + $key = base64_encode(sha1( + $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + TRUE + )); + $headers = [ + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Sec-websocket-Accept' => $key, + 'Sec-websocket-Version' => '13', + ]; + if (isset($request->header['sec-websocket-protocol'])) { + $headers['Sec-websocket-Protocol'] = $request->header['sec-websocket-protocol']; + } + foreach ($headers as $key => $val) { + $response->header($key, $val); + } + if (isset($request->get['debug']) && $request->get['debug'] == 'test') { + $response->status(101); + $response->end(); + return true; + } else { + return $this->connect($request, $response); + } + } + + /** + * @param Server $server + * @param int $fd + * @throws Exception + */ + public function onClose(Server $server, int $fd) + { + $event = Snowflake::get()->event; + try { + if ($event->exists(Event::SERVER_CLOSE)) { + $event->trigger(Event::SERVER_CLOSE, [$fd]); + } + } catch (\Throwable $exception) { +// $this->addError($exception->getMessage()); + } finally { + $event->trigger(Event::RELEASE_ALL); + Logger::insert(); + } + } + +} diff --git a/http-server/Exception/AuthException.php b/http-server/Exception/AuthException.php new file mode 100644 index 00000000..93a1a546 --- /dev/null +++ b/http-server/Exception/AuthException.php @@ -0,0 +1,26 @@ + $context]; + } else { + static::$_requests[$id][$key] = $context; + } + } else { + static::$_requests[$id] = $context; + } + return $context; + } + + /** + * @param $id + * @param $context + * @param null $key + * @return + */ + private static function setCoroutine($id, $context, $key = null) + { + if (!static::hasContext($id)) { + Coroutine::getContext()[$id] = []; + } + if (!empty($key)) { + if (!is_array(Coroutine::getContext()[$id])) { + Coroutine::getContext()[$id] = [$key => $context]; + } else { + Coroutine::getContext()[$id][$key] = $context; + } + } else { + Coroutine::getContext()[$id] = $context; + } + return $context; + } + + /** + * @param $id + * @param null $key + * @return false|mixed + */ + public static function autoIncr($id, $key = null) + { + if (!static::inCoroutine()) { + return false; + } + if (!isset(Coroutine::getContext()[$id][$key])) { + return false; + } + return Coroutine::getContext()[$id][$key] += 1; + } + + /** + * @param $id + * @param null $key + * @return false|mixed + */ + public static function autoDecr($id, $key = null) + { + if (!static::inCoroutine()) { + return false; + } + if (!isset(Coroutine::getContext()[$id][$key])) { + return false; + } + return Coroutine::getContext()[$id][$key] -= 1; + } + + /** + * @param $id + * @param null $key + * @return mixed + */ + public static function getContext($id, $key = null) + { + if (static::inCoroutine()) { + $array = Coroutine::getContext()[$id] ?? null; + } else { + $array = static::$_requests[$id] ?? null; + } + if (empty($key) || !is_array($array)) { + return $array; + } + return $array[$key]; + } + + /** + * @return mixed + */ + public static function getAllContext() + { + if (static::inCoroutine()) { + return Coroutine::getContext() ?? []; + } else { + return static::$_requests ?? []; + } + } + + /** + * @param $id + * @param null $key + */ + public static function deleteId($id, $key = null) + { + if (!static::hasContext($id, $key)) { + return; + } + if (static::inCoroutine()) { + if (!empty($key)) { + Coroutine::getContext()[$id][$key] = null; + } else { + Coroutine::getContext()[$id] = null; + } + } else { + unset(static::$_requests[$id]); + } + } + + /** + * @param $id + * @param null $key + * @return mixed + */ + public static function hasContext($id, $key = null) + { + if (static::inCoroutine()) { + $data = Coroutine::getContext()[$id] ?? null; + } else { + $data = static::$_requests[$id] ?? null; + } + if (empty($data)) { + return false; + } + if (empty($key)) { + return true; + } else if (!is_array($data)) { + return false; + } + return isset($data[$key]); + } + + + /** + * @return bool + */ + public static function inCoroutine() + { + return Coroutine::getCid() > 0; + } + +} + + + diff --git a/http-server/Http/File.php b/http-server/Http/File.php new file mode 100644 index 00000000..49e24513 --- /dev/null +++ b/http-server/Http/File.php @@ -0,0 +1,94 @@ + '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) + { + 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 + */ + public function rename() + { + if (!empty($this->newName)) { + return $this->newName; + } + $param = ['tmp_name' => $this->getTmpPath()]; + $this->newName = \BeReborn::rename($param); + return $this->newName; + } + + /** + * @return string + */ + public function getTmpPath() + { + return $this->tmp_name; + } + + /** + * @return bool + * + * check file have error + */ + public function hasError() + { + return $this->error !== 0; + } + + /** + * @return mixed + * + * get upload error info + */ + public function getErrorInfo() + { + if (!isset($this->errorInfo[$this->error])) { + return 'Unknown upload error.'; + } + return $this->errorInfo[$this->error]; + } +} diff --git a/http-server/Http/Formatter/HtmlFormatter.php b/http-server/Http/Formatter/HtmlFormatter.php new file mode 100644 index 00000000..3758795e --- /dev/null +++ b/http-server/Http/Formatter/HtmlFormatter.php @@ -0,0 +1,59 @@ +data = $data; + return $this; + } + + /** + * @return mixed + */ + public function getData() + { + $data = $this->data; + $this->clear(); + return $data; + } + + public function clear() + { + unset($this->data); + } +} diff --git a/http-server/Http/Formatter/JsonFormatter.php b/http-server/Http/Formatter/JsonFormatter.php new file mode 100644 index 00000000..c41af0c8 --- /dev/null +++ b/http-server/Http/Formatter/JsonFormatter.php @@ -0,0 +1,56 @@ +data = $data; + return $this; + } + + /** + * @return mixed + */ + public function getData() + { + $data = $this->data; + $this->clear(); + return $data; + } + + + public function clear() + { + unset($this->data); + } +} diff --git a/http-server/Http/Formatter/XmlFormatter.php b/http-server/Http/Formatter/XmlFormatter.php new file mode 100644 index 00000000..9c9aefb3 --- /dev/null +++ b/http-server/Http/Formatter/XmlFormatter.php @@ -0,0 +1,87 @@ +'); + + $this->toXml($dom, $data); + + $this->data = $dom->saveXML(); + } + return $this; + } + + /** + * @return string + */ + public function getData() + { + $data = $this->data; + $this->clear(); + return $data; + } + + /** + * @param SimpleXMLElement $dom + * @param $data + */ + public function toXml($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($val)); + } + } + } + + public function clear() + { + unset($this->data); + } +} diff --git a/http-server/Http/HttpHeaders.php b/http-server/Http/HttpHeaders.php new file mode 100644 index 00000000..d9ee328a --- /dev/null +++ b/http-server/Http/HttpHeaders.php @@ -0,0 +1,136 @@ +headers = $headers; + } + + /** + * @param $name + * @param $value + */ + public function setHeader($name, $value) + { + $this->response[$name] = $value; + } + + /** + * @param array $headers + */ + public function setHeaders(array $headers) + { + foreach ($headers as $key => $val) { + $this->response[$key] = $val; + } + } + + /** + * @param $name + * @param $value + */ + public function replace($name, $value) + { + $this->headers[$name] = $value; + } + + /** + * @param $name + * @param $value + */ + public function addHeader($name, $value) + { + $this->headers[$name] = $value; + } + + /** + * @param array $headers + * @return $this + */ + public function addHeaders(array $headers) + { + if (empty($headers)) { + return $this; + } + if (!empty($this->headers)) { + $headers = array_merge($this->headers, $headers); + } + $this->headers = $headers; + return $this; + } + + /** + * @return array + */ + public function getResponseHeaders() + { + return $this->response; + } + + /** + * @param $name + * @return mixed|null + */ + public function getHeader($name) + { + return $this->headers[$name] ?? null; + } + + + /** + * @param $name + * @return mixed|string|null + */ + public function get($name) + { + return $this->getHeader($name); + } + + + /** + * @param $name + * @return bool + */ + public function exists($name) + { + return isset($this->headers[$name]) && $this->headers[$name] != null; + } + + + /** + * @return array + */ + public function getHeaders() + { + return $this->headers; + } + +} diff --git a/http-server/Http/HttpParams.php b/http-server/Http/HttpParams.php new file mode 100644 index 00000000..f06e4c9b --- /dev/null +++ b/http-server/Http/HttpParams.php @@ -0,0 +1,404 @@ +body = $body; + $this->gets = $get ?? []; + $this->files = $files ?? []; + } + + /** + * @return int + */ + public function offset() + { + return ($this->page() - 1) * $this->size(); + } + + /** + * @param array $data + * 批量添加数据 + */ + public function setPosts($data) + { + if (!is_array($data)) { + return; + } + foreach ($data as $key => $vla) { + $this->body[$key] = $vla; + } + } + + /** + * @param string $key + * @param string $value + */ + public function addGetParam(string $key, string $value) + { + $this->gets[$key] = $value; + } + + /** + * @return int + */ + private function page() + { + return (int)$this->get('page', 1); + } + + /** + * @return int + */ + public function size() + { + return (int)$this->get('size', 20); + } + + + /** + * @param $name + * @param $defaultValue + * @param $call + * @return mixed|null + */ + public function get($name, $defaultValue = null, $call = null) + { + return $this->gets[$name] ?? $defaultValue; + } + + /** + * @param $name + * @param null $defaultValue + * @param $call + * @return mixed|null + */ + public function post($name, $defaultValue = null, $call = null) + { + $data = $this->body[$name] ?? $defaultValue; + if ($call !== null) { + $data = call_user_func($call, $data); + } + return $data; + } + + /** + * @param $name + * @return false|string + * @throws Exception + */ + public function json($name) + { + $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() + { + return $this->gets; + } + + /** + * @return array + */ + public function params() + { + return array_merge($this->body ?? [], $this->files ?? []); + } + + /** + * @return array + */ + public function load() + { + return array_merge($this->files, $this->body, $this->gets); + } + + /** + * @param $name + * @param array $defaultValue + * @return array|mixed + */ + public function array($name, $defaultValue = []) + { + return $this->body[$name] ?? $defaultValue; + } + + /** + * @param $name + * @return mixed|File|null + * @throws Exception + */ + public function file($name) + { + if (!isset($this->files[$name])) { + return null; + } + $param = $this->files[$name]; + $param['class'] = File::class; + return \BeReborn::createObject($param); + } + + /** + * @param $name + * @param bool $isNeed + * @return mixed|null + * @throws RequestException + */ + private function required($name, $isNeed = false) + { + $int = $this->body[$name] ?? NULL; + if (is_null($int) && $isNeed === true) { + throw new RequestException("You need to add request parameter $name"); + } + return $int; + } + + /** + * @param $name + * @param bool $isNeed + * @param null $min + * @param null $max + * @return int + * @throws Exception + */ + public function int($name, $isNeed = FALSE, $min = NULL, $max = NULL) + { + $int = $this->required($name, $isNeed); + if ($int === null) return null; + if (is_array($min)) { + list($min, $max) = $min; + } + if (is_null($int)) { + $length = 0; + } else { + $length = strlen(floatval($int)); + } + if (!is_numeric($int) || intval($int) != $int) { + throw new RequestException("The request parameter $name must integer."); + } + $this->between($length, $min, $max); + return (int)$int; + } + + /** + * @param $name + * @param bool $isNeed + * @param int $round + * @return float + * @throws Exception + */ + public function float($name, $isNeed = FALSE, $round = 0) + { + $int = $this->required($name, $isNeed); + if ($int === null) { + return null; + } + if ($round > 0) { + return round(floatval($int), $round); + } else { + return floatval($int); + } + } + + /** + * @param $name + * @param bool $isNeed + * @param null $length + * + * @return string + * @throws + */ + public function string($name, $isNeed = FALSE, $length = NULL) + { + $string = $this->required($name, $isNeed); + if ($string === null || $length === null) { + return $string; + } + if (!is_string($string)) { + $string = json_encode($string, JSON_UNESCAPED_UNICODE); + } + $_length = strlen($string); + if (is_array($length)) { + if (count($length) < 2) { + array_unshift($length, 0); + } + $this->between($_length, ...$length); + } else if (is_numeric($length) && $_length != $length) { + throw new RequestException("The length of the string must be $length characters"); + } + return $string; + } + + /** + * @param $_length + * @param $min + * @param $max + * @throws RequestException + */ + private function between($_length, $min, $max) + { + if ($min !== NULL && $_length < $min) { + throw new RequestException("The minimum value cannot be lower than $min"); + } + if ($max !== NULL && $_length > $max) { + throw new RequestException("Maximum cannot exceed $max, has length " . $_length); + } + } + + /** + * @param $name + * @param bool $isNeed + * + * @return string + * @throws RequestException + */ + public function email($name, $isNeed = FALSE) + { + $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 $name + * @param bool $isNeed + * + * @return string + * @throws RequestException + */ + public function bool($name, $isNeed = FALSE) + { + $email = $this->required($name, $isNeed); + if ($email === null) { + return false; + } + return (bool)$email; + } + + /** + * @param $name + * @param null $default + * + * @return mixed|null + * @throws RequestException + */ + public function timestamp($name, $default = NULL) + { + $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 $name + * @param null $default + * + * @return mixed|null + * @throws RequestException + */ + public function datetime($name, $default = 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 $name + * @param null $default + * @return mixed|null + * @throws RequestException + */ + public function ip($name, $default = 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; + } + + + /** + * @param $name + * @return mixed|null + */ + public function __get($name) + { + $load = $this->load(); + + return $load[$name] ?? null; + } + +} diff --git a/http-server/Http/Request.php b/http-server/Http/Request.php new file mode 100644 index 00000000..f936e7b1 --- /dev/null +++ b/http-server/Http/Request.php @@ -0,0 +1,424 @@ +fd = $fd; + } + + /** + * @return bool + */ + public function isFavicon() + { + return $this->getUri() === 'favicon.ico'; + } + + /** + * @return mixed + */ + public function getIdentity() + { + return $this->_grant; + } + + /** + * @return bool + */ + public function isHead() + { + $result = $this->headers->getHeader('request_method') == 'head'; + if ($result) { + $this->setStatus(101); + } else { + $this->setStatus(200); + } + return $result; + } + + /** + * @param $status + * @return mixed + */ + public function setStatus($status) + { + return $this->statusCode = $status; + } + + /** + * @return int + */ + public function getStatus() + { + return $this->statusCode; + } + + /** + * @return bool + */ + public function getIsPackage() + { + return $this->headers->getHeader('request_method') == 'package'; + } + + /** + * @return bool + */ + public function getIsReceive() + { + return $this->headers->getHeader('request_method') == 'receive'; + } + + + /** + * @param $value + */ + public function setGrantAuthorization($value) + { + $this->_grant = $value; + } + + + /** + * @return bool + */ + public function hasGrant() + { + return $this->_grant !== null; + } + + + /** + * @return string + */ + public function parseUri() + { + $array = []; + $explode = explode('/', $this->headers->getHeader('request_uri')); + foreach ($explode as $item) { + if (empty($item)) { + continue; + } + $array[] = $item; + } + return $this->uri = implode('/', ($this->explode = $array)); + } + + /** + * @return string[] + */ + public function getExplode() + { + return $this->explode; + } + + /** + * @return mixed|string + */ + public function getCurrent() + { + return current($this->explode); + } + + /** + * @return string + */ + public function getUri() + { + if (!$this->headers) { + return 'command exec.'; + } + if (!empty($this->uri)) { + return $this->uri; + } + $uri = $this->headers->getHeader('request_uri'); + $uri = ltrim($uri, '/'); + if (empty($uri)) return '/'; + return $uri; + } + + + /** + * @return mixed|string + * @throws Exception + */ + public function adapter() + { + if (!$this->isHead()) { + return router()->runHandler(); + } + return ''; + } + + + /** + * @return string|null + */ + public function getPlatform() + { + $user = $this->headers->getHeader('user-agent'); + $match = preg_match('/\(.*\)?/', $user, $output); + if (!$match || count($output) < 1) { + return null; + } + $output = strtolower(array_shift($output)); + if (strpos('mac', $output)) { + return 'mac'; + } else if (strpos('iphone', $output)) { + return 'iphone'; + } else if (strpos('android', $output)) { + return 'android'; + } else if (strpos('windows', $output)) { + return 'windows'; + } + return null; + } + + /** + * @return bool + */ + public function isIos() + { + return $this->getPlatform() == static::PLATFORM_IPHONE; + } + + /** + * @return bool + */ + public function isAndroid() + { + return $this->getPlatform() == static::PLATFORM_ANDROID; + } + + /** + * @return bool + */ + public function isMacOs() + { + return $this->getPlatform() == static::PLATFORM_MAC_OX; + } + + /** + * @return bool + */ + public function isWindows() + { + return $this->getPlatform() == static::PLATFORM_WINDOWS; + } + + /** + * @return bool + */ + public function getIsPost() + { + return $this->getMethod() == 'post'; + } + + /** + * @return bool + * @throws Exception + */ + public function getIsHttp() + { + if (!app()->has('socket')) { + return false; + } + $socket = \BeReborn::$app->getSocket()->getServer(); + if (empty($this->fd)) { + return false; + } + return $socket->exist($this->fd) && !$socket->isEstablished($this->fd); + } + + /** + * @return bool + */ + public function getIsOption() + { + return $this->getMethod() == 'options'; + } + + /** + * @return bool + */ + public function getIsGet() + { + return $this->getMethod() == 'get'; + } + + /** + * @return bool + */ + public function getIsDelete() + { + return $this->getMethod() == 'delete'; + } + + /** + * @return string + * + * 获取请求类型 + */ + public function getMethod() + { + $head = $this->headers->getHeader('request_method'); + return strtolower($head); + } + + /** + * @return bool + */ + public function getIsCli() + { + 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 + */ + public function getIp() + { + $headers = $this->headers->getHeaders(); + 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 + */ + public function getRuntime() + { + return sprintf('%.5f', microtime(TRUE) - $this->startTime); + } + + /** + * @return string + */ + public function getDebug() + { + $mainstay = sprintf("%.6f", microtime(true)); // 带毫秒的时间戳 + + $timestamp = floor($mainstay); // 时间戳 + $milliseconds = round(($mainstay - $timestamp) * 1000); // 毫秒 + + $datetime = date("Y-m-d H:i:s", $timestamp) . '.' . $milliseconds; + + $tmp = [ + '[Debug ' . $datetime . '] ', + $this->getIp(), + $this->getUri(), + '`' . $this->headers->getHeader('user-agent') . '`', + $this->getRuntime() + ]; + + return implode(' ', $tmp); + } + + + /** + * @param $request + * @return Request + */ + public static function create($request) + { + $sRequest = new Request(); + $sRequest->fd = $request->fd; + $sRequest->startTime = microtime(true); + $sRequest->params = new HttpParams(Help::toArray($request->rawContent()), $request->get, $request->files); + if (!empty($request->post)) { + $sRequest->params->setPosts($request->post ?? []); + } + $headers = $request->server; + if (!empty($request->header)) { + $headers = array_merge($headers, $request->header); + } + $sRequest->headers = new HttpHeaders($headers); + $sRequest->parseUri(); + return $sRequest; + } + + +} diff --git a/http-server/Http/Response.php b/http-server/Http/Response.php new file mode 100644 index 00000000..3156e274 --- /dev/null +++ b/http-server/Http/Response.php @@ -0,0 +1,226 @@ + JsonFormatter::class, + self::XML => XmlFormatter::class, + self::HTML => HtmlFormatter::class + ]; + + public $fd = 0; + + /** + * @param $format + * @return $this + */ + public function setFormat($format) + { + $this->format = $format; + return $this; + } + + /** + * 清理无用数据 + */ + public function clear() + { + $this->fd = 0; + $this->isWebSocket = false; + $this->format = null; + } + + /** + * @return string + */ + public function getContentType() + { + if ($this->format == null || $this->format == static::JSON) { + return 'application/json;charset=utf-8'; + } else if ($this->format == static::XML) { + return 'application/xml;charset=utf-8'; + } else { + return 'text/html;charset=utf-8'; + } + } + + /** + * @return mixed + * @throws Exception + */ + public function sender() + { + return $this->send(func_get_args()); + } + + /** + * @param $key + * @param $value + */ + public function addHeader($key, $value) + { + $response = Context::getContext('response'); + $response->header($key, $value); + } + + /** + * @param string $context + * @param int $statusCode + * @param null $response + * @return bool + * @throws Exception + */ + public function send($context = '', $statusCode = 200, $response = null) + { + $sendData = $this->parseData($context); + if ($response instanceof SResponse) { + $this->response = $response; + } + if ($this->response instanceof SResponse) { + return $this->sendData($this->response, $sendData, $statusCode); + } else { + return $this->printResult($sendData); + } + } + + /** + * @param $context + * @return mixed + * @throws Exception + */ + private function parseData($context) + { + if (isset($this->_format_maps[$this->format])) { + $config['class'] = $this->_format_maps[$this->format]; + } else { + $config['class'] = HtmlFormatter::class; + } + $formatter = Snowflake::createObject($config); + return $formatter->send($context)->getData(); + } + + /** + * @param $result + * @return string + * @throws Exception + */ + private function printResult($result) + { + $result = Help::toString($result); + + $string = 'Command Result: ' . PHP_EOL; + $string .= empty($result) ? 'success!' : $result . PHP_EOL; + $string .= 'Command Success!' . PHP_EOL; + echo $string; + + $event = Snowflake::get()->event; + $event->trigger('CONSOLE_END'); + + return 'ok'; + } + + /** + * @param $response + * @param $sendData + * @param $status + * @return mixed + */ + private function sendData($response, $sendData, $status) + { + $response->status($status); + $response->header('Content-Type', $this->getContentType()); + $response->header('Access-Control-Allow-Origin', '*'); + $response->header('Run-Time', $this->getRuntime()); + return $response->end($sendData); + } + + /** + * @param $url + * @param array $param + * @return int + */ + public function redirect($url, array $param = []) + { + if (!empty($param)) { + $url .= '?' . http_build_query($param); + } + $url = ltrim($url, '/'); + if (!preg_match('/^http/', $url)) { + $url = '/' . $url; + } + return $this->response->redirect($url); + } + + /** + * @param null $response + * @return mixed + * @throws ComponentException + */ + public static function create($response = null) + { + $ciResponse = Snowflake::get()->clone('response'); + $ciResponse->response = $response; + $ciResponse->startTime = microtime(true); + $ciResponse->format = self::JSON; + return $ciResponse; + } + + + /** + * @throws Exception + */ + public function sendNotFind() + { + $this->format = static::HTML; + $this->send('', 404); + } + + /** + * @return string + */ + public function getRuntime() + { + return sprintf('%.5f', microtime(TRUE) - $this->startTime); + } + +} diff --git a/http-server/IInterface/AuthIdentity.php b/http-server/IInterface/AuthIdentity.php new file mode 100644 index 00000000..e65ef7ed --- /dev/null +++ b/http-server/IInterface/AuthIdentity.php @@ -0,0 +1,18 @@ +nodes = $nodes; + } + + + /** + * @param $name + * @param $arguments + * @return $this + */ + public function __call($name, $arguments) + { + foreach ($this->nodes as $node) { + $node->{$name}(...$arguments); + } + return $this; + } + +} diff --git a/http-server/Route/CoreMiddleware.php b/http-server/Route/CoreMiddleware.php new file mode 100644 index 00000000..3d07ea37 --- /dev/null +++ b/http-server/Route/CoreMiddleware.php @@ -0,0 +1,39 @@ +headers; + + /** @var Response $response */ + $response = \BeReborn::getApp('response'); + $request_method = $header->getHeader('access-control-request-method'); + $request_headers = $header->getHeader('access-control-request-headers'); + $response->addHeader('Access-Control-Allow-Headers', $request_headers); + $response->addHeader('Access-Control-Request-Method', $request_method); + + return $next($request); + } + +} diff --git a/http-server/Route/Dispatch/Dispatch.php b/http-server/Route/Dispatch/Dispatch.php new file mode 100644 index 00000000..42677979 --- /dev/null +++ b/http-server/Route/Dispatch/Dispatch.php @@ -0,0 +1,74 @@ +handler = $handler; + $class->request = $request; + if ($handler instanceof \Closure) { + $class->bind(); + } + $class->bindParam(); + return $class; + } + + + /** + * @return mixed + * 执行函数 + */ + public function dispatch() + { + return call_user_func($this->handler, $this->request); + } + + + /** + * 设置作用域 + */ + protected function bind() + { + $this->handler = \Closure::bind($this->handler, new Controller()); + } + + + /** + * 参数绑定 + */ + protected function bindParam() + { + /** @var Controller $controller */ + if (is_array($this->handler)) { + $controller = $this->handler[0]; + } else { + $controller = $this->handler; + } + $request = \BeReborn::getApp('request'); + $controller->setRequest($request); + $controller->setHeaders($request->headers); + $controller->setInput($request->params); + } + +} diff --git a/http-server/Route/Filter.php b/http-server/Route/Filter.php new file mode 100644 index 00000000..1ea818a1 --- /dev/null +++ b/http-server/Route/Filter.php @@ -0,0 +1,129 @@ +rules = []; + $class->params = Input()->params(); + + return $this->_filters[] = $class; + } + + + /** + * @param array $value + * @return HeaderFilter|bool + * @throws Exception + */ + public function setHeader(array $value) + { + if (empty($value)) { + return true; + } + + /** @var HeaderFilter $class */ + $class = \BeReborn::createObject(HeaderFilter::class); + $class->rules = []; + $class->params = request()->headers->getHeaders(); + + return $this->_filters[] = $class; + } + + + /** + * @param array $value + * @return QueryFilter|bool + * @throws Exception + */ + public function setQuery(array $value) + { + if (empty($value)) { + return true; + } + + /** @var QueryFilter $class */ + $class = \BeReborn::createObject(QueryFilter::class); + $class->rules = []; + $class->params = request()->headers->getHeaders(); + + return $this->_filters[] = $class; + } + + + /** + * @throws Exception + */ + public function handler() + { + if (($error = $this->filters()) !== true) { + throw new FilterException($error); + } + if (!$this->grant()) { + throw new AuthException('Authentication error.'); + } + return true; + } + + /** + * @return bool + */ + private function filters() + { + if (empty($this->_filters)) { + return true; + } + foreach ($this->_filters as $filter) { + if (!$filter->check()) { + return false; + } + } + return true; + } + + /** + * @return bool|mixed + */ + private function grant() + { + if (!is_callable($this->grant, true)) { + return true; + } + return call_user_func($this->grant); + } + +} diff --git a/http-server/Route/Filter/BodyFilter.php b/http-server/Route/Filter/BodyFilter.php new file mode 100644 index 00000000..f5ddb425 --- /dev/null +++ b/http-server/Route/Filter/BodyFilter.php @@ -0,0 +1,25 @@ +validator(); + } + +} diff --git a/http-server/Route/Filter/Filter.php b/http-server/Route/Filter/Filter.php new file mode 100644 index 00000000..43fd1857 --- /dev/null +++ b/http-server/Route/Filter/Filter.php @@ -0,0 +1,46 @@ +setParams($this->params); + foreach ($this->rules as $val) { + $field = array_shift($val); + if (empty($val)) { + continue; + } + $validator->make($field, $val); + } + if (!$validator->validation()) { + return $this->addError($validator->getError()); + } + return true; + } + +} diff --git a/http-server/Route/Filter/FilterException.php b/http-server/Route/Filter/FilterException.php new file mode 100644 index 00000000..837c28f3 --- /dev/null +++ b/http-server/Route/Filter/FilterException.php @@ -0,0 +1,17 @@ +validator(); + } + +} diff --git a/http-server/Route/Filter/QueryFilter.php b/http-server/Route/Filter/QueryFilter.php new file mode 100644 index 00000000..914161a5 --- /dev/null +++ b/http-server/Route/Filter/QueryFilter.php @@ -0,0 +1,25 @@ +validator(); + } + +} diff --git a/http-server/Route/Handler.php b/http-server/Route/Handler.php new file mode 100644 index 00000000..da5d4726 --- /dev/null +++ b/http-server/Route/Handler.php @@ -0,0 +1,51 @@ +router = \BeReborn::$app->getRouter(); + + parent::__construct([]); + } + + /** + * @param $config + * @param $handler + */ + public function group($config, $handler) + { + $this->router->group($config, $handler, $this); + } + + + /** + * @param $route + * @param $handler + * @return Handler + */ + public function handler($route, $handler) + { + return $this->router->addRoute($route, $handler, 'receive'); + } + +} diff --git a/http-server/Route/Limits.php b/http-server/Route/Limits.php new file mode 100644 index 00000000..6924e7c8 --- /dev/null +++ b/http-server/Route/Limits.php @@ -0,0 +1,69 @@ +route[$path] = [$limit, $duration, $isBindConsumer]; + return $this; + } + + /** + * @param int $userId + * @return bool + * @throws Exception + * + * 判断有没有被限流 + */ + public function isRestrictedCurrent(int $userId = 0) + { + $path = \request()->getUri(); + if (!isset($this->route[$path])) { + return false; + } + $redis = \BeReborn::getRedis(); + [$limit, $duration, $isBindConsumer] = $this->route[$path]; + if ($limit < 1) { + return false; + } + if ($isBindConsumer && $userId < 1) { + return true; + } + + $uri = md5($path) . '_' . $userId; + if ($redis->incr($uri) > $limit) { + return true; + } + if ($redis->ttl($uri) == -1) { + $redis->expire($uri, $duration); + } + return false; + } + + +} diff --git a/http-server/Route/Middleware.php b/http-server/Route/Middleware.php new file mode 100644 index 00000000..6a7fe448 --- /dev/null +++ b/http-server/Route/Middleware.php @@ -0,0 +1,77 @@ +middleWares[] = $call; + return $this; + } + + /** + * @param array $array + * @return $this + */ + public function setMiddleWares(array $array) + { + $this->middleWares = $array; + return $this; + } + + /** + * @param $dispatch + * @return mixed + * @throws Exception + */ + public function getGenerate($dispatch) + { + $last = function ($passable) use ($dispatch) { + return Dispatch::create($dispatch, $passable)->dispatch(); + }; + $data = array_reduce(array_reverse($this->middleWares), $this->core(), $last); + $this->middleWares = []; + return $data; + } + + /** + * @return Closure + */ + public function core() + { + return function ($stack, $pipe) { + return function ($passable) use ($stack, $pipe) { + if ($pipe instanceof IMiddleware) { + return $pipe->handler($passable, $stack); + } else { + return $pipe($passable, $stack); + } + }; + }; + } + +} diff --git a/http-server/Route/Node.php b/http-server/Route/Node.php new file mode 100644 index 00000000..44c9be6b --- /dev/null +++ b/http-server/Route/Node.php @@ -0,0 +1,313 @@ +handler = $handler; + } else if (is_string($handler) && strpos($handler, '@') !== false) { + list($controller, $action) = explode('@', $handler); + if (!empty($this->namespace)) { + $controller = implode('\\', $this->namespace) . '\\' . $controller; + } + $this->handler = $this->getReflect($controller, $action); + } else if ($handler != null && !is_callable($handler, true)) { + $this->_error = 'Controller is con\'t exec.'; + } else { + $this->handler = $handler; + } + return $this->newExec(); + } + + /** + * @param $request + * @return bool + */ + public function methodAllow(Request $request) + { + if ($this->method == $request->getMethod()) { + return true; + } + return $this->method == 'any'; + } + + /** + * @return bool + * @throws Exception + */ + public function checkSuffix() + { + if ($this->enableHtmlSuffix) { + $url = request()->getUri(); + $nowLength = strlen($this->htmlSuffix); + if (strpos($url, $this->htmlSuffix) !== strlen($url) - $nowLength) { + return false; + } + } + return $this->checkRule(); + } + + /** + * @return bool + * @throws Exception + */ + private function checkRule() + { + if (empty($this->rules)) { + return true; + } + foreach ($this->rules as $rule) { + if (!isset($rule['class'])) { + $rule['class'] = Filter::class; + } + /** @var Filter $object */ + $object = \BeReborn::createObject($rule); + if (!$object->handler()) { + return false; + }; + } + return true; + } + + /** + * @param string $controller + * @param string $action + * @return null|array + * @throws Exception + */ + private function getReflect(string $controller, string $action) + { + try { + $reflect = new \ReflectionClass($controller); + if (!$reflect->isInstantiable()) { + throw new Exception($controller . ' Class is con\'t Instantiable.'); + } + + if (!empty($action) && !$reflect->hasMethod($action)) { + throw new Exception('method ' . $action . ' not exists at ' . $controller . '.'); + } + return [$reflect->newInstance(), $action]; + } catch (Exception $exception) { + $this->_error = $exception->getMessage(); + $this->error($exception->getMessage(), 'router'); + return null; + } + } + + /** + * @return string + * 错误信息 + */ + public function getError() + { + return $this->_error; + } + + /** + * @param Node $node + * @param string $field + * @return Node + */ + public function addChild(Node $node, string $field) + { + /** @var Node $oLod */ + $oLod = $this->childes[$field] ?? null; + if (!empty($oLod)) { + $node = $oLod; + } + $this->childes[$field] = $node; + return $this->childes[$field]; + } + + /** + * @param $rule + * @return $this + */ + public function filter($rule) + { + if (empty($rule)) { + return $this; + } + if (!isset($rule[0])) { + $rule = [$rule]; + } + foreach ($rule as $value) { + if (empty($value)) { + continue; + } + $this->rules[] = $value; + } + return $this; + } + + /** + * @param string $search + * @return Node|mixed + */ + public function findNode(string $search) + { + if (empty($this->childes)) { + return null; + } + + if (isset($this->childes[$search])) { + return $this->childes[$search]; + } + + $_searchMatch = '/<(\w+)?:(.+)?>/'; + foreach ($this->childes as $key => $val) { + if (preg_match($_searchMatch, $key, $match)) { + \Input()->addGetParam($match[1] ?? '--', $search); + return $this->childes[$key]; + } + } + return null; + } + + /** + * @param $options + * @return $this + */ + public function bindOptions($options) + { + if (is_object($options)) { + $this->options = $options; + } else { + $options = array_filter($options); + $last = $options[count($options) - 1]; + if (empty($last)) { + return $this; + } + $this->options = $last; + } + return $this; + } + + /** + * @param string $alias + * @return $this + * 别称 + */ + public function alias(string $alias) + { + $_alias = $alias; + return $this; + } + + + /** + * @param int $limit + * @param int $duration + * @param bool $isBindConsumer + * @return $this + * @throws Exception + */ + public function limits(int $limit, int $duration = 60, bool $isBindConsumer = false) + { + $limits = \BeReborn::$app->getLimits(); + $limits->addLimits($this->path, $limit, $duration, $isBindConsumer); + return $this; + } + + /** + * @param $middles + * @throws + */ + public function bindMiddleware(array $middles) + { + $_tmp = []; + if (empty($middles)) { + return; + } + foreach ($middles as $middle) { + if (empty($middle)) { + continue; + } + try { + if (is_array($middle)) { + $_tmp = $this->each($middle, $_tmp); + } else { + $_tmp[] = \BeReborn::createObject($middle); + } + } catch (Exception $exception) { + } + } + $this->middleware = $_tmp; + $this->newExec(); + } + + + /** + * @throws Exception + */ + private function newExec() + { + if (!empty($this->handler)) { + $made = new Middleware(); + $made->setMiddleWares($this->middleware); + $this->callback = $made->getGenerate($this->handler); + } + return $this; + } + + + /** + * @param $array + * @param $_temp + * @return array + * @throws Exception + */ + private function each($array, $_temp) + { + if (empty($array)) { + return $_temp; + } + foreach ($array as $class) { + if (is_array($class)) { + $_temp = $this->each($class, $_temp); + } else { + $_temp[] = \BeReborn::createObject($class); + } + } + return $_temp; + } +} diff --git a/http-server/Route/Router.php b/http-server/Route/Router.php new file mode 100644 index 00000000..ff6a5d2d --- /dev/null +++ b/http-server/Route/Router.php @@ -0,0 +1,504 @@ +dir = Config::get('controller.path', false, $this->dir); + } + + /** + * @param $path + * @param $handler + * @param string $method + * @return mixed|Node|null + * @throws + */ + public function addRoute($path, $handler, $method = 'any') + { + if (!isset($this->nodes[$method])) { + $this->nodes[$method] = []; + } + + list($first, $explode) = $this->split($path); + $parent = $this->nodes[$method][$first] ?? null; + if ($handler instanceof \Closure) { + $handler = Closure::bind($handler, new Controller()); + } + + if (empty($parent)) { + $parent = $this->NodeInstance($first, 0, $method); + $this->nodes[$method][$first] = $parent; + } + if ($first === '/') { + return $parent->bindHandler($handler); + } + + $parent = $this->bindNode($parent, $explode, $method); + return $parent->bindHandler($handler); + } + + /** + * @param Node $parent + * @param array $explode + * @param $method + * @return Node + */ + private function bindNode($parent, $explode, $method) + { + $a = 0; + if (empty($explode)) { + return $parent->addChild($this->NodeInstance('/', $a, $method), '/'); + } + foreach ($explode as $value) { + if (empty($value)) { + continue; + } + ++$a; + + $search = $parent->findNode($value); + if ($search === null) { + $parent = $parent->addChild($this->NodeInstance($value, $a, $method), $value); + } else { + $parent = $search; + } + } + return $parent; + } + + /** + * @param $route + * @param $handler + * @return Node|mixed|null + */ + public function socket($route, $handler) + { + return $this->addRoute($route, $handler, 'socket'); + } + + + /** + * @param $route + * @param $handler + * @param int $port + * @return Node|mixed|null + */ + public function gRpc($route, $handler, $port = 33007) + { + $route = ltrim($route, '/'); + if (!empty($port)) { + $route = $port . '/' . $route; + } + return $this->addRoute($route, $handler, 'grpc'); + } + + + /** + * @param $route + * @param $handler + * @return Node|mixed|null + */ + public function task($route, $handler) + { + return $this->addRoute($route, $handler, 'Task'); + } + + /** + * @param $route + * @param $handler + * @return mixed|Node|null + * @throws + */ + public function post($route, $handler) + { + return $this->addRoute($route, $handler, 'post'); + } + + /** + * @param $route + * @param $handler + * @return mixed|Node|null + * @throws + */ + public function get($route, $handler) + { + return $this->addRoute($route, $handler, 'get'); + } + + /** + * @param $route + * @param $handler + * @return mixed|Node|null + * @throws + */ + public function options($route, $handler) + { + return $this->addRoute($route, $handler, 'options'); + } + + + /** + * @param $port + * @param Closure $closure + * @throws + */ + public function listen(int $port, Closure $closure) + { + $stdClass = \BeReborn::createObject(Handler::class); + $this->group(['prefix' => $port], $closure, $stdClass); + } + + /** + * @param $route + * @param $handler + * @return Any + */ + public function any($route, $handler) + { + $nodes = []; + foreach (['get', 'post', 'options', 'put', 'delete'] as $method) { + $nodes[] = $this->addRoute($route, $handler, $method); + } + return new Any($nodes); + } + + /** + * @param $route + * @param $handler + * @return mixed|Node|null + * @throws + */ + public function delete($route, $handler) + { + return $this->addRoute($route, $handler, 'delete'); + } + + /** + * @param $route + * @param $handler + * @return mixed|Node|null + * @throws + */ + public function put($route, $handler) + { + return $this->addRoute($route, $handler, 'put'); + } + + /** + * @param $value + * @param $index + * @param $method + * @return Node + * @throws + */ + public function NodeInstance($value, $index = 0, $method = 'get') + { + $node = new Node(); + $node->childes = []; + $node->path = $value; + $node->index = $index; + $node->method = $method; + + $name = array_column($this->groupTacks, 'namespace'); + + $dir = array_column($this->groupTacks, 'dir'); + if (!empty($dir)) { + array_unshift($name, implode('\\', $dir)); + } else { + if ($method == 'receive') { + $dir = 'App\\Tcp'; + } else if ($method == 'package') { + $dir = 'App\\Udp'; + } else { + $dir = $this->dir; + } + array_unshift($name, $dir); + } + + if (!empty($name) && $name = array_filter($name)) { + $node->namespace = $name; + } + + $name = array_column($this->groupTacks, 'middleware'); + if (!empty($name) && $name = array_filter($name)) { + $node->bindMiddleware($name); + } + + $options = array_column($this->groupTacks, 'options'); + if (!empty($options) && is_array($options)) { + $node->bindOptions($options); + } + + $rules = array_column($this->groupTacks, 'filter'); + $rules = array_shift($rules); + if (!empty($rules) && is_array($rules)) { + $node->filter($rules); + } + + return $node; + } + + /** + * @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() + { + $prefix = array_column($this->groupTacks, 'prefix'); + + $prefix = array_filter($prefix); + + if (empty($prefix)) { + return ''; + } + + return '/' . implode('/', $prefix); + } + + /** + * @param array $explode + * @param $method + * @return Node|null + * 查找指定路由 + */ + public function tree_search($explode, $method) + { + if (empty($explode)) { + return $this->nodes[$method]['/'] ?? null; + } + $first = array_shift($explode); + if (!($parent = $this->nodes[$method][$first] ?? null)) { + return null; + } + if (empty($explode)) { + return $parent->findNode('/'); + } + while ($value = array_shift($explode)) { + $node = $parent->findNode($value); + if (!$node) { + break; + } + $parent = $node; + } + return $parent; + } + + /** + * @param $path + * @return array + * '*' + */ + public function split($path) + { + $prefix = $this->addPrefix(); + $path = ltrim($path, '/'); + if (!empty($prefix)) { + $path = $prefix . '/' . $path; + } + + $explode = array_filter(explode('/', $path)); + if (empty($explode)) { + return ['/', []]; + } + + $first = array_shift($explode); + if (empty($explode)) { + $explode = []; + } + return [$first, $explode]; + } + + /** + * @return array + */ + public function each() + { + $paths = []; + foreach ($this->nodes as $node) { + /** @var Node[] $node */ + foreach ($node as $_node) { + if ($_node->path == '/') { + continue; + } + $path = strtoupper($_node->method) . ' : ' . $_node->path; + if (!empty($_node->childes)) { + $path = $this->readByChild($_node->childes, $path); + } + $paths[] = $path; + } + } + return $this->readByArray($paths); + } + + /** + * @param $array + * @param array $returns + * @return array + */ + private function readByArray($array, $returns = []) + { + foreach ($array as $value) { + if (empty($value)) { + continue; + } + if (is_array($value)) { + $returns = $this->readByArray($value, $returns); + } else { + [$method, $route] = explode(' : ', $value); + + $returns[] = ['method' => $method, 'route' => $route]; + } + } + return $returns; + } + + + /** + * @param $child + * @param string $paths + * @return array + */ + private function readByChild($child, $paths = '') + { + $newPath = []; + /** @var Node $item */ + foreach ($child as $item) { + if ($item->path == '/') { + continue; + } + if (!empty($item->childes)) { + $newPath[] = $this->readByChild($item->childes, $paths . '/' . $item->path); + } else { + [$first, $route] = explode(' : ', $paths); + + $newPath[] = strtoupper($item->method) . ' : ' . $route . '/' . $item->path; + } + + } + return $newPath; + } + + /** + * @return mixed + * @throws + */ + public function dispatch() + { + $request = Context::getContext('request'); + if (!($node = $this->find_path($request))) { + return JSON::to(404, 'Page not found.'); + } + if (empty($node->callback)) { + return JSON::to(404, 'Page not found.'); + } + return call_user_func($node->callback, $request); + } + + /** + * @param $request + * @return Node|false|int|mixed|string|null + */ + private function find_path($request) + { + $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() + { + try { + $this->loadDir(APP_PATH . '/routes'); + } catch (Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * @param $path + * @throws Exception + * 加载目录下的路由文件 + */ + private function loadDir($path) + { + try { + $files = glob($path . '/*'); + for ($i = 0; $i < count($files); $i++) { + if (is_dir($files[$i])) { + $this->loadDir($files[$i]); + } else { + $this->loadFile($files[$i]); + } + } + } catch (Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * @param $file + */ + private function loadFile($file) + { + $router = $this; + include_once "Router.php"; + } + +} diff --git a/http-server/Server.php b/http-server/Server.php new file mode 100644 index 00000000..06b8beb2 --- /dev/null +++ b/http-server/Server.php @@ -0,0 +1,127 @@ + '127.0.0.1', 'port'=> 5775, 'mode'=> SWOOLE_TCP], + * ['host'=> '127.0.0.1', 'port'=> 5775, 'mode'=> SWOOLE_TCP], + * ['host'=> '127.0.0.1', 'port'=> 5775, 'mode'=> SWOOLE_TCP], + * ['host'=> '127.0.0.1', 'port'=> 5775, 'mode'=> SWOOLE_TCP], + * ['host'=> '127.0.0.1', 'port'=> 5775, 'mode'=> SWOOLE_UDP], + * ['host'=> '127.0.0.1', 'port'=> 5775, 'mode'=> SWOOLE_TCP] + * ] + */ +class Server extends Application +{ + const HTTP = 'HTTP'; + const TCP = 'TCP'; + const PACKAGE = 'PACKAGE'; + const WEBSOCKET = 'WEBSOCKET'; + + private $server = [ + 'HTTP' => [SWOOLE_TCP, Http::class], + 'TCP' => [SWOOLE_TCP, Receive::class], + 'PACKAGE' => [SWOOLE_UDP, Packet::class], + 'WEBSOCKET' => [SWOOLE_SOCK_TCP, WebSocket::class], + ]; + + /** + * @param array $configs + * @return array + * @throws Exception + */ + public function initCore(array $configs) + { + $response = []; + foreach ($configs as $server) { + $response[] = $this->create($server); + } + return $response; + } + + + /** + * @param $config + * @return mixed + * @throws Exception + */ + private function create($config) + { + $settings = $config['settings'] ?? []; + if (!isset($this->server[$config['type']])) { + throw new Exception('Unknown server type(' . $config['type'] . ').'); + } + $server = $this->dispatchCreate($config, $settings); + if (isset($config['events'])) { + $this->createEventListen($config); + } + return $server; + } + + + /** + * @param $config + */ + protected function createEventListen($config) + { + if (!is_array($config['events'])) { + return; + } + $event = Snowflake::get()->event; + foreach ($config['events'] as $name => $_event) { + $event->on($name, $_event); + } + } + + /** + * @param $config + * @param $settings + * @return mixed + * @throws Exception + */ + private function dispatchCreate($config, $settings) + { + switch ($config['type']) { + case self::HTTP: + $handler = [ + ['request', [Http::class, 'onHandler']] + ]; + break; + case self::TCP: + $handler = [ + ['receive', [Receive::class, 'onReceive']] + ]; + break; + case self::PACKAGE: + $handler = [ + ['packet', [Packet::class, 'onHandler']] + ]; + break; + case self::WEBSOCKET: + $handler = [ + ['handshake', [WebSocket::class, 'onHandshake']], + ['message', [WebSocket::class, 'onMessage']], + ['close', [WebSocket::class, 'onClose']], + ]; + break; + default: + throw new Exception('Unknown server type(' . $config['type'] . ').'); + } + return [$this->server[$config['type']], $config, $handler, $settings]; + } + + +} diff --git a/http-server/ServerManager.php b/http-server/ServerManager.php new file mode 100644 index 00000000..7eb419e5 --- /dev/null +++ b/http-server/ServerManager.php @@ -0,0 +1,186 @@ +set($settings ?? [], $handlers, $config); + static::notice($application, $workerId, $config); + if (property_exists($server, 'pack')) { + $server->pack = $config['message']['pack'] ?? function ($data) { + return $data; + }; + } + if (property_exists($server, 'unpack')) { + $server->unpack = $config['message']['unpack'] ?? function ($data) { + return $data; + }; + } + return $server->start(); + } + + + /** + * @param $application + * @param $config + * @param $category + * @return array + */ + protected static function parameter($application, $config, $category) + { + return [$application, $config['host'], $config['port'], SWOOLE_PROCESS, $category[0]]; + } + + + /** + * @param $process + * @param $application + * @param $pool + * @param $workerId + * @return mixed + */ + protected static function createProcess($process, $application, $pool, $workerId) + { + $process = new $process($application); + $application->debug(sprintf('Worker #%d is running.', $workerId)); + return $process->start($pool->getProcess($workerId)); + } + + + /** + * @param $application + * @param $workerId + * @param $config + */ + protected static function notice($application, $workerId, $config) + { + $application->debug(sprintf('Worker #%d Listener %s::%d is running.', $workerId, $config['host'], $config['port'])); + } + + /** + * @param $server + * @param $settings + * @param \Snowflake\Application $application + * @param array $events + * @param array $config + * @return mixed|void + * @throws NotFindClassException + * @throws ReflectionException + */ + public static function set($server, $settings, $application, $events = [], $config = []) + { + $server->on('start', static::createHandler('start')); + $server->on('workerStop', static::createHandler('workerStop')); + $server->on('workerExit', static::createHandler('workerExit')); + $server->on('workerStart', static::createHandler('workerStart')); + $server->on('workerError', static::createHandler('workerError')); + $server->on('managerStop', static::createHandler('managerStop')); + $server->on('managerStart', static::createHandler('managerStart')); + static::addListener($server, $application, $config); + static::bindCallback($server, $events); + static::addTask($server, $settings); + } + + + /** + * @param $server + * @param $settings + * @throws NotFindClassException + * @throws ReflectionException + */ + protected static function addTask($server, $settings) + { + if (($taskNumber = $settings['task_worker_num'] ?? 0) > 0) { + $server->on('finish', static::createHandler('finish')); + $callback = static::createHandler('task'); + if ($settings['task_enable_coroutine'] ?? false) { + $server->on('task', [$callback, 'onContinueTask']); + } else { + $server->on('task', [$callback, 'onTask']); + } + } + } + + + /** + * @param $server + * @param $application + * @param $config + * @return void + */ + protected static function addListener($server, $application, $config) + { + $grpc = $config['grpc'] ?? []; + if (empty($grpc) || !is_array($grpc)) { + return; + } + $listener = $server->addListener($grpc['host'], $grpc['port'], $grpc['mode']); + $listener->set($grpc['settings'] ?? []); + if (!isset($grpc['receive'])) { + $application->error(sprintf('must add listener %s::%s callback', $grpc['host'], $grpc['port'])); + return; + } + if ($grpc['receive'] instanceof \Closure) { + $grpc['receive'] = \Closure::bind($grpc['receive'], $server); + } + $listener->on('receive', $grpc['receive']); + } + + + /** + * @param $server + * @param $callbacks + */ + protected static function bindCallback($server, $callbacks) + { + if (empty($callbacks) || !is_array($callbacks)) { + return; + } + foreach ($callbacks as $callback) { + $server->on($callback[0], [$server, $callback[1][1]]); + } + } + + + /** + * @param $eventName + * @return array + * @throws NotFindClassException + * @throws ReflectionException + * @throws Exception + */ + protected static function createHandler($eventName) + { + $classPrefix = 'HttpServer\Events\Trigger\On' . ucfirst($eventName); + if (!class_exists($classPrefix)) { + throw new Exception('class not found.'); + } + $class = Snowflake::createObject($classPrefix, [Snowflake::get()]); + return [$class, 'onHandler']; + } + +} diff --git a/http-server/config.php b/http-server/config.php new file mode 100644 index 00000000..4242017c --- /dev/null +++ b/http-server/config.php @@ -0,0 +1,111 @@ + [ + [ + 'type' => Server::HTTP, + 'host' => '127.0.0.1', + 'port' => 9527, + 'settings' => [ + 'worker_num' => 10, + 'enable_coroutine' => 1 + ], + 'events' => [ + Event::SERVER_WORKER_START => function () { + $router = Snowflake::get()->router; + $router->loader(); + }, + ] + ], + [ + 'type' => Server::PACKAGE, + 'host' => '127.0.0.1', + 'port' => 9628, + 'settings' => [ + 'worker_num' => 10, + 'enable_coroutine' => 1 + ], + 'message' => [ + 'pack' => function ($data) { + return \Snowflake\Core\JSON::encode($data); + }, + 'unpack' => function ($data) { + return \Snowflake\Core\JSON::decode($data); + }, + ], + 'events' => [ + ] + ], + [ + 'type' => Server::TCP, + 'host' => '127.0.0.1', + 'port' => 9629, + 'settings' => [ + 'worker_num' => 10, + 'enable_coroutine' => 1 + ], + 'message' => [ + 'pack' => function ($data) { + var_dump($data); + return \Snowflake\Core\JSON::encode($data); + }, + 'unpack' => function ($data) { + return \Snowflake\Core\JSON::decode($data); + }, + ], + 'events' => [ + Event::RECEIVE_CONNECTION => function ($data) { + return 'hello word~'; + } + ] + ], + [ + 'type' => Server::WEBSOCKET, + 'host' => '127.0.0.1', + 'port' => 9530, + 'settings' => [ + 'worker_num' => 10, + 'enable_coroutine' => 1 + ], + 'grpc' => [ + 'host' => '127.0.0.1', + 'port' => 5555, + 'mode' => SWOOLE_SOCK_TCP, + 'receive' => function ($server, int $fd, int $reactorId, string $data) { + $server->push(1, 'success.'); + $server->send($fd, 'success.'); + }, + 'settings' => [] + ], + 'events' => [ + Event::SERVER_WORKER_START => function () { + $websocket = Snowflake::get()->annotation->websocket; +// $websocket->path = $this->socketControllers; + $websocket->namespace = 'App\\Sockets\\'; + $websocket->registration_notes(); + }, + Event::SERVER_HANDSHAKE => function (Request $request, Response $response) { + $this->error($request->fd . ' connect.'); + $response->status(101); + $response->end(); + }, + Event::SERVER_MESSAGE => function (\Swoole\WebSocket\Server $server, Frame $frame) { + $this->error('websocket SERVER_MESSAGE.'); + return $server->push($frame->fd, 'hello word~'); + }, + Event::SERVER_CLOSE => function (int $fd) { + $this->error($fd . ' disconnect.'); + return 'hello word~'; + } + ] + ], + ] +]; diff --git a/p.php b/p.php new file mode 100644 index 00000000..abd21e9e --- /dev/null +++ b/p.php @@ -0,0 +1,75 @@ +setHost('127.0.0.1'); + $client->setPort(9628); + $client->setErrorField('code'); + $client->setErrorMsgField('message'); + var_dump($client->sendTo('', [],SWOOLE_UDP)->getMessage()); + + $client = HttpServer\Client\Client::NewRequest(); + $client->setHost('127.0.0.1'); + $client->setPort(9629); + $client->setErrorField('code'); + $client->setErrorMsgField('message'); + var_dump($client->sendTo('', [],SWOOLE_TCP)->getMessage()); + + $client = HttpServer\Client\Client::NewRequest(); + $client->setHost('127.0.0.1'); + $client->setPort(5555); + $client->setErrorField('code'); + $client->setErrorMsgField('message'); + var_dump($client->sendTo('', [],SWOOLE_SOCK_TCP)->getMessage()); + + $client = HttpServer\Client\Client::NewRequest(); + $client->setHost('127.0.0.1'); + $client->setPort(9527); + $client->setErrorField('code'); + $client->setErrorMsgField('message'); + var_dump($client->send('', [])->getMessage()); +}); +$mail = new \PHPMailer\PHPMailer\PHPMailer(true); + +try { + //Server settings + $mail->SMTPDebug = \PHPMailer\PHPMailer\SMTP::DEBUG_SERVER; // Enable verbose debug output + $mail->isSMTP(); // Send using SMTP + $mail->Host = 'smtp1.example.com'; // Set the SMTP server to send through + $mail->SMTPAuth = true; // Enable SMTP authentication + $mail->Username = 'user@example.com'; // SMTP username + $mail->Password = 'secret'; // SMTP password + $mail->SMTPSecure = \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS; // Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` encouraged + $mail->Port = 587; // TCP port to connect to, use 465 for `PHPMailer::ENCRYPTION_SMTPS` above + + //Recipients + $mail->setFrom('from@example.com', 'Mailer'); + $mail->addAddress('joe@example.net', 'Joe User'); // Add a recipient + $mail->addAddress('ellen@example.com'); // Name is optional + $mail->addReplyTo('info@example.com', 'Information'); + $mail->addCC('cc@example.com'); + $mail->addBCC('bcc@example.com'); + + // Attachments + $mail->addAttachment('/var/tmp/file.tar.gz'); // Add attachments + $mail->addAttachment('/tmp/image.jpg', 'new.jpg'); // Optional name + + // Content + $mail->isHTML(true); // Set email format to HTML + $mail->Subject = 'Here is the subject'; + $mail->Body = 'This is the HTML message body in bold!'; + $mail->AltBody = 'This is the body in plain text for non-HTML mail clients'; + + $mail->send(); + echo 'Message has been sent'; +} catch (Exception $e) { + echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}"; +} + diff --git a/socket.html b/socket.html new file mode 100644 index 00000000..3e48305f --- /dev/null +++ b/socket.html @@ -0,0 +1,55 @@ + + + + + Title + + + +

+
+
+
diff --git a/system/Abstracts/BaseAnnotation.php b/system/Abstracts/BaseAnnotation.php
new file mode 100644
index 00000000..26346476
--- /dev/null
+++ b/system/Abstracts/BaseAnnotation.php
@@ -0,0 +1,20 @@
+moreComponents();
+		$this->parseInt($config);
+		$this->initErrorHandler();
+		$this->enableEnvConfig();
+
+		Component::__construct($config);
+	}
+
+
+	/**
+	 * @return mixed
+	 */
+	public function enableEnvConfig()
+	{
+		if (!file_exists($this->envPath)) {
+			return [];
+		}
+		$lines = $this->readLinesFromFile($this->envPath);
+		foreach ($lines as $line) {
+			if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
+				[$key, $value] = explode('=', $line);
+				putenv(trim($key) . '=' . trim($value));
+			}
+		}
+		return $lines;
+	}
+
+
+	/**
+	 * Read lines from the file, auto detecting line endings.
+	 *
+	 * @param string $filePath
+	 *
+	 * @return array
+	 */
+	protected function readLinesFromFile($filePath)
+	{
+		// Read file into an array of lines with auto-detected line endings
+		$autodetect = ini_get('auto_detect_line_endings');
+		ini_set('auto_detect_line_endings', '1');
+		$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+		ini_set('auto_detect_line_endings', $autodetect);
+
+		return $lines;
+	}
+
+	/**
+	 * Determine if the line in the file is a comment, e.g. begins with a #.
+	 *
+	 * @param string $line
+	 *
+	 * @return bool
+	 */
+	protected function isComment($line)
+	{
+		$line = ltrim($line);
+
+		return isset($line[0]) && $line[0] === '#';
+	}
+
+	/**
+	 * Determine if the given line looks like it's setting a variable.
+	 *
+	 * @param string $line
+	 *
+	 * @return bool
+	 */
+	protected function looksLikeSetter($line)
+	{
+		return strpos($line, '=') !== false;
+	}
+
+
+	/**
+	 * @param $config
+	 *
+	 * @throws
+	 */
+	public function parseInt($config)
+	{
+		foreach ($config as $key => $value) {
+			Config::set($key, $value);
+		}
+//		if (isset($config['id'])) {
+//			$this->id = $config['id'];
+//			unset($config['id']);
+//		}
+//		if (isset($config['storage'])) {
+//			$this->storage = $config['storage'];
+//			unset($config['storage']);
+//		}
+//		if (!empty($this->runtimePath)) {
+//			if (!is_dir($this->runtimePath)) {
+//				mkdir($this->runtimePath, 777);
+//			}
+//
+//			if (!is_dir($this->runtimePath) || !is_writeable($this->runtimePath)) {
+//				throw new InitException("Directory {$this->runtimePath} does not have write permission");
+//			}
+//		}
+//		if (isset($config['aliases'])) {
+//			$this->classAlias($config);
+//		}
+//
+//		foreach ($this->moreComponents() as $key => $val) {
+//			if (isset($config['components'][$key])) {
+//				$config['components'][$key] = array_merge($val, $config['components'][$key]);
+//			} else {
+//				$config['components'][$key] = $val;
+//			}
+//		}
+	}
+
+	/**
+	 * @param array $data
+	 */
+	private function classAlias(array &$data)
+	{
+		foreach ($data['aliases'] as $key => $val) {
+			class_alias($val, $key, true);
+		}
+		unset($data['aliases']);
+	}
+
+
+	/**
+	 * @param $name
+	 * @return mixed
+	 * @throws ComponentException
+	 */
+	public function clone($name)
+	{
+		return clone $this->get($name);
+	}
+
+	/**
+	 *
+	 * @throws Exception
+	 */
+	public function initErrorHandler()
+	{
+		$this->get('error')->register();
+	}
+
+	/**
+	 * @return mixed
+	 */
+	public function getLocalIps()
+	{
+		return swoole_get_local_ip();
+	}
+
+	/**
+	 * @return mixed
+	 */
+	public function getFirstLocal()
+	{
+		return current($this->getLocalIps());
+	}
+
+	/**
+	 * @param $ip
+	 * @return bool
+	 */
+	public function isLocal($ip)
+	{
+		return $this->getFirstLocal() == $ip;
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	protected function moreComponents()
+	{
+		return $this->setComponents([
+			'error'             => ['class' => ErrorHandler::class],
+			'event'             => ['class' => Event::class],
+			'annotation'        => ['class' => Annotation::class],
+			'processes'         => ['class' => Processes::class],
+			'connections'       => ['class' => Connection::class],
+			'redis_connections' => ['class' => RedisClient::class],
+			'pool'              => ['class' => \Snowflake\Pool\Pool::class],
+			'response'          => ['class' => Response::class],
+			'request'           => ['class' => Request::class],
+			'config'            => ['class' => Config::class],
+			'logger'            => ['class' => Logger::class],
+			'router'            => ['class' => Router::class],
+		]);
+	}
+}
diff --git a/system/Abstracts/BaseObject.php b/system/Abstracts/BaseObject.php
new file mode 100644
index 00000000..00184a77
--- /dev/null
+++ b/system/Abstracts/BaseObject.php
@@ -0,0 +1,211 @@
+init();
+	}
+
+	public function init()
+	{
+
+	}
+
+	/**
+	 * @return string
+	 */
+	public static function className()
+	{
+		return get_called_class();
+	}
+
+	/**
+	 * @param $name
+	 * @param $value
+	 *
+	 * @throws Exception
+	 */
+	public function __set($name, $value)
+	{
+		$method = 'set' . ucfirst($name);
+		if (method_exists($this, $method)) {
+			$this->{$method}($value);
+		} else {
+			$this->error('set ' . $name . ' not exists ' . get_called_class());
+			throw new Exception('The set name ' . $name . ' not find in class ' . get_class($this));
+		}
+	}
+
+	/**
+	 * @param $name
+	 *
+	 * @return mixed
+	 * @throws Exception
+	 */
+	public function __get($name)
+	{
+		$method = 'get' . ucfirst($name);
+		if (method_exists($this, $method)) {
+			return $this->$method();
+		} else {
+			throw new Exception('The get name ' . $name . ' not find in class ' . get_class($this));
+		}
+	}
+
+	/**
+	 * @param $name
+	 * @param $arguments
+	 *
+	 * @return mixed
+	 * @throws Exception
+	 */
+	public function __call($name, $arguments)
+	{
+		if (!method_exists($this, $name)) {
+			throw new Exception("Not find " . get_called_class() . "::($name)");
+		} else {
+			$result = $this->$name(...$arguments);
+			if (method_exists($this, 'defer')) {
+				$this->defer();
+			}
+			return $result;
+		}
+	}
+
+	/**
+	 * @param $message
+	 * @param string $model
+	 * @return bool
+	 * @throws Exception
+	 */
+	public function addError($message, $model = 'app')
+	{
+		if ($message instanceof Exception) {
+			$this->error($message->getMessage(), $message->getFile(), $message->getLine());
+		} else {
+			if (!is_string($message)) {
+				$message = json_encode($message, JSON_UNESCAPED_UNICODE);
+			}
+			$this->error($message);
+		}
+		return FALSE;
+	}
+
+
+	/**
+	 * @param string $message
+	 * @param string $method
+	 * @param string $file
+	 * @throws
+	 */
+	public function debug($message, string $method = __METHOD__, string $file = __FILE__)
+	{
+		if (!is_string($message)) {
+			$message = print_r($message, true);
+		}
+		echo "\033[35m[DEBUG][" . date('Y-m-d H:i:s') . ']: ' . $message . "\033[0m";
+		echo PHP_EOL;
+		Logger::trance($message, 'debug');
+	}
+
+
+	/**
+	 * @param string $message
+	 * @param string $method
+	 * @param string $file
+	 * @throws
+	 */
+	public function info($message, string $method = __METHOD__, string $file = __FILE__)
+	{
+		if (!is_string($message)) {
+			$message = print_r($message, true);
+		}
+		echo "\033[34m[INFO][" . date('Y-m-d H:i:s') . ']: ' . $message . "\033[0m";
+		echo PHP_EOL;
+		Logger::trance($message, 'info');
+	}
+
+	/**
+	 * @param string $message
+	 * @param string $method
+	 * @param string $file
+	 * @throws
+	 */
+	public function success($message, string $method = __METHOD__, string $file = __FILE__)
+	{
+		if (!is_string($message)) {
+			$message = print_r($message, true);
+		}
+		echo "\033[36m[SUCCESS][" . date('Y-m-d H:i:s') . ']: ' . $message . "\033[0m";
+		echo PHP_EOL;
+		Logger::trance($message, 'success');
+	}
+
+
+	/**
+	 * @param string $message
+	 * @param string $method
+	 * @param string $file
+	 * @throws
+	 */
+	public function warning($message, string $method = __METHOD__, string $file = __FILE__)
+	{
+		if (!is_string($message)) {
+			$message = print_r($message, true);
+		}
+		echo "\033[33m[SUCCESS][" . date('Y-m-d H:i:s') . ']: ' . $message . "\033[0m";
+		echo PHP_EOL;
+		Logger::trance($message, 'warning');
+	}
+
+
+	/**
+	 * @param string $message
+	 * @param string|null $method
+	 * @param string|null $file
+	 * @throws Exception
+	 */
+	public function error($message, string $method = null, string $file = null)
+	{
+		if (!empty($file)) {
+			echo "\033[41;37m[ERROR][" . date('Y-m-d H:i:s') . ']: ' . $file . "\033[0m";
+			echo PHP_EOL;
+		}
+		if (!is_string($message)) {
+			$message = print_r($message, true);
+		}
+		echo "\033[41;37m[ERROR][" . date('Y-m-d H:i:s') . ']: ' . (empty($method) ? '' : $method . ': ') . $message . "\033[0m";
+		echo PHP_EOL;
+		Logger::error($message, 'error');
+	}
+
+}
diff --git a/system/Abstracts/Component.php b/system/Abstracts/Component.php
new file mode 100644
index 00000000..d29f3cbc
--- /dev/null
+++ b/system/Abstracts/Component.php
@@ -0,0 +1,145 @@
+_events[$name])) {
+			array_push($this->_events[$name], [$callback, $param]);
+		} else {
+			$this->_events[$name][] = [$callback, $param];
+		}
+	}
+
+	/**
+	 * @param $name
+	 * @param null $callback
+	 * @return bool
+	 */
+	public function hasEvent($name, $callback = null)
+	{
+		if (!isset($this->_events[$name])) {
+			return false;
+		}
+		if (!is_array($this->_events[$name])) {
+			return false;
+		}
+		foreach ($this->_events[$name] as $event) {
+			[$_callback, $param] = $event;
+			if ($_callback === $callback) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * @param $name
+	 * @param null $event
+	 * @param array $params
+	 * @param bool $isRemove
+	 * @throws Exception
+	 */
+	public function trigger($name, $event = null, $params = [], $isRemove = false)
+	{
+		if (isset($this->_events[$name])) {
+			$events = $this->_events[$name];
+			foreach ($events as $key => $_event) {
+				if (!empty($event)) {
+					$_event = $event;
+				}
+				call_user_func($_event, ...$params);
+				if ($isRemove) {
+					unset($this->_events[$name][$key]);
+					of($name, $_event);
+				}
+			}
+		}
+		fire($name, $event);
+	}
+
+	/**
+	 * @param $name
+	 * @param null $handler
+	 * @return mixed
+	 */
+	public function off($name, $handler = NULL)
+	{
+		if (!isset($this->_events[$name])) {
+			return of($name, $handler);
+		}
+
+		if (empty($handler)) {
+			unset($this->_events[$name]);
+
+			return of($name, $handler);
+		}
+
+		foreach ($this->_events[$name] as $key => $val) {
+			if ($val[0] != $handler) {
+				continue;
+			}
+			unset($this->_events[$name][$key]);
+
+			break;
+		}
+		return of($name, $handler);
+	}
+
+	/**
+	 */
+	public function offAll()
+	{
+		$this->_events = [];
+		ofAll();
+	}
+
+
+	/**
+	 * @param $name
+	 * @return mixed
+	 * @throws Exception
+	 */
+	public function __get($name)
+	{
+		$method = 'get' . ucfirst($name);
+		if (method_exists($this, $method)) {
+			return $this->$method();
+		} else if (property_exists($this, $name)) {
+			return $this->$name ?? null;
+		} else {
+			return parent::__get($name);
+		}
+	}
+}
diff --git a/system/Abstracts/Config.php b/system/Abstracts/Config.php
new file mode 100644
index 00000000..ef38327f
--- /dev/null
+++ b/system/Abstracts/Config.php
@@ -0,0 +1,85 @@
+config;
+		if (isset($config->data[$key])) {
+			return $config->data[$key];
+		} else if ($default !== null) {
+			return $default;
+		} else if ($try) {
+			throw new ConfigException(str_replace(':key', $key, self::ERROR_MESSAGE));
+		}
+		return NULL;
+	}
+
+	/**
+	 * @param $key
+	 * @param $value
+	 * @return mixed
+	 * @throws Exception
+	 */
+	public static function set($key, $value)
+	{
+		$config = Snowflake::get()->config;
+		return $config->data[$key] = $value;
+	}
+
+	/**
+	 * @param $key
+	 * @param bool $must_not_null
+	 * @return bool
+	 */
+	public static function has($key, $must_not_null = false)
+	{
+		$config = Snowflake::get()->config;
+		if (!isset($config->data[$key])) {
+			return false;
+		}
+		$config = $config->data[$key];
+		if ($must_not_null === false) {
+			return true;
+		}
+		return !empty($config);
+	}
+
+	/**
+	 * @param $name
+	 * @param $value
+	 */
+	public function __set($name, $value)
+	{
+		$this->data[$name] = $value;
+	}
+}
diff --git a/system/Abstracts/Configure.php b/system/Abstracts/Configure.php
new file mode 100644
index 00000000..28d8f675
--- /dev/null
+++ b/system/Abstracts/Configure.php
@@ -0,0 +1,18 @@
+name($name, $isMaster);
+		if (isset($this->_items[$name]) &&
+			$this->_items[$name] instanceof Channel) {
+			return;
+		}
+		$this->_items[$name] = new Channel($max);
+	}
+
+	/**
+	 * @param $name
+	 * @param int $timeout
+	 * @return mixed
+	 */
+	protected function get($name, $timeout = -1)
+	{
+		if ($this->is_ticker) {
+			Coroutine::sleep(1);
+		}
+		if ($timeout != -1) {
+			$client = $this->_items[$name]->pop($timeout);
+		} else {
+			$client = $this->_items[$name]->pop(60);
+		}
+		if (!$this->checkCanUse(...$client)) {
+			unset($client);
+			return [0, null];
+		} else {
+			return $client;
+		}
+	}
+
+	/**
+	 * @param $cds
+	 * @param false $isMaster
+	 * @return string
+	 */
+	public function name($cds, $isMaster = false)
+	{
+		return hash('sha1', $cds . ($isMaster ? 'master' : 'slave'));
+	}
+
+
+	/**
+	 * @param $time
+	 * @param $client
+	 * @return mixed
+	 * 检查连接可靠性
+	 * @throws Exception
+	 */
+	public function checkCanUse($time, $client)
+	{
+		throw new Exception('Undefined system processing function.');
+	}
+
+	/**
+	 * @param $name
+	 * @throws Exception
+	 */
+	public function desc($name)
+	{
+		throw new Exception('Undefined system processing function.');
+	}
+
+	/**
+	 * @param array $config
+	 * @param $isMaster
+	 * @throws Exception
+	 */
+	public function getConnection(array $config, $isMaster)
+	{
+		throw new Exception('Undefined system processing function.');
+	}
+
+	/**
+	 * @param $name
+	 * @return mixed
+	 */
+	public function hasItem($name)
+	{
+		return $this->hasLength($name) > 0;
+	}
+
+
+	/**
+	 * @param $name
+	 * @return mixed
+	 */
+	public function hasLength($name)
+	{
+		if (!isset($this->_items[$name])) {
+			return 0;
+		}
+		return $this->_items[$name]->length();
+	}
+
+
+	/**
+	 * @param $name
+	 * @param $client
+	 */
+	public function push($name, $client)
+	{
+		if (!isset($this->_items[$name])) {
+			return;
+		}
+		if (!$this->_items[$name]->isFull()) {
+			$this->_items[$name]->push([time(), $client]);
+		}
+	}
+
+
+	/**
+	 * @param $name
+	 */
+	public function clean($name)
+	{
+		if (!isset($this->_items[$name])) {
+			return;
+		}
+		while ([$time, $client] = $this->_items[$name]->pop(0.001)) {
+			unset($client);
+		}
+	}
+
+}
diff --git a/system/Annotation/Annotation.php b/system/Annotation/Annotation.php
new file mode 100644
index 00000000..47b211e1
--- /dev/null
+++ b/system/Annotation/Annotation.php
@@ -0,0 +1,149 @@
+ Websocket::class
+	];
+
+
+	/**
+	 * @param $name
+	 * @param $class
+	 */
+	public function register($name, $class)
+	{
+		$this->_classMap[$name] = $class;
+	}
+
+
+	/**
+	 * @throws ReflectionException
+	 */
+	public function registration_notes()
+	{
+		if (!is_array($this->path)) {
+			return;
+		}
+		foreach ($this->path as $item) {
+			$this->scanning($item);
+		}
+	}
+
+	/**
+	 * @param string $path
+	 * @throws ReflectionException
+	 * @throws Exception
+	 */
+	protected function scanning($path = '')
+	{
+		if (empty($path)) {
+			$path = rtrim($this->path, '/');
+		}
+		foreach (glob($path . '/*') as $file) {
+			if (is_dir($file)) {
+				$this->scanning($path);
+			}
+
+			$explode = explode('/', $file);
+
+			$class = str_replace('.php', '', end($explode));
+
+			$reflect = new ReflectionClass($this->namespace . $class);
+
+			$methods = $reflect->getMethods(\ReflectionMethod::IS_PUBLIC);
+			if (empty($methods) || !$reflect->isInstantiable()) {
+				continue;
+			}
+			$this->resolve($reflect, $methods);
+		}
+	}
+
+
+	/**
+	 * @param ReflectionClass $class
+	 * @param array $methods
+	 * @throws Exception
+	 */
+	public function resolve(ReflectionClass $class, array $methods)
+	{
+		throw new Exception('Undefined analytic function.');
+	}
+
+
+	/**
+	 * @param $path
+	 * @param mixed ...$param
+	 * @return bool|mixed
+	 */
+	public function runWith($path, $param)
+	{
+		if (!$this->has($path)) {
+			return null;
+		}
+		return call_user_func($this->_Scan_directory[$path], ...$param);
+	}
+
+
+	/**
+	 * @param $name
+	 * @param $callback
+	 */
+	public function push($name, $callback)
+	{
+		$this->_Scan_directory[$name] = $callback;
+	}
+
+
+	/**
+	 * @param $path
+	 * @return bool|mixed
+	 */
+	public function has($path)
+	{
+		return isset($this->_Scan_directory[$path]);
+	}
+
+
+	/**
+	 * @param $name
+	 * @return mixed|null
+	 * @throws ReflectionException
+	 * @throws NotFindClassException
+	 * @throws Exception
+	 */
+	public function __get($name)
+	{
+		if (isset($this->_classMap[$name])) {
+			return Snowflake::createObject($this->_classMap[$name]);
+		}
+		return parent::__get($name); // TODO: Change the autogenerated stub
+	}
+
+
+}
diff --git a/system/Annotation/Definition/Websocket.php b/system/Annotation/Definition/Websocket.php
new file mode 100644
index 00000000..68521ec9
--- /dev/null
+++ b/system/Annotation/Definition/Websocket.php
@@ -0,0 +1,66 @@
+newInstance();
+
+		foreach ($methods as $function) {
+			$comment = $function->getDocComment();
+			$methodName = $function->getName();
+
+			preg_match('/@Event\((.*)?\)/', $comment, $events);
+			if (!isset($events[1])) {
+				continue;
+			}
+
+			if (!($_key = $this->getName($events, $comment))) {
+				continue;
+			}
+			$this->push($_key, [$controller, $methodName]);
+		}
+	}
+
+	/**
+	 * @param $events
+	 * @param $comment
+	 * @return false|string
+	 */
+	private function getName($events, $comment)
+	{
+		$event = $events[1];
+		if ($event !== 'message') {
+			return self::EVENT . $event;
+		}
+		preg_match('/@Message\((.*)?\)/', $comment, $message);
+		if (isset($message[1])) {
+			return false;
+		}
+		return self::MESSAGE . $message[1];
+	}
+
+}
diff --git a/system/Application.php b/system/Application.php
new file mode 100644
index 00000000..08eed1e8
--- /dev/null
+++ b/system/Application.php
@@ -0,0 +1,71 @@
+processes;
+		if (Config::has('servers', true)) {
+			/** @var Server $https */
+			$https = $this->make(Server::class);
+			$servers = $https->initCore(Config::get('servers'));
+			$process->push($servers);
+		}
+		if (Config::has('processes', true)) {
+			$process->push(Config::get('processes'));
+		}
+	}
+
+
+	/**
+	 * @throws Exception
+	 */
+	public function start()
+	{
+		$process = Snowflake::get()->processes;
+		$process->start();
+	}
+
+
+	/**
+	 * @param $className
+	 * @param null $abstracts
+	 * @return mixed
+	 * @throws Exception
+	 */
+	public function make($className, $abstracts = null)
+	{
+		return Snowflake::createObject($className);
+	}
+}
diff --git a/system/Cache/File.php b/system/Cache/File.php
new file mode 100644
index 00000000..7eecef9d
--- /dev/null
+++ b/system/Cache/File.php
@@ -0,0 +1,147 @@
+getCacheKey($key);
+		if (!$this->exists($tmpFile)) {
+			touch($tmpFile);
+		}
+		System::writeFile($tmpFile, $val, LOCK_EX);
+	}
+
+	/**
+	 * @param $key
+	 * @param array $hashKeys
+	 * @return mixed|void
+	 */
+	public function hMget($key, array $hashKeys)
+	{
+		$hash = $this->get($key);
+		if (!is_array($hash)) {
+			return false;
+		}
+
+		$nowHash = [];
+		foreach ($hashKeys as $hashKey) {
+			$nowHash[$hashKey] = $hash[$hashKey] ?? null;
+		}
+		return $nowHash;
+	}
+
+	/**
+	 * @param $key
+	 * @param array $val
+	 * @return mixed|void
+	 */
+	public function hMset($key, array $val)
+	{
+		$hash = $this->get($key);
+		if (!is_array($hash)) {
+			return false;
+		}
+
+		$merge = array_merge($hash, $val);
+		return $this->set($key, $merge);
+	}
+
+	/**
+	 * @param $key
+	 * @param $hashKey
+	 * @return mixed|void
+	 */
+	public function hget($key, $hashKey)
+	{
+		$hash = $this->get($key);
+		if (!is_array($hash)) {
+			return false;
+		}
+		return $hash[$hashKey] ?? null;
+	}
+
+	/**
+	 * @param $key
+	 * @param $hashKey
+	 * @param $hashValue
+	 * @return mixed|void
+	 */
+	public function hset($key, $hashKey, $hashValue)
+	{
+		$hash = $this->get($key);
+		if (!is_array($hash)) {
+			return false;
+		}
+
+		$hash[$hashKey] = $hashValue;
+
+		return $this->set($key, $hash);
+	}
+
+	/**
+	 * @param $key
+	 * @return bool
+	 */
+	public function exists($key)
+	{
+		return file_exists($key);
+	}
+
+	/**
+	 * @param $key
+	 * @return mixed|null
+	 */
+	public function get($key)
+	{
+		$tmpFile = $this->getCacheKey($key);
+		if (!$this->exists($tmpFile)) {
+			return NULL;
+		}
+		$content = file_get_contents($tmpFile);
+		return unserialize($content);
+	}
+
+	/**
+	 * @param $key
+	 * @return string
+	 * @throws
+	 */
+	private function getCacheKey($key)
+	{
+		return storage($key,'cache');
+	}
+}
diff --git a/system/Cache/ICache.php b/system/Cache/ICache.php
new file mode 100644
index 00000000..e12e521d
--- /dev/null
+++ b/system/Cache/ICache.php
@@ -0,0 +1,64 @@
+_memcached = new \Memcached();
+		$isConnected = $this->_memcached->addServer(
+			env('cache.memcached.host', $this->host),
+			env('cache.memcached.port', $this->port),
+			env('cache.memcached.timeout', $this->timeout)
+		);
+		if (!$isConnected) {
+			throw new Exception('Cache Memcache Host 127.0.0.1 Connect Fail.');
+		}
+	}
+
+
+	/**
+	 * @param $key
+	 * @param $val
+	 * @return mixed|void
+	 */
+	public function set($key, $val)
+	{
+		// TODO: Implement set() method.
+		if (is_array($val) || is_object($val)) {
+			$val = serialize($val);
+		}
+
+		$this->_memcached->set($key, $val);
+	}
+
+	/**
+	 * @param $key
+	 * @return mixed|void
+	 */
+	public function get($key)
+	{
+		// TODO: Implement get() method.
+	}
+
+	/**
+	 * @param $key
+	 * @param array $hashKeys
+	 * @return mixed|void
+	 */
+	public function hMget($key, array $hashKeys)
+	{
+		// TODO: Implement hMget() method.
+	}
+
+	/**
+	 * @param $key
+	 * @param array $val
+	 * @return mixed|void
+	 */
+	public function hMset($key, array $val)
+	{
+		// TODO: Implement hMset() method.
+	}
+
+	/**
+	 * @param $key
+	 * @param $hashKey
+	 * @return mixed|void
+	 */
+	public function hget($key, $hashKey)
+	{
+		// TODO: Implement hget() method.
+	}
+
+	/**
+	 * @param $key
+	 * @param $hashKey
+	 * @param $hashValue
+	 * @return mixed|void
+	 */
+	public function hset($key, $hashKey, $hashValue)
+	{
+		// TODO: Implement hset() method.
+	}
+
+	/**
+	 * @param $key
+	 * @return bool|void
+	 */
+	public function exists($key)
+	{
+		// TODO: Implement exists() method.
+	}
+
+
+}
diff --git a/system/Cache/Redis.php b/system/Cache/Redis.php
new file mode 100644
index 00000000..6480ecc7
--- /dev/null
+++ b/system/Cache/Redis.php
@@ -0,0 +1,348 @@
+event;
+		$event->on(Event::RELEASE_ALL, [$this, 'destroy']);
+		$event->on(Event::EVENT_AFTER_REQUEST, [$this, 'release']);
+	}
+
+	/**
+	 * @param $name
+	 * @param $arguments
+	 * @return mixed
+	 * @throws
+	 */
+	public function __call($name, $arguments)
+	{
+		try {
+			if (method_exists($this, $name)) {
+				$data = $this->{$name}(...$arguments);
+			} else {
+				$data = $this->proxy()->{$name}(...$arguments);
+			}
+		} catch (\Throwable $exception) {
+			$this->error(print_r($exception, true));
+			$data = false;
+		} finally {
+			return $data;
+		}
+	}
+
+	/**
+	 * 释放连接池
+	 */
+	public function release()
+	{
+		$connections = Snowflake::get()->pool->redis;
+		$connections->release($this->get_config(), true);
+	}
+
+	/**
+	 * 销毁连接池
+	 */
+	public function destroy()
+	{
+		$connections = Snowflake::get()->pool->redis;
+		$connections->destroy($this->get_config(), true);
+	}
+
+	/**
+	 * @return \Redis
+	 * @throws Exception
+	 */
+	public function proxy()
+	{
+		$connections = Snowflake::get()->pool->redis;
+
+		$config = $this->get_config();
+		$name = $config['host'] . ':' . $config['prefix'] . ':' . $config['databases'];
+
+		$connections->initConnections('redis:' . $name, true);
+		$connections->setLength(env('REDIS.POOL_LENGTH', 100));
+
+		$client = $connections->getConnection($config, true);
+		if (!($client instanceof \Redis)) {
+			throw new Exception('Redis connections more.');
+		}
+		return $client;
+	}
+
+	/**
+	 * @return array
+	 */
+	public function get_config(): array
+	{
+		return [
+			'host'         => env('REDIS.HOST', $this->host),
+			'port'         => env('REDIS.PORT', $this->port),
+			'auth'         => env('REDIS.PASSWORD', $this->auth),
+			'timeout'      => env('REDIS.TIMEOUT', 2),
+			'databases'    => env('REDIS.DATABASE', $this->databases),
+			'read_timeout' => env('REDIS.READ_TIMEOUT', 10),
+			'prefix'       => env('REDIS.PREFIX', 'system:'),
+		];
+	}
+
+}
diff --git a/system/Console/AbstractConsole.php b/system/Console/AbstractConsole.php
new file mode 100644
index 00000000..e886d155
--- /dev/null
+++ b/system/Console/AbstractConsole.php
@@ -0,0 +1,139 @@
+_config = $config;
+		$this->signCommand(\BeReborn::createObject(DefaultCommand::class));
+	}
+
+	/**
+	 * @return array
+	 */
+	public function getConfig()
+	{
+		return $this->_config;
+	}
+
+	/**
+	 * @return $this
+	 */
+	public function setParameters()
+	{
+		$this->parameters = new Dtl($_SERVER['argv']);
+		return $this;
+	}
+
+	/**
+	 * @param Command $command
+	 * @return mixed
+	 */
+	public function execCommand(Command $command)
+	{
+		return $command->handler($this->parameters);
+	}
+
+	/**
+	 * @return Command|null
+	 */
+	public function search()
+	{
+		$name = $this->parameters->getCommandName();
+		$this->parameters->set('commandList', $this->getCommandList());
+		foreach ($this->commands as $command) {
+			if ($command->command != $name) {
+				continue;
+			}
+			return $command;
+		}
+		return null;
+	}
+
+	/**
+	 * @param Command $abstractConsole
+	 *
+	 * 注册命令
+	 */
+	public function signCommand(Command $abstractConsole)
+	{
+		$this->commands[] = $abstractConsole;
+	}
+
+	/**
+	 * @param $kernel
+	 * @throws Exception
+	 */
+	public function batch($kernel)
+	{
+		if (is_object($kernel)) {
+			if (!property_exists($kernel, 'commands')) {
+				return;
+			}
+			$kernel = $kernel->commands;
+		}
+		if (!is_array($kernel)) {
+			return;
+		}
+		foreach ($kernel as $command) {
+			$this->signCommand(\BeReborn::createObject($command));
+		}
+	}
+
+	/**
+	 * @param Command $abstractConsole
+	 * 释放一个命令
+	 */
+	public function destroyCommand(Command $abstractConsole)
+	{
+		foreach ($this->commands as $index => $command) {
+			if ($abstractConsole === $command) {
+				unset($this->commands[$index]);
+				break;
+			}
+		}
+	}
+
+	/**
+	 * @return array
+	 */
+	private function getCommandList()
+	{
+		$_tmp = [];
+		foreach ($this->commands as $command) {
+			$_tmp[$command->command] = [$command->description, $command];
+		}
+		ksort($_tmp, SORT_ASC);
+		return $_tmp;
+	}
+
+
+}
diff --git a/system/Console/Application.php b/system/Console/Application.php
new file mode 100644
index 00000000..62b93cf2
--- /dev/null
+++ b/system/Console/Application.php
@@ -0,0 +1,91 @@
+channel = new Channel(1);
+		$this->console = new Console($config);
+	}
+
+	/**
+	 * @param $class
+	 * @throws
+	 */
+	public function register($class)
+	{
+		if (is_string($class) || is_callable($class, true)) {
+			$class = \BeReborn::createObject($class);
+		}
+		$this->console->signCommand($class);
+	}
+
+
+	/**
+	 * @param null $kernel
+	 * @return string|void
+	 * @throws \Exception
+	 */
+	public function run($kernel = null)
+	{
+		setCommand(true);
+		try {
+			$params = '';
+			$kernel = \BeReborn::make($kernel, Kernel::class);
+
+			Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
+
+			$this->console->setParameters();
+			$this->console->batch($kernel);
+			$class = $this->console->search();
+			$params = response()->send($this->console->execCommand($class));
+		} catch (\Exception $exception) {
+			$params = response()->send(implode("\n", [
+				'Msg: ' . $exception->getMessage(),
+				'Line: ' . $exception->getLine(),
+				'File: ' . $exception->getFile()
+			]));
+		} finally {
+			Timer::clearAll();
+			return $params;
+		}
+	}
+
+}
diff --git a/system/Console/Command.php b/system/Console/Command.php
new file mode 100644
index 00000000..faf7f742
--- /dev/null
+++ b/system/Console/Command.php
@@ -0,0 +1,38 @@
+command;
+	}
+
+
+	/**
+	 * @return string
+	 *
+	 * 返回命令描述
+	 */
+	public function getDescription()
+	{
+		return $this->description;
+	}
+
+}
diff --git a/system/Console/CommandInterface.php b/system/Console/CommandInterface.php
new file mode 100644
index 00000000..3be3a58a
--- /dev/null
+++ b/system/Console/CommandInterface.php
@@ -0,0 +1,15 @@
+get('commandList');
+
+		$last = '';
+		$lists = [str_pad('Commands', 24, ' ', STR_PAD_RIGHT) . '注释'];
+		foreach ($param as $key => $val) {
+			$split = explode(':', $key);
+			if (empty($last) && isset($split[0])) {
+				$lists[] = str_pad("\033[32;40;1;1m" . $split[0] . " \033[0m", 40, ' ', STR_PAD_RIGHT);
+			} else if (isset($split[0]) && $last != $split[0]) {
+				$lists[] = str_pad("\033[32;40;1;1m" . $split[0] . " \033[0m", 40, ' ', STR_PAD_RIGHT);
+			}
+
+			$last = $split[0] ?? '';
+
+			list($method, $ts) = $val;
+			$lists[] = str_pad("\033[32;40;1;1m  " . $key . " \033[0m", 40, ' ', STR_PAD_RIGHT) . $method;
+		}
+		return implode(PHP_EOL, $lists);
+	}
+
+}
diff --git a/system/Console/Dtl.php b/system/Console/Dtl.php
new file mode 100644
index 00000000..af4d0233
--- /dev/null
+++ b/system/Console/Dtl.php
@@ -0,0 +1,132 @@
+parameters = $this->resolve($parameters);
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getCommandName()
+	{
+		return $this->command;
+	}
+
+	/**
+	 * @param $parameters
+	 * @return array
+	 * @throws \Exception
+	 */
+	public function resolve($parameters)
+	{
+		$arrays = [];
+		$parameters = array_slice($parameters, 1);
+
+		$this->command = array_shift($parameters);
+		foreach ($parameters as $parameter) {
+			$explode = explode('=', $parameter);
+			if (count($explode) < 2) {
+				continue;
+			}
+			$arrays[array_shift($explode)] = current($explode);
+		}
+
+		$request = new Request();
+		$request->fd = 0;
+		$request->params = new HttpParams([], $arrays, []);
+		$request->headers = new HttpParams([]);
+
+		Context::setRequest($request);
+		return $arrays;
+	}
+
+
+	/**
+	 * @param $key
+	 * @param null $default
+	 * @return mixed|null
+	 */
+	public function get($key, $default = null)
+	{
+		return $this->parameters[$key] ?? $default;
+	}
+
+	/**
+	 * @param $key
+	 * @param $value
+	 * @return $this
+	 */
+	public function set($key, $value)
+	{
+		$this->parameters[$key] = $value;
+		return $this;
+	}
+
+	/**
+	 * @param array $array
+	 * @return $this
+	 */
+	public function setBatch(array $array)
+	{
+		if (empty($array)) {
+			return $this;
+		}
+		foreach ($array as $key => $value) {
+			$this->parameters[$key] = $value;
+		}
+		return $this;
+	}
+
+	/**
+	 * @return array
+	 * 获取
+	 */
+	public function getAll()
+	{
+		return $this->parameters;
+	}
+
+	/**
+	 * @return null
+	 * 清理
+	 */
+	public function clear()
+	{
+		return $this->parameters = null;
+	}
+
+	/**
+	 * @return false|string
+	 */
+	public function toJson()
+	{
+		return json_encode($this->parameters, JSON_UNESCAPED_UNICODE);
+	}
+}
diff --git a/system/Console/ICommand.php b/system/Console/ICommand.php
new file mode 100644
index 00000000..3ae021fd
--- /dev/null
+++ b/system/Console/ICommand.php
@@ -0,0 +1,12 @@
+ $val) {
+			if (is_array($val) || is_object($val)) {
+				$tmp[$key] = self::toArray($val);
+			} else {
+				$tmp[$key] = $val;
+			}
+		}
+		return $tmp;
+	}
+
+
+	/**
+	 * @param $data
+	 * @return array|mixed
+	 * @throws Exception
+	 */
+	public static function objToArray($data)
+	{
+		if (!is_object($data)) {
+			return $data;
+		}
+		if (method_exists($data, 'get')) {
+			$data = $data->get();
+			if (is_array($data)) {
+				return $data;
+			}
+		}
+		if (method_exists($data, 'toArray')) {
+			$data = $data->toArray();
+		} else {
+			$data = get_object_vars((object)$data);
+		}
+		return $data;
+	}
+
+
+	/**
+	 * @param array $oldArray
+	 * @param array $newArray
+	 * @return array
+	 */
+	public static function merge(array $oldArray, array $newArray)
+	{
+		if (empty($oldArray)) {
+			return $newArray;
+		} else if (empty($newArray)) {
+			return $oldArray;
+		}
+		foreach ($newArray as $item => $value) {
+			if (!isset($oldArray[$item])) {
+				$oldArray[$item] = $value;
+			}
+			if (is_array($value)) {
+				$oldArray[$item] = self::merge($oldArray[$item], $value);
+			} else {
+				$oldArray[$item] = $value;
+			}
+		}
+		return $oldArray;
+	}
+
+}
diff --git a/system/Core/DateFormat.php b/system/Core/DateFormat.php
new file mode 100644
index 00000000..25221409
--- /dev/null
+++ b/system/Core/DateFormat.php
@@ -0,0 +1,103 @@
+";
+		foreach ($data as $key => $val) {
+			if (is_numeric($val)) {
+				$xml .= "<" . $key . ">" . $val . "";
+			} else {
+				$xml .= "<" . $key . ">";
+			}
+		}
+		$xml .= "";
+		return $xml;
+	}
+
+
+	/**
+	 * @param $xml
+	 * @return array|mixed
+	 */
+	public static function toArray($xml)
+	{
+		if (empty($xml)) {
+			return [];
+		} else if (is_array($xml)) {
+			return $xml;
+		}
+		if (!Xml::isXml($xml)) {
+			$xml = JSON::decode($xml);
+		}
+		return $xml;
+
+		/*        $matchQuote = '/(<\?xml.*?\?>)?<([a-zA-Z_]+)>(<([a-zA-Z_]+)><\/\4>)+<\/\2>/';*/
+//        if (!preg_match($matchQuote, $xml)) {
+//            return self::jsonToArray($xml);
+//        }
+//        try {
+//            $data = @simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
+//            if ($data !== false) {
+//                $data = json_decode(json_encode($data), TRUE);
+//            } else {
+//                $data = $xml;
+//            }
+//        } catch (\Exception $exception) {
+//            $data = $xml;
+//        } finally {
+//            return $data;
+//        }
+	}
+
+
+	public static function jsonToArray($xml)
+	{
+		$_xml = json_decode($xml, true);
+		if (is_null($_xml)) {
+			return $xml;
+		}
+		return $_xml;
+	}
+
+	/**
+	 * @param $xml
+	 * @return mixed
+	 */
+	public static function xmlToArray($xml)
+	{
+		if (is_array($xml)) {
+			return $xml;
+		}
+		if (($data = @simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)) !== false) {
+			return json_decode(json_encode($data), TRUE);
+		}
+		if (!is_null($json = json_decode($xml, TRUE))) {
+			return $json;
+		}
+		return $xml;
+	}
+
+	/**
+	 * @param $parameter
+	 * @return array|false|string
+	 * @throws \Exception
+	 */
+	public static function toString($parameter)
+	{
+		if (!is_string($parameter)) {
+			$parameter = ArrayAccess::toArray($parameter);
+			if (is_array($parameter)) {
+				$parameter = JSON::encode($parameter);
+			}
+		}
+		return $parameter;
+	}
+
+	/**
+	 * @param mixed $json
+	 * @return false|mixed|string
+	 */
+	public static function toJson($json)
+	{
+		if (is_object($json)) {
+			$json = get_object_vars($json);
+		}
+		if (is_array($json)) {
+			return json_encode($json, JSON_UNESCAPED_UNICODE);
+		}
+		$matchQuote = '/(<\?xml.*?\?>)?<([a-zA-Z_]+)>(<([a-zA-Z_]+)><\/\4>)+<\/\2>/';
+		if (preg_match($matchQuote, $json)) {
+			$json = self::xmlToArray($json);
+		} else {
+			$json = json_decode($json, true);
+		}
+		if (!is_array($json)) {
+			$json = [];
+		}
+		return json_encode($json, JSON_UNESCAPED_UNICODE);
+	}
+
+	/**
+	 * @param int $length
+	 * @return string
+	 *
+	 * 随机字符串
+	 */
+	public static function random($length = 20)
+	{
+		$res = [];
+		$str = 'abcdefghijklmnopqrstuvwxyz';
+		$str .= strtoupper($str) . '1234567890';
+		for ($i = 0; $i < $length; $i++) {
+			$rand = substr($str, rand(0, strlen($str) - 2), 1);
+			if (empty($rand)) {
+				$rand = substr($str, strlen($str) - 3, 1);
+			}
+			array_push($res, $rand);
+		}
+
+		return implode($res);
+	}
+
+	/**
+	 * @param array $array
+	 * @param $key
+	 * @param $type
+	 * @return string
+	 */
+	public static function sign(array $array, $key, $type)
+	{
+		ksort($array, SORT_ASC);
+		$string = [];
+		foreach ($array as $hashKey => $val) {
+			if (empty($val)) {
+				continue;
+			}
+			$string[] = $hashKey . '=' . $val;
+		}
+		$string[] = 'key=' . $key;
+		$string = implode('&', $string);
+		if ($type == 'MD5') {
+			return strtoupper(md5($string));
+		} else {
+			return hash('sha256', $string);
+		}
+	}
+
+}
diff --git a/system/Core/JSON.php b/system/Core/JSON.php
new file mode 100644
index 00000000..9e54405f
--- /dev/null
+++ b/system/Core/JSON.php
@@ -0,0 +1,119 @@
+ $offset && count($strings) < $size) {
+				$strings[] = [
+					'id'      => $count,
+					'content' => $buffer
+				];
+			}
+		}
+		//关闭文件
+		fclose($fp);
+		unset($fp);
+
+		return ['total' => $count, 'list' => $strings];
+	}
+
+	/**
+	 * @param $filename
+	 * @param $start
+	 * @param $lines
+	 * @return mixed
+	 */
+	public static function read_backward_line($filename, $start, $lines)
+	{
+		$lines++;
+		$offset = -1;
+		$read = '';
+		$fp = @fopen($filename, "r");
+
+		$tmpStart = 0;
+		while ($lines && fseek($fp, $offset, SEEK_END) >= 0) {
+
+			$c = fgetc($fp);
+			if ($c == "\n" || $c == "\r") {
+				if (++$tmpStart >= $start)
+					$lines--;
+			}
+
+
+			if ($tmpStart >= $start)
+				$read .= $c;
+			$offset--;
+		}
+
+		$read = trim($read);
+
+		$contents = [];
+		$read = array_reverse(explode("\n", strrev($read)));
+		foreach ($read as $key => $value) {
+			if (empty($value)) {
+				unset($read[$key]);
+			} else {
+				$contents[] = ['content' => $value, 'id' => $key + $start];
+			}
+		}
+
+		$response['total'] = self::read_count_by_file($filename);
+		$response['list'] = $contents;
+
+		return $response;
+	}
+
+	/**
+	 * @param $filepath
+	 * @return int
+	 */
+	private static function read_count_by_file($filepath)
+	{
+		$count = 0;
+		//只读方式打开文件
+		$fp = fopen($filepath, "r");
+
+		//开始循环读取$buffer_size
+		while (fgets($fp)) {
+			$count++;
+		}
+		//关闭文件
+		fclose($fp);
+		unset($fp);
+
+		return $count;
+	}
+
+	/**
+	 * @param $filepath
+	 * @param int $page
+	 * @param int $size
+	 * @return array
+	 */
+	public static function folderPagination($filepath, $page = 1, $size = 20)
+	{
+		$count = 0;
+		$strings = [];
+		$offset = ($page - 1) * $size;
+
+		if (!is_dir($filepath)) {
+			return [0, []];
+		}
+
+		foreach (glob($filepath . '/*') as $key => $value) {
+			$count++;
+			if ($key < $offset || count($strings) >= $size) {
+				continue;
+			}
+			$explode = explode(DIRECTORY_SEPARATOR, $value);
+
+			$addTime = fileatime($value);
+			$changeTime = filectime($value);
+			$modifyTime = filemtime($value);
+			$strings[] = [
+				'id'    => $count,
+				'path'  => $value,
+				'isDir' => (int)is_dir($value),
+				'name'  => end($explode),
+				'atime' => [
+					'format'    => date('Y-m-d H:i:s', $addTime),
+					'microtime' => $addTime
+				],
+				'ctime' => [
+					'format'    => date('Y-m-d H:i:s', $changeTime),
+					'microtime' => $changeTime
+				],
+				'mtime' => [
+					'format'    => date('Y-m-d H:i:s', $modifyTime),
+					'microtime' => $modifyTime
+				],
+			];
+		}
+
+		array_multisort($strings, array_column($strings, 'isDir'), SORT_DESC);
+
+		return ['total' => $count, 'list' => $strings];
+	}
+
+
+}
diff --git a/system/Core/Str.php b/system/Core/Str.php
new file mode 100644
index 00000000..e6cc3331
--- /dev/null
+++ b/system/Core/Str.php
@@ -0,0 +1,267 @@
+= $sublen) {
+				if ($i != count($t_string[0]) - 1) $str .= $append;
+				break;
+			}
+		}
+		return $str;
+	}
+
+	/**
+	 * @param $data
+	 *
+	 * @param null $callback
+	 * @return bool
+	 * 判断是否为json字符串
+	 */
+	public static function isJson($data, $callback = null)
+	{
+		$json = !is_null(json_decode($data)) && !is_numeric($data);
+		if ($json && is_callable($callback, true)) {
+			return call_user_func($callback, $data);
+		}
+		return $json;
+	}
+
+	/**
+	 * @param $data
+	 *
+	 * @param null $callBack
+	 * @return bool
+	 * 判断是否序列化字符串
+	 */
+	public static function isSerialize($data, $callBack = null)
+	{
+		$false = !empty($data) && unserialize($data) !== false;
+		if ($false && is_callable($callBack, true)) {
+			return call_user_func($callBack, $data);
+		}
+		return $false;
+	}
+
+	/**
+	 * @param     $string
+	 * @param int $length
+	 *
+	 * @param string $append
+	 * @return string
+	 */
+	public static function cut($string, int $length = 20, $append = '...')
+	{
+		if (empty($string)) {
+			return '';
+		}
+		if ($length < 1) {
+			$length = 1;
+		}
+		$array = str_split($string);
+		if (count($array) <= $length) {
+			return implode('', $array);
+		}
+		$string = implode('', array_slice($array, 0, $length));
+		if (!empty($append)) {
+			$string .= $append;
+		}
+		return $string;
+	}
+
+	/**
+	 * @param        $str
+	 * @param int $number
+	 * @param string $key
+	 *
+	 * @return string
+	 */
+	public static function encrypt($str, $number = 10, $key = 'xshucai.com')
+	{
+		$res = [];
+		$add = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$len = strlen($key) < 0 ? 1 : strlen($key) + 5 > strlen($add) ? strlen($add) - 5 : strlen($key);
+		if ($number < 1) $number = 10;
+		$array = str_split($str);
+		asort($array);
+		$str = implode('', $array);
+		for ($i = 0; $i < $number; $i++) {
+			$_tmp = md5($key) . md5($str) . mb_substr($add, $len, $len + 5, 'utf-8');
+			$res[] = md5($_tmp);
+		}
+		sort($res, SORT_STRING);
+		return hash('sha384', implode('', $res));
+	}
+
+	/**
+	 * @param $file
+	 * @param $type
+	 * @return string
+	 */
+	public static function filename($file, $type)
+	{
+		switch ($type) {
+			case 'image/png':
+				return md5_file($file) . '.png';
+			case 'image/jpeg':
+			case 'image/jpg':
+				return md5_file($file) . '.jpg';
+			case 'image/gif':
+				return md5_file($file) . '.gif';
+				break;
+		}
+		return md5_file($file);
+	}
+
+	/**
+	 * @param $endTime
+	 * @param int|null $startTime
+	 * @return array
+	 * 剩余天,带分秒
+	 */
+	public static function timeout($endTime, int $startTime = null)
+	{
+		$endTime = $endTime - (!empty($startTime) ? $startTime : time());
+
+		$day = intval($endTime / (3600 * 24));
+
+		$hours = intval(($endTime - ($day * (3600 * 24))) / 3600);
+
+		$minute = intval(($endTime - ($day * (3600 * 24) + $hours * 3600)) / 60);
+
+		$scrod = intval(($endTime - ($day * (3600 * 24) + $hours * 3600 + $minute * 60)));
+
+		return [$day, $hours, $minute, $scrod];
+	}
+
+
+	/**
+	 * @return false|int
+	 */
+	public static function get_sy_time()
+	{
+		$time = strtotime('+1days', strtotime(date('Y-m-d')));
+
+		return $time - time();
+	}
+
+	/**
+	 * @param string $string
+	 * @return string
+	 */
+	public static function encode(string $string)
+	{
+		return addslashes($string);
+	}
+
+	/**
+	 * @param string $string
+	 * @return string|string[]|null
+	 * 清除标点符号
+	 */
+	public static function clear(string $string)
+	{
+		$char = '。、!?:;﹑•"…‘’“”〝〞∕¦‖— 〈〉﹞﹝「」‹›〖〗】【»«』『〕〔》《﹐¸﹕︰﹔!¡?¿﹖﹌﹏﹋'´ˊˋ―﹫︳︴¯_ ̄﹢﹦﹤‐­˜﹟﹩﹠﹪﹡﹨﹍﹉﹎﹊ˇ︵︶︷︸︹︿﹀︺︽︾ˉ﹁﹂﹃﹄︻︼()';
+		return preg_replace(array("/[[:punct:]]/i", '/[' . $char . ']/u', '/[ ]{2,}/'), '', $string);
+	}
+
+
+	/**
+	 * @param int $user
+	 * @param array $param
+	 * @param int $requestTime
+	 *
+	 * @return string
+	 * @throws Exception
+	 */
+	public static function token($user, $param = [], $requestTime = NULL)
+	{
+		$str = '';
+		if (!$requestTime) {
+			$requestTime = microtime(true);
+		}
+		$_user = str_split(md5($user . md5($user)));
+		ksort($_user);
+		foreach ($_user as $key => $val) {
+			$str .= md5(sha1($key . $val . 'www.xshucai.com'));
+		}
+		if (is_array($param)) {
+			foreach ($param as $key => $val) {
+				$str .= md5($str . sha1($key . md5($val)));
+			}
+		}
+		$str .= sha1(base64_encode($requestTime));
+
+		$md5 = md5($str . $user);
+
+		return preg_replace('/(\w{10})(\w{3})(\w{4})(\w{9})(\w{6})/', '$1-$2-$3-$4-$5', $md5);
+	}
+
+}
diff --git a/system/Core/Xml.php b/system/Core/Xml.php
new file mode 100644
index 00000000..ae5527a7
--- /dev/null
+++ b/system/Core/Xml.php
@@ -0,0 +1,49 @@
+_singletons[$class])) {
+			return $this->_singletons[$class];
+		} else if (!isset($this->_constructs[$class])) {
+			return $this->resolve($class, $constrict, $config);
+		}
+		$definition = $this->_constructs[$class];
+		if (is_callable($definition, TRUE)) {
+			return call_user_func($definition, $this, $constrict, $config);
+		} else if (is_array($definition)) {
+			$object = $this->resolveDefinition($definition, $class, $config, $constrict);
+		} else if (is_object($definition)) {
+			return $this->_singletons[$class] = $definition;
+		} else {
+			throw new NotFindClassException($class);
+		}
+		return $this->_singletons[$class] = $object;
+	}
+
+	/**
+	 * @param $definition
+	 * @param $class
+	 * @param $config
+	 * @param $constrict
+	 * @return mixed
+	 * @throws NotFindClassException
+	 * @throws ReflectionException
+	 */
+	private function resolveDefinition($definition, $class, $config, $constrict)
+	{
+		if (!isset($definition['class'])) {
+			throw new NotFindClassException($class);
+		}
+		$_className = $definition['class'];
+		unset($definition['class']);
+
+		$config = array_merge($definition, $config);
+		$definition = $this->mergeParam($class, $constrict);
+
+		if ($_className === $class) {
+			$object = $this->resolve($class, $definition, $config);
+		} else {
+			$object = $this->get($class, $definition, $config);
+		}
+		return $object;
+	}
+
+	/**
+	 * @param $class
+	 * @param $constrict
+	 * @param $config
+	 *
+	 * @return mixed
+	 * @throws NotFindClassException
+	 * @throws ReflectionException
+	 */
+	private function resolve($class, $constrict, $config)
+	{
+		/**
+		 * @var ReflectionClass $reflect
+		 * @var array $dependencies
+		 */
+		list($reflect, $dependencies) = $this->resolveDependencies($class);
+		foreach ($constrict as $index => $param) {
+			$dependencies[$index] = $param;
+		}
+		if (!$reflect->isInstantiable()) {
+			throw new NotFindClassException($reflect->getName());
+		}
+		if (empty($config)) {
+			return $reflect->newInstanceArgs($dependencies ?? []);
+		}
+
+		if (!empty($dependencies) && $reflect->implementsInterface('BeReborn\Base\Configure')) {
+			$dependencies[count($dependencies) - 1] = $config;
+			return $reflect->newInstanceArgs($dependencies);
+		}
+		if (!empty($config)) {
+			$this->_param[$class] = $config;
+		}
+		$object = $reflect->newInstanceArgs($dependencies ?? []);
+		foreach ($config as $key => $val) {
+			$object->{$key} = $val;
+		}
+		if (method_exists($object, 'afterInit')) {
+			call_user_func([$object, 'afterInit']);
+		}
+		return $object;
+	}
+
+	/**
+	 * @param $class
+	 *
+	 * @return array
+	 * @throws ReflectionException
+	 */
+	private function resolveDependencies($class)
+	{
+		$dependencies = [];
+		if (isset($this->_reflection[$class])) {
+			$reflection = $this->_reflection[$class];
+		} else {
+			$reflection = new ReflectionClass($class);
+			$this->_reflection[$class] = $reflection;
+		}
+		$constructs = $reflection->getConstructor();
+		if (empty($constructs) || !is_array($constructs)) {
+			return [$reflection, []];
+		}
+		foreach ($constructs->getParameters() as $key => $param) {
+			if (version_compare(PHP_VERSION, '5.6.0', '>=') && $param->isVariadic()) {
+				break;
+			} else if ($param->isDefaultValueAvailable()) {
+				$dependencies[] = $param->getDefaultValue();
+			} else {
+				$c = $param->getClass();
+				$dependencies[] = $c === NULL ? NULL : $c->getName();
+			}
+		}
+		$this->_constructs[$class] = $dependencies;
+		return [$reflection, $dependencies];
+	}
+
+	/**
+	 * @param $class
+	 */
+	public function unset($class)
+	{
+		if (is_array($class) && isset($class['class'])) {
+			$class = $class['class'];
+		} else if (is_object($class)) {
+			$class = get_class($class);
+		}
+		unset(
+			$this->_reflection[$class], $this->_singletons[$class],
+			$this->_param[$class], $this->_constructs[$class]
+		);
+	}
+
+	/**
+	 * @return $this
+	 */
+	public function flush()
+	{
+		$this->_reflection = [];
+		$this->_singletons = [];
+		$this->_param = [];
+		$this->_constructs = [];
+		return $this;
+	}
+
+	/**
+	 * @param $class
+	 * @param $newParam
+	 *
+	 * @return mixed
+	 */
+	private function mergeParam($class, $newParam)
+	{
+		if (empty($this->_param[$class])) {
+			return $newParam;
+		} else if (empty($newParam)) {
+			return $this->_param[$class];
+		}
+		$old = $this->_param[$class];
+		foreach ($newParam as $key => $val) {
+			$old[$key] = $val;
+		}
+		return $old;
+	}
+}
diff --git a/system/Di/Service.php b/system/Di/Service.php
new file mode 100644
index 00000000..f6292f6e
--- /dev/null
+++ b/system/Di/Service.php
@@ -0,0 +1,142 @@
+_components[$id])) {
+			return $this->_components[$id];
+		}
+		if (isset($this->_definition[$id])) {
+			$object = $this->_definition[$id];
+			if (!is_object($object)) {
+				$object = Snowflake::createObject($object);
+			}
+		} else if (!isset($this->_alias[$id])) {
+			throw new ComponentException("Unknown component ID: $id");
+		} else {
+			$id = $this->_alias[$id];
+
+			$object = Snowflake::createObject($id);
+		}
+		return $this->_components[$id] = $object;
+	}
+
+	/**
+	 * @param string $className
+	 * @param string $alias
+	 */
+	public function setAlias(string $className, string $alias)
+	{
+		$this->_alias[$className] = $alias;
+	}
+
+	/**
+	 * @param $id
+	 * @param $definition
+	 *
+	 * @return callable|mixed|void
+	 * @throws Exception
+	 */
+	public function set($id, $definition)
+	{
+		if ($definition === NULL) {
+			return $this->remove($id);
+		}
+
+		unset($this->_components[$id]);
+		if (is_object($definition) || is_callable($definition, TRUE)) {
+			return $this->_definition[$id] = $definition;
+		} else if (!is_array($definition)) {
+			throw new ComponentException("Unexpected configuration type for the \"$id\" component: " . gettype($definition));
+		}
+		if (!isset($definition['class'])) {
+			throw new ComponentException("The configuration for the \"$id\" component must contain a \"class\" element.");
+		} else {
+			$this->_definition[$id] = $definition;
+		}
+		return $this->_components[$id] = Snowflake::createObject($definition);
+	}
+
+	/**
+	 * @param $id
+	 * @return bool
+	 */
+	public function has($id)
+	{
+		return isset($this->_definition[$id]) || isset($this->_components[$id]) || isset($this->_alias[$id]);
+	}
+
+	/**
+	 * @param array $data
+	 * @throws Exception
+	 */
+	public function setComponents(array $data)
+	{
+		foreach ($data as $key => $val) {
+			$this->set($key, $val);
+		}
+	}
+
+
+	/**
+	 * @param $name
+	 * @return mixed
+	 * @throws Exception
+	 */
+	public function __get($name)
+	{
+		if ($this->has($name)) {
+			return $this->get($name);
+		}
+
+		return parent::__get($name);
+	}
+
+	/**
+	 * @param $id
+	 */
+	public function remove($id)
+	{
+		unset($this->_components[$id]);
+		unset($this->_definition[$id]);
+		if (isset($this->_alias[$id])) {
+			unset($this->_components[$this->_alias[$id]]);
+			unset($this->_definition[$this->_alias[$id]]);
+			unset($this->_alias[$id]);
+		}
+	}
+}
diff --git a/system/Error/ErrorHandler.php b/system/Error/ErrorHandler.php
new file mode 100644
index 00000000..3537db9f
--- /dev/null
+++ b/system/Error/ErrorHandler.php
@@ -0,0 +1,159 @@
+category = 'shutdown';
+
+		$messages = explode(PHP_EOL, $lastError['message']);
+
+		$message = array_shift($messages);
+
+		$this->sendError($message, $lastError['file'], $lastError['line']);
+	}
+
+
+	/**
+	 * @param Exception $exception
+	 *
+	 * @throws Exception
+	 */
+	public function exceptionHandler($exception)
+	{
+		$this->category = 'exception';
+
+		$event = Snowflake::get()->event;
+		$event->trigger(Event::RELEASE_ALL);
+
+		$this->sendError($exception->getMessage(), $exception->getFile(), $exception->getLine());
+	}
+
+	/**
+	 * @throws Exception
+	 *
+	 * 以异常形式抛出错误,防止执行后续程序
+	 */
+	public function errorHandler()
+	{
+		$error = func_get_args();
+		if (strpos($error[2], 'vendor/Reboot.php') !== FALSE) {
+			return;
+		}
+
+		$path = ['file' => $error[2], 'line' => $error[3]];
+
+		if ($error[0] === 0) {
+			$error[0] = 500;
+		}
+
+		$data = JSON::to(500, 'Error : ' . $error[1], $path);
+
+		Logger::error($data, 'error');
+
+		$event = Snowflake::get()->event;
+		$event->trigger(Event::RELEASE_ALL);
+
+		throw new \ErrorException($error[1], $error[0], 1, $error[2], $error[3]);
+	}
+
+	/**
+	 * @param $message
+	 * @param $file
+	 * @param $line
+	 * @param int $code
+	 * @return false|string
+	 * @throws Exception
+	 */
+	public function sendError($message, $file, $line, $code = 500)
+	{
+		$path = ['file' => $file, 'line' => $line];
+
+		$data = JSON::to($code, $this->category . ': ' . $message, $path);
+
+		Logger::trance($data, $this->category);
+
+		return print_r($data);
+	}
+
+	/**
+	 * @return mixed
+	 */
+	public function getErrorMessage()
+	{
+		$message = $this->message;
+		$this->message = NULL;
+		return $message->getData();
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function getAsError()
+	{
+		return $this->message !== NULL;
+	}
+
+	/**
+	 * @param $message
+	 * @param $category
+	 *
+	 * @throws Exception
+	 */
+	public function writer($message, $category = 'app')
+	{
+		Logger::debug($message, $category);
+	}
+}
diff --git a/system/Error/ErrorInterface.php b/system/Error/ErrorInterface.php
new file mode 100644
index 00000000..dca2cc28
--- /dev/null
+++ b/system/Error/ErrorInterface.php
@@ -0,0 +1,27 @@
+getMessage();
+		} else {
+			if (is_array($message) || is_object($message)) {
+				$message = static::arrayFormat($message);
+			}
+		}
+		if (is_array($message)) {
+			$message = static::arrayFormat($message);
+		}
+		if (!empty($message)) {
+			if (!is_array(static::$logs)) {
+				static::$logs = [];
+			}
+			static::$logs[] = [$category, $message];
+		}
+		return $message;
+	}
+
+
+	/**
+	 * @param $message
+	 * @param $category
+	 * @throws Exception
+	 */
+	public static function print_r($message, $category = '')
+	{
+		/** @var Process $logger */
+		$logger = \BeReborn::getApp('logger');
+		$logger->write(JSON::encode([$message, $category]));
+	}
+
+
+	/**
+	 * @param string $application
+	 * @return mixed
+	 */
+	public static function getLastError($application = 'app')
+	{
+		$_tmp = [];
+		foreach (static::$logs as $key => $val) {
+			if ($val[0] != $application) {
+				continue;
+			}
+			$_tmp[] = $val[1];
+		}
+		if (empty($_tmp)) {
+			return 'Unknown error.';
+		}
+		return end($_tmp);
+	}
+
+	/**
+	 * @param $messages
+	 * @param string $category
+	 * @throws Exception
+	 */
+	public static function write(string $messages, $category = 'app')
+	{
+		if (empty($messages)) {
+			return;
+		}
+		$fileName = 'server-' . date('Y-m-d') . '.log';
+		$dirName = 'log/' . (empty($category) ? 'app' : $category);
+		$logFile = '[' . date('Y-m-d H:i:s') . ']' . $messages . PHP_EOL;
+		Snowflake::writeFile(storage($fileName, $dirName), $logFile, FILE_APPEND);
+	}
+
+	/**
+	 * @param $logFile
+	 * @return false|string
+	 */
+	private static function getSource($logFile)
+	{
+		if (!file_exists($logFile)) {
+			shell_exec('echo 3 > /proc/sys/vm/drop_caches');
+			touch($logFile);
+		}
+		if (is_writeable($logFile)) {
+			$logFile = realpath($logFile);
+		}
+		return $logFile;
+	}
+
+	/**
+	 * @throws Exception
+	 * 写入日志
+	 */
+	public static function insert()
+	{
+		if (empty(static::$logs)) {
+			return;
+		}
+		foreach (static::$logs as $log) {
+			[$category, $message] = $log;
+			static::write($message, $category);
+		}
+		static::$logs = [];
+	}
+
+	/**
+	 * @return array
+	 */
+	public static function clear()
+	{
+		return static::$logs = [];
+	}
+
+	/**
+	 * @param $data
+	 * @return string
+	 */
+	private static function arrayFormat($data)
+	{
+		if (is_string($data)) {
+			return $data;
+		}
+		if ($data instanceof Exception) {
+			$data = static::getException($data);
+		} else if (is_object($data)) {
+			$data = get_object_vars($data);
+		}
+
+		$_tmp = [];
+		foreach ($data as $key => $val) {
+			if (is_array($val)) {
+				$_tmp[] = static::arrayFormat($val);
+			} else {
+				$_tmp[] = (is_string($key) ? $key . ' : ' : '') . $val;
+			}
+		}
+		return implode(PHP_EOL, $_tmp);
+	}
+
+	/**
+	 * @param Exception $exception
+	 * @return array
+	 */
+	private static function getException($exception)
+	{
+		$_tmp = [$exception->getMessage()];
+		$_tmp[] = $exception->getFile() . ' on line ' . $exception->getLine();
+		$_tmp[] = $exception->getTrace();
+		return $_tmp;
+	}
+
+}
diff --git a/system/Event.php b/system/Event.php
new file mode 100644
index 00000000..bf07843b
--- /dev/null
+++ b/system/Event.php
@@ -0,0 +1,199 @@
+_events[$name])) {
+			$this->_events[$name] = [];
+		}
+		if ($callback instanceof \Closure) {
+			$callback = \Closure::bind($callback, Snowflake::get());
+		} else if (is_array($callback) && is_string($callback[0])) {
+			if (!class_exists($callback[0])) {
+				throw new \Exception('Undefined callback class.');
+			}
+			$callback[0] = Snowflake::createObject($callback[0]);
+		}
+		if (!empty($this->_events[$name]) && $isAppend === true) {
+			array_unshift($this->_events[$name], [$callback, $parameter]);
+		} else {
+			$this->_events[$name][] = [$callback, $parameter];
+		}
+	}
+
+
+	/**
+	 * @param $name
+	 * @param $callback
+	 */
+	public function of($name, $callback)
+	{
+		if (!isset($this->_events[$name])) {
+			return;
+		}
+		foreach ($this->_events[$name] as $index => $event) {
+			[$handler, $parameter] = $event;
+			if ($handler !== $callback) {
+				continue;
+			}
+			unset($this->_events[$name][$index]);
+		}
+	}
+
+
+	/**
+	 * @param $name
+	 * @return bool
+	 */
+	public function offName($name)
+	{
+		if (!$this->exists($name)) {
+			return true;
+		}
+		unset($this->_events[$name]);
+		return $this->exists($name);
+	}
+
+
+	/**
+	 * @param $name
+	 * @param null $callback
+	 * @return bool
+	 */
+	public function exists($name, $callback = null)
+	{
+		if (!isset($this->_events[$name])) {
+			return false;
+		}
+		if ($callback === null) {
+			return true;
+		}
+		foreach ($this->_events[$name] as $event) {
+			[$handler, $parameter] = $event;
+			if ($handler === $callback) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+
+	/**
+	 * @param $name
+	 * @param $handler
+	 * @return mixed|null
+	 */
+	public function get($name, $handler)
+	{
+		if (!$this->exists($name)) {
+			return null;
+		}
+		foreach ($this->_events[$name] as $event) {
+			[$callback, $parameter] = $event;
+			if ($callback === $handler) {
+				return $event;
+			}
+		}
+		return null;
+	}
+
+
+	/**
+	 * @param $name
+	 * @param null $handler
+	 * @param null $parameter
+	 * @param false $is_remove
+	 * @return bool|mixed
+	 * @throws \Exception
+	 */
+	public function trigger($name, $parameter = null, $handler = null, $is_remove = false)
+	{
+		if (!$this->exists($name)) {
+			return false;
+		}
+		if (!empty($handler) && $this->exists($name, $handler)) {
+			[$handler, $defaultParameter] = $this->get($name, $handler);
+			if (!empty($parameter)) {
+				$defaultParameter = ArrayAccess::merge($defaultParameter, $parameter);
+			}
+			if (!is_array($defaultParameter)) {
+				$defaultParameter = [$defaultParameter];
+			}
+			$result = call_user_func($handler, ...$defaultParameter);
+			if ($is_remove) {
+				$this->of($name, $handler);
+			}
+			return $result;
+		}
+		foreach ($this->_events[$name] as $event) {
+			[$handler, $defaultParameter] = $event;
+			try {
+				if (!empty($parameter)) {
+					$defaultParameter = ArrayAccess::merge($defaultParameter, $parameter);
+				}
+				if (!is_array($defaultParameter)) {
+					$defaultParameter = [$defaultParameter];
+				}
+				call_user_func($handler, ...$defaultParameter);
+			} catch (\Throwable $exception) {
+				$this->error($exception->getMessage());
+			}
+		}
+		if ($is_remove) {
+			$this->offName($name);
+		}
+		return true;
+	}
+
+
+}
diff --git a/system/Exception/ComponentException.php b/system/Exception/ComponentException.php
new file mode 100644
index 00000000..486561b7
--- /dev/null
+++ b/system/Exception/ComponentException.php
@@ -0,0 +1,26 @@
+timeout = $timeout;
+	}
+
+
+	/**
+	 * @param $value
+	 */
+	public function setLength($value)
+	{
+		$this->max = $value;
+	}
+
+	/**
+	 * @param $cds
+	 * @return bool
+	 *
+	 * db is in transaction
+	 */
+	public function inTransaction($cds)
+	{
+		[$coroutineId, $coroutineName] = $this->getIndex($cds, true);
+		if (!Context::hasContext('begin_' . $coroutineName, $coroutineId)) {
+			return false;
+		}
+		return Context::getContext('begin_' . $coroutineName, $coroutineId) == 0;
+	}
+
+	/**
+	 * @param $coroutineName
+	 */
+	public function beginTransaction($coroutineName)
+	{
+		[$coroutineId, $coroutineName] = $this->getIndex($coroutineName, true);
+		if (!Context::hasContext('begin_' . $coroutineName, $coroutineId)) {
+			Context::setContext('begin_' . $coroutineName, 0, $coroutineId);
+		}
+		if (Context::getContext('begin_' . $coroutineName, $coroutineId) === 0) {
+			$connection = Context::getContext($coroutineName);
+			if ($connection instanceof PDO && !$connection->inTransaction()) {
+				$connection->beginTransaction();
+			}
+		}
+		Context::autoIncr('begin_' . $coroutineName, $coroutineId);
+	}
+
+	/**
+	 * @param $coroutineName
+	 */
+	public function commit($coroutineName)
+	{
+		[$coroutineId, $coroutineName] = $this->getIndex($coroutineName, true);
+		if (!Context::hasContext('begin_' . $coroutineName, $coroutineId)) {
+			return;
+		}
+		if (Context::autoDecr('begin_' . $coroutineName, $coroutineId) > 0) {
+			return;
+		}
+		$connection = Context::getContext($coroutineName);
+		if ($connection instanceof PDO) {
+			if ($connection->inTransaction()) {
+				$this->info('connection commit.');
+				$connection->commit();
+			}
+			Context::setContext('begin_' . $coroutineName, 0, $coroutineId);
+		}
+	}
+
+	/**
+	 * @param $coroutineId
+	 * @param $coroutineName
+	 * @return array
+	 */
+	private function instanceTrance($coroutineId, $coroutineName)
+	{
+		return [$coroutineId, $coroutineName];
+	}
+
+	/**
+	 * @param $name
+	 * @param false $isMaster
+	 * @return array
+	 */
+	private function getIndex($name, $isMaster = false)
+	{
+		return $this->instanceTrance(Coroutine::getCid(), $this->name($name, $isMaster));
+	}
+
+	/**
+	 * @param $coroutineName
+	 */
+	public function rollback($coroutineName)
+	{
+		[$coroutineId, $coroutineName] = $this->getIndex($coroutineName, true);
+		if (!Context::hasContext('begin_' . $coroutineName, $coroutineId)) {
+			return;
+		}
+		if (Context::autoDecr('begin_' . $coroutineName, $coroutineId) > 0) {
+			return;
+		}
+		if ($this->hasClient($coroutineName)) {
+			/** @var PDO $connection */
+			$connection = Context::getContext($coroutineName);
+			if ($connection->inTransaction()) {
+				$this->info('connection rollBack.');
+				$connection->rollBack();
+			}
+		}
+		Context::setContext('begin_' . $coroutineName, 0, $coroutineId);
+	}
+
+
+	/**
+	 * @param array $config
+	 * @param bool $isMaster
+	 * @return mixed
+	 * @throws Exception
+	 */
+	public function getConnection(array $config, $isMaster = false)
+	{
+		[$coroutineId, $coroutineName] = $this->getIndex($config['cds'], $isMaster);
+		if (Context::hasContext($coroutineName)) {
+			return Context::getContext($coroutineName);
+		} else if (!$this->hasItem($coroutineName)) {
+			return $this->saveClient($coroutineName, $this->nowClient($coroutineName, $config));
+		} else {
+			return $this->getByChannel($coroutineName, $config);
+		}
+	}
+
+
+	/**
+	 * @param $name
+	 * @param $config
+	 * @return Connection
+	 * @throws Exception
+	 */
+	public function fill($name, $config)
+	{
+		if ($this->hasLength($name) >= 10) {
+			return $this;
+		}
+		for ($i = 0; $i < 10 - $this->hasLength($name); $i++) {
+			$this->push($name, $this->createConnect($config['cds'], $config['username'], $config['password']));
+		}
+		return $this;
+	}
+
+	/**
+	 * @param $coroutineName
+	 * @param $config
+	 * @return mixed
+	 * @throws Exception
+	 */
+	public function getByChannel($coroutineName, $config)
+	{
+		$this->info('client has :' . $this->hasLength($coroutineName));
+		[$time, $client] = $this->get($coroutineName, -1);
+		if ($client instanceof PDO) {
+			return $this->saveClient($coroutineName, $client);
+		}
+		unset($client);
+		if (!$this->hasItem($coroutineName)) {
+			return $this->saveClient($coroutineName, $this->nowClient($coroutineName, $config));
+		}
+		return $this->getByChannel($coroutineName, $config);
+	}
+
+
+	/**
+	 * @param $coroutineName
+	 * @param $client
+	 * @return mixed
+	 */
+	private function saveClient($coroutineName, $client)
+	{
+		return Context::setContext($coroutineName, $client);
+	}
+
+
+	/**
+	 * @param $coroutineName
+	 * @param $config
+	 * @return PDO
+	 * @throws Exception
+	 */
+	private function nowClient($coroutineName, $config)
+	{
+		$client = $this->createConnect($config['cds'], $config['username'], $config['password']);
+		$this->success('create db client -> ' . $config['cds'] . ':' . $this->hasLength($coroutineName));
+		if (isset(Context::getContext('begin_' . $coroutineName)[Coroutine::getCid()])) {
+			$number = Context::getContext('begin_' . $coroutineName)[Coroutine::getCid()];
+			$number > 0 && $client->beginTransaction();
+		}
+		$this->incr($coroutineName);
+		return $client;
+	}
+
+
+	/**
+	 * @param $coroutineName
+	 * @param $isMaster
+	 */
+	public function release($coroutineName, $isMaster)
+	{
+		[$coroutineId, $coroutineName] = $this->getIndex($coroutineName, $isMaster);
+		if (!$this->hasClient($coroutineName)) {
+			return;
+		}
+
+		/** @var PDO $client */
+		$client = Context::getContext($coroutineName);
+		if ($client->inTransaction()) {
+			$client->commit();
+		}
+		$this->push($coroutineName, $client);
+		$this->remove($coroutineName);
+	}
+
+
+	/**
+	 * @param $coroutineName
+	 * @return bool
+	 */
+	private function hasClient($coroutineName)
+	{
+		return Context::hasContext($coroutineName);
+	}
+
+
+	/**
+	 * batch release
+	 */
+	public function connection_clear()
+	{
+		$this->debug('receive all clients.');
+		$connections = Context::getAllContext();
+		foreach ($connections as $name => $connection) {
+			if (empty($connection) || !($connection instanceof PDO)) {
+				continue;
+			}
+			/** @var PDO $pdoClient */
+			if ($connection->inTransaction()) {
+				$connection->commit();
+			}
+			$this->push($name, $connection);
+			$this->remove($name);
+		}
+	}
+
+
+	/**
+	 * @param $coroutineName
+	 */
+	public function remove($coroutineName)
+	{
+		Context::deleteId($coroutineName);
+	}
+
+	/**
+	 * @param $time
+	 * @param $connect
+	 * @return bool
+	 */
+	public function checkCanUse($time, $connect)
+	{
+		try {
+			if ($time + 60 * 10 < time()) {
+				return false;
+			}
+			if (empty($connect) || !($connect instanceof PDO)) {
+				return false;
+			}
+			if (!$connect->getAttribute(PDO::ATTR_SERVER_INFO)) {
+				return false;
+			}
+			return true;
+		} catch (\Swoole\Error | \Throwable $exception) {
+			return false;
+		}
+	}
+
+
+	/**
+	 * @param $cds
+	 * @param $username
+	 * @param $password
+	 * @return PDO
+	 * @throws Exception
+	 */
+	public function createConnect($cds, $username, $password)
+	{
+		try {
+			$link = new PDO($cds, $username, $password, [
+				PDO::ATTR_EMULATE_PREPARES => false,
+				//                PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE,
+				PDO::ATTR_CASE             => PDO::CASE_NATURAL,
+				PDO::ATTR_TIMEOUT          => $this->timeout,
+			]);
+			$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+			$link->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
+			$link->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING);
+			return $link;
+		} catch (\Throwable $exception) {
+			if ($exception->getCode() !== 2006) {
+				$this->addError($cds . '  ->  ' . $exception->getMessage());
+				throw new Exception($exception->getMessage());
+			}
+			$this->addError($cds . '  ->  ' . $exception->getMessage());
+			return $this->createConnect($cds, $username, $password);
+		}
+	}
+
+	/**
+	 * @param $coroutineName
+	 */
+	public function disconnect($coroutineName)
+	{
+		if (!$this->hasClient($coroutineName)) {
+			return;
+		}
+		$this->remove($coroutineName);
+		$this->clean($coroutineName);
+	}
+
+	/**
+	 * @param $coroutineName
+	 */
+	public function incr($coroutineName)
+	{
+		if (!isset($this->hasCreate[$coroutineName])) {
+			$this->hasCreate[$coroutineName] = 0;
+		}
+		$this->hasCreate[$coroutineName] += 1;
+	}
+
+	/**
+	 * @param $coroutineName
+	 */
+	public function desc($coroutineName)
+	{
+		if (!isset($this->hasCreate[$coroutineName])) {
+			$this->hasCreate[$coroutineName] = 0;
+		}
+		$this->hasCreate[$coroutineName] -= 1;
+	}
+}
diff --git a/system/Pool/Pool.php b/system/Pool/Pool.php
new file mode 100644
index 00000000..2ae4aab7
--- /dev/null
+++ b/system/Pool/Pool.php
@@ -0,0 +1,34 @@
+redis_connections;
+	}
+
+	/**
+	 * @return Connection
+	 */
+	public function getDb()
+	{
+		return Snowflake::get()->connections;
+	}
+
+}
diff --git a/system/Pool/RedisClient.php b/system/Pool/RedisClient.php
new file mode 100644
index 00000000..5a4112f1
--- /dev/null
+++ b/system/Pool/RedisClient.php
@@ -0,0 +1,182 @@
+max = $value;
+	}
+
+
+	/**
+	 * @param array $config
+	 * @param bool $isMaster
+	 * @return mixed|null
+	 * @throws Exception
+	 */
+	public function getConnection(array $config, $isMaster = false)
+	{
+		$name = $config['host'] . ':' . $config['prefix'] . ':' . $config['databases'];
+		[$coroutineId, $coroutineName] = $this->getIndex('redis:' . $name, $isMaster);
+		if (Context::hasContext($coroutineName)) {
+			return Context::getContext($coroutineName);
+		} else if (!$this->hasItem($coroutineName)) {
+			$this->success('create redis client -> ' . $config['host'] . ':' . $this->hasLength($coroutineName));
+			return $this->saveClient($coroutineName, $this->createConnect($config));
+		}
+		return $this->getByChannel($coroutineName, $config);
+	}
+
+
+	/**
+	 * @param $coroutineName
+	 * @param $config
+	 * @return mixed
+	 * @throws Exception
+	 */
+	public function getByChannel($coroutineName, $config)
+	{
+		$this->info('redis client has :' . $this->hasLength($coroutineName));
+		if (!$this->hasItem($coroutineName)) {
+			$this->success('create redis client -> ' . $config['host'] . ':' . $this->hasLength($coroutineName));
+			return $this->saveClient($coroutineName, $this->createConnect($config));
+		}
+		[$time, $client] = $this->get($coroutineName, -1);
+		if ($client === null) {
+			return $this->getByChannel($coroutineName, $config);
+		}
+		return $this->saveClient($coroutineName, $client);
+	}
+
+
+	/**
+	 * @param $coroutineName
+	 * @param $client
+	 * @return mixed
+	 * @throws Exception
+	 */
+	private function saveClient($coroutineName, $client)
+	{
+		return Context::setContext($coroutineName, $client);
+	}
+
+
+	/**
+	 * @param array $config
+	 * @return Redis
+	 * @throws Exception
+	 */
+	private function createConnect(array $config)
+	{
+		$redis = new Redis();
+		if (!$redis->connect($config['host'], $config['port'], $config['timeout'])) {
+			throw new Exception('The Redis Connect Fail.');
+		}
+		if (empty($config['auth']) || !$redis->auth($config['auth'])) {
+			throw new Exception(sprintf('Redis Error: %s, Host %s, Auth %s', $redis->getLastError(), $config['host'], $config['auth']));
+		}
+		if (!isset($config['read_timeout'])) {
+			$config['read_timeout'] = 10;
+		}
+		$redis->select($config['databases']);
+		$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
+		$redis->setOption(Redis::OPT_PREFIX, $config['prefix']);
+		return $redis;
+	}
+
+	/**
+	 * @param array $config
+	 * @param bool $isMaster
+	 */
+	public function release(array $config, $isMaster = false)
+	{
+		$name = $config['host'] . ':' . $config['prefix'] . ':' . $config['databases'];
+		[$coroutineId, $coroutineName] = $this->getIndex('redis:' . $name, $isMaster);
+		if (!Context::hasContext($coroutineName)) {
+			return;
+		}
+
+		$client = Context::getContext($coroutineName);
+
+		$this->push($coroutineName, $client);
+		$this->remove($coroutineName);
+	}
+
+	/**
+	 * @param array $config
+	 * @param bool $isMaster
+	 */
+	public function destroy(array $config, $isMaster = false)
+	{
+		$name = $config['host'] . ':' . $config['prefix'] . ':' . $config['databases'];
+		[$coroutineId, $coroutineName] = $this->getIndex('redis:' . $name, $isMaster);
+		if (!Context::hasContext($coroutineName)) {
+			return;
+		}
+		$this->remove($coroutineName);
+		$this->clean($coroutineName);
+	}
+
+	/**
+	 * @param $coroutineName
+	 */
+	public function remove($coroutineName)
+	{
+		Context::deleteId($coroutineName);
+	}
+
+	/**
+	 * @param $time
+	 * @param $client
+	 * @return bool|mixed
+	 * @throws RedisException
+	 */
+	public function checkCanUse($time, $client)
+	{
+		if ($time + 60 * 10 < time()) {
+			return false;
+		}
+		if (!($client instanceof Redis)) {
+			return false;
+		}
+		if (!$client->isConnected() || !$client->ping('connect.')) {
+			return false;
+		}
+		return true;
+	}
+
+	public function desc($name)
+	{
+		// TODO: Implement desc() method.
+	}
+
+	/**
+	 * @param $name
+	 * @param false $isMaster
+	 * @return array
+	 */
+	private function getIndex($name, $isMaster = false)
+	{
+		return [Coroutine::getCid(), $this->name($name, $isMaster)];
+	}
+
+
+}
diff --git a/system/Process/ISystem.php b/system/Process/ISystem.php
new file mode 100644
index 00000000..9ca9b55e
--- /dev/null
+++ b/system/Process/ISystem.php
@@ -0,0 +1,12 @@
+read());
+		});
+	}
+
+}
diff --git a/system/Process/PrintIn.php b/system/Process/PrintIn.php
new file mode 100644
index 00000000..4a3fc49b
--- /dev/null
+++ b/system/Process/PrintIn.php
@@ -0,0 +1,20 @@
+application = $application;
+		parent::__construct([]);
+	}
+
+
+	/**
+	 * @param \Swoole\Process $process
+	 * @return mixed
+	 */
+	abstract public function onHandler(\Swoole\Process $process);
+
+
+	/**
+	 * @param $process
+	 */
+	protected function start($process)
+	{
+		$this->onHandler($process);
+	}
+
+}
diff --git a/system/Process/ServerInotify.php b/system/Process/ServerInotify.php
new file mode 100644
index 00000000..929f72a5
--- /dev/null
+++ b/system/Process/ServerInotify.php
@@ -0,0 +1,195 @@
+pid);
+		if (extension_loaded('inotify')) {
+			$this->inotify = inotify_init();
+			$this->events = IN_MODIFY | IN_DELETE | IN_CREATE | IN_MOVE;
+			$process->name('event: file change.');
+
+			$this->watch(APP_PATH);
+			Event::add($this->inotify, [$this, 'check']);
+			Event::wait();
+		} else {
+			Timer::tick(1000, [$this, 'tick']);
+		}
+	}
+
+
+	public function tick()
+	{
+	}
+
+	/**
+	 * 开始监听
+	 */
+	public function check()
+	{
+		if (!($events = inotify_read($this->inotify))) {
+			return;
+		}
+		if ($this->isReloading) {
+			if (!$this->isReloadingOut) {
+				$this->isReloadingOut = true;
+			}
+			return;
+		}
+
+		$eventList = [IN_CREATE, IN_DELETE, IN_MODIFY, IN_MOVED_TO, IN_MOVED_FROM];
+		foreach ($events as $ev) {
+			if (empty($ev['name'])) {
+				continue;
+			}
+			if ($ev['mask'] == IN_IGNORED) {
+				continue;
+			} else if (in_array($ev['mask'], $eventList)) {
+				$fileType = strstr($ev['name'], '.');
+				//非重启类型
+				if ($fileType !== '.php') {
+					continue;
+				}
+			} else {
+				continue;
+			}
+			try {
+				if ($this->int !== -1) {
+					return;
+				}
+				$this->int = @swoole_timer_after(2000, [$this, 'reload']);
+			} catch (Exception $exception) {
+			}
+
+			$this->isReloading = true;
+		}
+	}
+
+	/**
+	 * @throws Exception
+	 */
+	public function reload()
+	{
+		//清理所有监听
+		$this->trigger_reload();
+		$this->clearWatch();
+
+		//重新监听
+		foreach ($this->dirs as $root) {
+			$this->watch($root);
+		}
+
+		$this->int = -1;
+		$this->isReloading = FALSE;
+		$this->isReloadingOut = FALSE;
+	}
+
+	/**
+	 * 重启
+	 */
+	public function trigger_reload()
+	{
+		Snowflake::reload();
+	}
+
+	/**
+	 * 清理所有inotify监听
+	 */
+	public function clearWatch()
+	{
+		try {
+			foreach ($this->watchFiles as $wd) {
+				@inotify_rm_watch($this->inotify, $wd);
+			}
+		} catch (Exception $exception) {
+		}
+		$this->watchFiles = [];
+	}
+
+
+	/**
+	 * @param $dir
+	 * @param bool $root
+	 * @return bool
+	 * @throws Exception
+	 */
+	public function watch($dir, $root = TRUE)
+	{
+		//目录不存在
+		if (!is_dir($dir)) {
+			throw new Exception("[$dir] is not a directory.");
+		}
+		//避免重复监听
+		if (isset($this->watchFiles[$dir])) {
+			return FALSE;
+		}
+		//根目录
+		if ($root) {
+			$this->dirs[] = $dir;
+		}
+
+		if (in_array($dir, [APP_PATH . '/config', APP_PATH . '/commands', APP_PATH . '/.git', APP_PATH . '/.gitee'])) {
+			return FALSE;
+		}
+
+		$wd = @inotify_add_watch($this->inotify, $dir, $this->events);
+		$this->watchFiles[$dir] = $wd;
+
+		$files = scandir($dir);
+		foreach ($files as $f) {
+			if ($f == '.' or $f == '..' or $f == 'runtime' or preg_match('/\.txt/', $f) or preg_match('/\.sql/', $f) or preg_match('/\.log/', $f)) {
+				continue;
+			}
+			$path = $dir . '/' . $f;
+			//递归目录
+			if (is_dir($path)) {
+				$this->watch($path, FALSE);
+			}
+
+			//检测文件类型
+			$fileType = strstr($f, '.');
+			if ($fileType == '.php') {
+				try {
+					$wd = @inotify_add_watch($this->inotify, $path, $this->events);
+					$this->watchFiles[$path] = $wd;
+				} catch (Exception $exception) {
+				}
+			}
+		}
+		return TRUE;
+	}
+}
diff --git a/system/Process/System.php b/system/Process/System.php
new file mode 100644
index 00000000..885c418a
--- /dev/null
+++ b/system/Process/System.php
@@ -0,0 +1,12 @@
+ [
+		'Leafleting' => Leafleting::class,
+		'inotify'    => ServerInotify::class,
+	]
+
+];
diff --git a/system/Processes.php b/system/Processes.php
new file mode 100644
index 00000000..40948ba1
--- /dev/null
+++ b/system/Processes.php
@@ -0,0 +1,119 @@
+set(Pool::class, new Pool($this->size(), SWOOLE_IPC_UNIXSOCK));
+		$server->on('workerStart', function (Pool $pool, int $workerId) use ($application) {
+			ServerManager::create($pool, $this->processes[$workerId], $application, $workerId);
+		});
+		$server->on('workerStop', function (Pool $pool, int $workerId) {
+			$event = Snowflake::get()->event;
+			if ($event->exists(Event::PROCESS_WORKER_STOP)) {
+				$event->trigger(Event::PROCESS_WORKER_STOP);
+			}
+		});
+		$server->on('message', function ($pool, $message) {
+			file_put_contents('a.log', func_get_args());
+		});
+		return $server;
+	}
+
+
+	protected function system_mail()
+	{
+		try {
+			$mail = new PHPMailer(true);
+			//Server settings
+			$mail->SMTPDebug = SMTP::DEBUG_SERVER;                      // Enable verbose debug output
+			$mail->isSMTP();                                            // Send using SMTP
+			$mail->Host = 'smtp1.example.com';                          // Set the SMTP server to send through
+			$mail->SMTPAuth = true;                                     // Enable SMTP authentication
+			$mail->Username = 'user@example.com';                       // SMTP username
+			$mail->Password = 'secret';                                 // SMTP password
+			$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;         // Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` encouraged
+			$mail->Port = 587;                                          // TCP port to connect to, use 465 for `PHPMailer::ENCRYPTION_SMTPS` above
+
+			//Recipients
+			$mail->setFrom('from@example.com', 'Mailer');
+			$mail->addAddress('joe@example.net', 'Joe User');     // Add a recipient
+			$mail->addAddress('ellen@example.com');               // Name is optional
+			$mail->addReplyTo('info@example.com', 'Information');
+			$mail->addCC('cc@example.com');
+			$mail->addBCC('bcc@example.com');
+
+			// Attachments
+			$mail->addAttachment('/var/tmp/file.tar.gz');         // Add attachments
+			$mail->addAttachment('/tmp/image.jpg', 'new.jpg');    // Optional name
+
+			// Content
+			$mail->isHTML(true);                                  // Set email format to HTML
+			$mail->Subject = 'Here is the subject';
+			$mail->Body = 'This is the HTML message body in bold!';
+			$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
+
+			$mail->send();
+			echo 'Message has been sent';
+		} catch (Exception $e) {
+			echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
+		}
+	}
+
+	/**
+	 * @return int
+	 */
+	protected function size()
+	{
+		return count($this->processes);
+	}
+
+
+	/**
+	 * @throws Exception
+	 */
+	public function start()
+	{
+		$server = $this->initCore();
+		$server->start();
+	}
+
+
+	/**
+	 * @param array $servers
+	 * @return $this
+	 */
+	public function push(array $servers)
+	{
+		foreach ($servers as $server) {
+			$this->processes[] = $server;
+		}
+		return $this;
+	}
+}
diff --git a/system/Snowflake.php b/system/Snowflake.php
new file mode 100644
index 00000000..4c4312da
--- /dev/null
+++ b/system/Snowflake.php
@@ -0,0 +1,191 @@
+has($name);
+	}
+
+
+	/**
+	 * @param $className
+	 * @param $id
+	 */
+	public static function setAlias($className, $id)
+	{
+		return static::$service->setAlias($className, $id);
+	}
+
+
+	/**
+	 * @param $className
+	 * @param array $construct
+	 * @return mixed
+	 * @throws NotFindClassException
+	 * @throws ReflectionException
+	 * @throws Exception
+	 */
+	public static function createObject($className, $construct = [])
+	{
+		if (is_string($className)) {
+			return static::$container->get($className, $construct);
+		} else if (is_array($className)) {
+			if (!isset($className['class']) || empty($className['class'])) {
+				throw new Exception('Object configuration must be an array containing a "class" element.');
+			}
+			$class = $className['class'];
+			unset($className['class']);
+			return static::$container->get($class, $construct, $className);
+		} else if (is_callable($className, TRUE)) {
+			return call_user_func($className, $construct);
+		} else {
+			throw new Exception('Unsupported configuration type: ' . gettype($className));
+		}
+	}
+
+	/**
+	 * @return string
+	 * @throws Exception
+	 */
+	public static function getStoragePath()
+	{
+		$path = realpath(static::$service->storage);
+		if (!is_dir($path)) {
+//			mkdir($path);
+		}
+		return $path;
+	}
+
+
+	/**
+	 * @return bool
+	 */
+	public static function inCoroutine()
+	{
+		return Coroutine::getCid() > 0;
+	}
+
+
+	/**
+	 * @param $workerId
+	 * @return false|int|mixed
+	 * @throws Exception
+	 */
+	public static function setProcessId($workerId)
+	{
+		return self::writeFile(storage('socket.sock'), $workerId);
+	}
+
+
+	/**
+	 * @param $fileName
+	 * @param $content
+	 * @param int $is_append
+	 * @return false|int|mixed
+	 */
+	public static function writeFile($fileName, $content, $is_append = FILE_APPEND)
+	{
+		return false;
+//		if (self::inCoroutine()) {
+//			return Coroutine::writeFile($fileName, $content, $is_append);
+//		} else {
+//			return file_put_contents($fileName, $content, $is_append);
+//		}
+	}
+
+
+	/**
+	 * @param $object
+	 * @param $config
+	 * @return mixed
+	 */
+	public static function configure($object, $config)
+	{
+		foreach ($config as $key => $value) {
+			$object->$key = $value;
+		}
+		return $object;
+	}
+
+	public static function clearProcessId($worker_pid)
+	{
+
+	}
+
+
+	/**
+	 * @return bool
+	 */
+	public static function isMac()
+	{
+		$output = strtolower(PHP_OS | PHP_OS_FAMILY);
+		if (strpos('mac', $output) !== false) {
+			return true;
+		} else if (strpos('darwin', $output) !== false) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * @return bool
+	 */
+	public static function isLinux()
+	{
+		if (!static::isMac()) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	public static function reload()
+	{
+	}
+
+}
+
+Snowflake::$container = new Container();
diff --git a/test.php b/test.php
new file mode 100644
index 00000000..856a20da
--- /dev/null
+++ b/test.php
@@ -0,0 +1,15 @@
+start();