From 86e8ad68e9c9af85f5e8e69c58577d6b3827e17d Mon Sep 17 00:00:00 2001 From: "as2252258@163.com" Date: Sun, 28 Mar 2021 16:09:18 +0800 Subject: [PATCH] modify --- Annotation/Loader.php | 464 ++++++------- HttpServer/Abstracts/Callback.php | 2 - HttpServer/Command.php | 57 +- HttpServer/Events/OnBeforeReload.php | 3 + HttpServer/Events/OnWorkerStart.php | 4 +- HttpServer/Shutdown.php | 123 ++++ System/Abstracts/BaseApplication.php | 868 ++++++++++++------------ System/Abstracts/TraitApplication.php | 2 + System/Process/Process.php | 2 +- System/Snowflake.php | 910 ++++++++++++++------------ function.php | 51 +- 11 files changed, 1341 insertions(+), 1145 deletions(-) create mode 100644 HttpServer/Shutdown.php diff --git a/Annotation/Loader.php b/Annotation/Loader.php index f8251a84..438bd3a6 100644 --- a/Annotation/Loader.php +++ b/Annotation/Loader.php @@ -24,280 +24,280 @@ class Loader extends BaseObject { - private array $_classes = []; + private array $_classes = []; - private array $_fileMap = []; + private array $_fileMap = []; - private array $_directoryMap = []; + private array $_directoryMap = []; - /** - * @param $path - * @param $namespace - * @throws Exception - */ - public function loader($path, $namespace) - { - $this->_scanDir(new DirectoryIterator($path), $namespace); - } + /** + * @param $path + * @param $namespace + * @throws Exception + */ + public function loader($path, $namespace) + { + $this->_scanDir(new DirectoryIterator($path), $namespace); + } - /** - * @return array - */ - public function getClasses(): array - { - return $this->_classes; - } + /** + * @return array + */ + public function getClasses(): array + { + return $this->_classes; + } - /** - * @param string $class - * @param string $property - * @return mixed - */ - public function getProperty(string $class, string $property = ''): mixed - { - if (!isset($this->_classes[$class])) { - return null; - } - $properties = $this->_classes[$class]['property']; - if (!empty($property) && isset($properties[$property])) { - return $properties[$property]; - } - return $properties; - } + /** + * @param string $class + * @param string $property + * @return mixed + */ + public function getProperty(string $class, string $property = ''): mixed + { + if (!isset($this->_classes[$class])) { + return null; + } + $properties = $this->_classes[$class]['property']; + if (!empty($property) && isset($properties[$property])) { + return $properties[$property]; + } + return $properties; + } - /** - * @param string $class - * @param mixed $handler - * @return Loader - */ - public function injectProperty(string $class, object $handler): static - { - $properties = $this->getProperty($class); - if (empty($properties)) { - return $this; - } - foreach ($properties as $property => $attributes) { - foreach ($attributes as $attribute) { - $attribute->execute([$handler, $property]); - } - } - return $this; - } + /** + * @param string $class + * @param mixed $handler + * @return Loader + */ + public function injectProperty(string $class, object $handler): static + { + $properties = $this->getProperty($class); + if (empty($properties)) { + return $this; + } + foreach ($properties as $property => $attributes) { + foreach ($attributes as $attribute) { + $attribute->execute([$handler, $property]); + } + } + return $this; + } - /** - * @param string $class - * @param string $method - * @return mixed - */ - public function getMethod(string $class, string $method = ''): array - { - if (!isset($this->_classes[$class])) { - return []; - } - $properties = $this->_classes[$class]['methods']; - if (!empty($method) && isset($properties[$method])) { - return $properties[$method]; - } - return $properties; - } + /** + * @param string $class + * @param string $method + * @return mixed + */ + public function getMethod(string $class, string $method = ''): array + { + if (!isset($this->_classes[$class])) { + return []; + } + $properties = $this->_classes[$class]['methods']; + if (!empty($method) && isset($properties[$method])) { + return $properties[$method]; + } + return $properties; + } - /** - * @param string $class - * @return array - */ - public function getTarget(string $class): array - { - return $this->_classes[$class] ?? []; - } + /** + * @param string $class + * @return array + */ + public function getTarget(string $class): array + { + return $this->_classes[$class] ?? []; + } - /** - * @param DirectoryIterator $paths - * @param $namespace - * @throws Exception - */ - public function _scanDir(DirectoryIterator $paths, $namespace) - { - $DIRECTORY = $this->createDirectoryMap($paths); - foreach ($paths as $path) { - if ($path->getFilename() === '.' || $path->getFilename() === '..') { - continue; - } - if (str_starts_with($path->getFilename(), '.')) { - continue; - } - if ($path->isDir()) { - $this->_scanDir(new DirectoryIterator($path->getRealPath()), $namespace); - continue; - } + /** + * @param DirectoryIterator $paths + * @param $namespace + * @throws Exception + */ + public function _scanDir(DirectoryIterator $paths, $namespace) + { + /** @var DirectoryIterator $path */ + $DIRECTORY = $this->createDirectoryMap($paths); + foreach ($paths as $path) { + if ($path->isDot()) continue; - if ($path->getExtension() !== 'php') { - continue; - } + if (str_starts_with($path->getFilename(), '.')) { + continue; + } + if ($path->isDir()) { + $this->_scanDir(new DirectoryIterator($path->getRealPath()), $namespace); + continue; + } - if (!in_array($path->getRealPath(), $this->_directoryMap[$DIRECTORY])) { - $this->_directoryMap[$DIRECTORY][] = $path->getRealPath(); - } + if ($path->getExtension() !== 'php') { + continue; + } - try { - $replace = Snowflake::getDi()->getReflect($this->explodeFileName($path, $namespace)); - if (empty($replace) || !$replace->isInstantiable()) { - continue; - } + if (!in_array($path->getRealPath(), $this->_directoryMap[$DIRECTORY])) { + $this->_directoryMap[$DIRECTORY][] = $path->getRealPath(); + } - if (!$replace->getAttributes(Target::class)) { - continue; - } + try { + $replace = Snowflake::getDi()->getReflect($this->explodeFileName($path, $namespace)); + if (empty($replace) || !$replace->isInstantiable()) { + continue; + } - $_array = ['handler' => $replace->newInstanceWithoutConstructor(), 'target' => [], 'methods' => [], 'property' => []]; - foreach ($replace->getAttributes() as $attribute) { - if ($attribute->getName() == Attribute::class) { - continue; - } - $_array['target'][] = $attribute->newInstance(); - } + if (!$replace->getAttributes(Target::class)) { + continue; + } - $methods = $replace->getMethods(ReflectionMethod::IS_PUBLIC); - foreach ($methods as $method) { - $_method = []; - foreach ($method->getAttributes() as $attribute) { - if (!class_exists($attribute->getName())) { - continue; - } - $_method[] = $attribute->newInstance(); - } - $_array['methods'][$method->getName()] = $_method; - } + $_array = ['handler' => $replace->newInstanceWithoutConstructor(), 'target' => [], 'methods' => [], 'property' => []]; + foreach ($replace->getAttributes() as $attribute) { + if ($attribute->getName() == Attribute::class) { + continue; + } + $_array['target'][] = $attribute->newInstance(); + } - $methods = $replace->getProperties(); - foreach ($methods as $method) { - $_property = []; - if ($method->isStatic()) continue; - foreach ($method->getAttributes() as $attribute) { - if (!class_exists($attribute->getName())) { - continue; - } - $property = $attribute->newInstance(); - if ($property instanceof Inject) { - $property->execute([$_array['handler'], $method]); - } else { - $_property[] = $attribute->newInstance(); - } - } - $_array['property'][$method->getName()] = $_property; - } + $methods = $replace->getMethods(ReflectionMethod::IS_PUBLIC); + foreach ($methods as $method) { + $_method = []; + foreach ($method->getAttributes() as $attribute) { + if (!class_exists($attribute->getName())) { + continue; + } + $_method[] = $attribute->newInstance(); + } + $_array['methods'][$method->getName()] = $_method; + } - $this->_fileMap[$replace->getFileName()] = $replace->getName(); + $methods = $replace->getProperties(); + foreach ($methods as $method) { + $_property = []; + if ($method->isStatic()) continue; + foreach ($method->getAttributes() as $attribute) { + if (!class_exists($attribute->getName())) { + continue; + } + $property = $attribute->newInstance(); + if ($property instanceof Inject) { + $property->execute([$_array['handler'], $method]); + } else { + $_property[] = $attribute->newInstance(); + } + } + $_array['property'][$method->getName()] = $_property; + } + + $this->_fileMap[$replace->getFileName()] = $replace->getName(); - $this->_classes[$replace->getName()] = $_array; - } catch (Throwable $throwable) { - $this->error($throwable->getMessage()); - $this->error($throwable->getFile()); - $this->error($throwable->getLine()); - } - } - } + $this->_classes[$replace->getName()] = $_array; + } catch (Throwable $throwable) { + $this->error($throwable->getMessage()); + $this->error($throwable->getFile()); + $this->error($throwable->getLine()); + } + } + } - /** - * @param string $path - */ - public function loadByDirectory(string $path) - { - foreach ($this->_fileMap as $fileName => $className) { - if (!str_starts_with($fileName, $path)) { - continue; - } - if (!isset($this->_classes[$className])) { - continue; - } + /** + * @param string $path + */ + public function loadByDirectory(string $path) + { + foreach ($this->_fileMap as $fileName => $className) { + if (!str_starts_with($fileName, $path)) { + continue; + } + if (!isset($this->_classes[$className])) { + continue; + } - $annotations = $this->_classes[$className]; - if (isset($annotations['target']) && !empty($annotations['target'])) { - foreach ($annotations['target'] as $value) { - $value->execute([$annotations['handler']]); - } - } + $annotations = $this->_classes[$className]; + if (isset($annotations['target']) && !empty($annotations['target'])) { + foreach ($annotations['target'] as $value) { + $value->execute([$annotations['handler']]); + } + } - foreach ($annotations['methods'] as $name => $attribute) { - foreach ($attribute as $value) { - if (!($value instanceof \Annotation\Attribute)) { - continue; - } - $value->execute([$annotations['handler'], $name]); - } - } - } - } + foreach ($annotations['methods'] as $name => $attribute) { + foreach ($attribute as $value) { + if (!($value instanceof \Annotation\Attribute)) { + continue; + } + $value->execute([$annotations['handler'], $name]); + } + } + } + } - /** - * @param DirectoryIterator $path - * @param string $namespace - * @return string - */ - private function explodeFileName(DirectoryIterator $path, string $namespace): string - { - $replace = str_replace(APP_PATH . 'app', '', $path->getRealPath()); + /** + * @param DirectoryIterator $path + * @param string $namespace + * @return string + */ + private function explodeFileName(DirectoryIterator $path, string $namespace): string + { + $replace = str_replace(APP_PATH . 'app', '', $path->getRealPath()); - $replace = str_replace('.php', '', $replace); - $replace = str_replace(DIRECTORY_SEPARATOR, '\\', $replace); - $explode = explode('\\', $replace); - array_shift($explode); + $replace = str_replace('.php', '', $replace); + $replace = str_replace(DIRECTORY_SEPARATOR, '\\', $replace); + $explode = explode('\\', $replace); + array_shift($explode); - return $namespace . '\\' . implode('\\', $explode); - } + return $namespace . '\\' . implode('\\', $explode); + } - /** - * @param DirectoryIterator $directoryIterator - * @return string - */ - public function createDirectoryMap(DirectoryIterator $directoryIterator): string - { - $DIRECTORY = explode(DIRECTORY_SEPARATOR, $directoryIterator->getRealPath()); - array_pop($DIRECTORY); + /** + * @param DirectoryIterator $directoryIterator + * @return string + */ + public function createDirectoryMap(DirectoryIterator $directoryIterator): string + { + $DIRECTORY = explode(DIRECTORY_SEPARATOR, $directoryIterator->getRealPath()); + array_pop($DIRECTORY); - $DIRECTORY = implode(DIRECTORY_SEPARATOR, $DIRECTORY); + $DIRECTORY = implode(DIRECTORY_SEPARATOR, $DIRECTORY); - if (!isset($this->_directoryMap[$DIRECTORY])) { - $this->_directoryMap[$DIRECTORY] = []; - } - return $DIRECTORY; - } + if (!isset($this->_directoryMap[$DIRECTORY])) { + $this->_directoryMap[$DIRECTORY] = []; + } + return $DIRECTORY; + } - /** - * @param string $Directory - * @return array - */ - public function getDirectoryFiles(string $Directory): array - { - if (!isset($this->_directoryMap[$Directory])) { - return []; - } - return $this->_directoryMap[$Directory]; - } + /** + * @param string $Directory + * @return array + */ + public function getDirectoryFiles(string $Directory): array + { + if (!isset($this->_directoryMap[$Directory])) { + return []; + } + return $this->_directoryMap[$Directory]; + } - /** - * @param string $filename - * @return mixed - */ - public function getClassByFilepath(string $filename): mixed - { - if (!isset($this->_fileMap[$filename])) { - return null; - } - return $this->_classes[$this->_fileMap[$filename]]; - } + /** + * @param string $filename + * @return mixed + */ + public function getClassByFilepath(string $filename): mixed + { + if (!isset($this->_fileMap[$filename])) { + return null; + } + return $this->_classes[$this->_fileMap[$filename]]; + } } diff --git a/HttpServer/Abstracts/Callback.php b/HttpServer/Abstracts/Callback.php index b8146c86..eedd8b1c 100644 --- a/HttpServer/Abstracts/Callback.php +++ b/HttpServer/Abstracts/Callback.php @@ -36,8 +36,6 @@ abstract class Callback extends HttpService protected function clear(Server $server, $worker_id, $message) { try { - Snowflake::clearProcessId($server->worker_pid); - /** @var Process $logger */ $logger = Snowflake::app()->get(LoggerProcess::class); $logger->write(Json::encode([$this->_MESSAGE[$message] . $worker_id, 'app'])); diff --git a/HttpServer/Command.php b/HttpServer/Command.php index 98837e14..9029225a 100644 --- a/HttpServer/Command.php +++ b/HttpServer/Command.php @@ -5,12 +5,8 @@ namespace HttpServer; use Exception; -use ReflectionException; use Snowflake\Abstracts\Input; -use Snowflake\Event; -use Snowflake\Exception\ComponentException; use Snowflake\Exception\ConfigException; -use Snowflake\Exception\NotFindPropertyException; use Snowflake\Snowflake; /** @@ -20,40 +16,41 @@ use Snowflake\Snowflake; class Command extends \Console\Command { - public string $command = 'sw:server'; + public string $command = 'sw:server'; - public string $description = 'server start|stop|reload|restart'; + public string $description = 'server start|stop|reload|restart'; - const ACTIONS = ['start', 'stop', 'restart']; + const ACTIONS = ['start', 'stop', 'restart']; - /** - * @param Input $dtl - * @return string - * @throws Exception - * @throws ConfigException - */ - public function onHandler(Input $dtl): string - { - $manager = Snowflake::app()->getServer(); - $manager->setDaemon($dtl->get('daemon', 0)); + /** + * @param Input $dtl + * @return string + * @throws Exception + * @throws ConfigException + */ + public function onHandler(Input $dtl): string + { + $manager = Snowflake::app()->getServer(); + $manager->setDaemon($dtl->get('daemon', 0)); - if (!in_array($dtl->get('action'), self::ACTIONS)) { - return 'I don\'t know what I want to do.'; - } + if (!in_array($dtl->get('action'), self::ACTIONS)) { + return 'I don\'t know what I want to do.'; + } - if ($manager->isRunner() && $dtl->get('action') == 'start') { - return 'Service is running. Please use restart.'; - } + /** @var Shutdown $shutdown */ + $shutdown = Snowflake::app()->get('shutdown'); + if ($shutdown->isRunning() && $dtl->get('action') == 'start') { + return 'Service is running. Please use restart.'; + } - $manager->shutdown(); - if ($dtl->get('action') == 'stop') { - return 'shutdown success.'; - } - - return $manager->start(); - } + $shutdown->shutdown(); + if ($dtl->get('action') == 'stop') { + return 'shutdown success.'; + } + return $manager->start(); + } } diff --git a/HttpServer/Events/OnBeforeReload.php b/HttpServer/Events/OnBeforeReload.php index e8ef6a02..1a9d9183 100644 --- a/HttpServer/Events/OnBeforeReload.php +++ b/HttpServer/Events/OnBeforeReload.php @@ -26,6 +26,9 @@ class OnBeforeReload extends Callback { $event = Snowflake::app()->getEvent(); $event->trigger(Event::SERVER_BEFORE_RELOAD, [$server]); + + Snowflake::clearWorkerPid(); + Snowflake::clearTaskPid(); } } diff --git a/HttpServer/Events/OnWorkerStart.php b/HttpServer/Events/OnWorkerStart.php index d9d108ea..55ad3912 100644 --- a/HttpServer/Events/OnWorkerStart.php +++ b/HttpServer/Events/OnWorkerStart.php @@ -69,7 +69,9 @@ class OnWorkerStart extends Callback { putenv('environmental=' . Snowflake::TASK); - fire(Event::SERVER_TASK_START); + Snowflake::setTaskId($server->worker_pid); + + fire(Event::SERVER_TASK_START); } diff --git a/HttpServer/Shutdown.php b/HttpServer/Shutdown.php new file mode 100644 index 00000000..d9ea16b0 --- /dev/null +++ b/HttpServer/Shutdown.php @@ -0,0 +1,123 @@ +taskDirectory = storage(null, 'pid/task'); + $this->workerDirectory = storage(null, 'pid/worker'); + $this->managerDirectory = storage(null, 'pid/manager'); + $this->processDirectory = storage(null, 'pid/process'); + } + + + /** + * @throws Exception + */ + public function shutdown(): void + { + $master_pid = Server()->setting['pid_file'] ?? PID_PATH; + clearstatcache($master_pid); + if (file_exists($master_pid)) { + $this->close($master_pid); + } + $this->closeOther(); + } + + + /** + * 关闭其他进程 + */ + private function closeOther(): void + { + $this->directoryCheck($this->managerDirectory); + $this->directoryCheck($this->taskDirectory); + $this->directoryCheck($this->workerDirectory); + $this->directoryCheck($this->processDirectory); + } + + + /** + * @return bool + * @throws Exception + * check server is running. + */ + public function isRunning() + { + $master_pid = Server()->setting['pid_file'] ?? PID_PATH; + + return $this->pidIsExists($master_pid); + } + + + /** + * @param $content + * @return bool + */ + public function pidIsExists($content): bool + { + $content = shell_exec('ps -eo pid,cmd,state | grep ' . $content . ' | grep -v grep'); + if (empty($content)) { + return false; + } + return true; + } + + + /** + * @param string $path + */ + public function directoryCheck(string $path) + { + $dir = new \DirectoryIterator($path); + if ($dir->getSize() < 1) { + return true; + } + foreach ($dir as $value) { + /** @var \DirectoryIterator $value */ + if (!$value->valid()) continue; + + $this->close($value->getRealPath()); + } + return false; + } + + + /** + * @param string $value + */ + public function close(string $value) + { + $resource = fopen($value, 'r'); + $content = fgets($resource); + fclose($resource); + + while ($this->pidIsExists($content)) { + exec('kill -15 ' . $content); + sleep(1); + } + @unlink($value); + } + + +} diff --git a/System/Abstracts/BaseApplication.php b/System/Abstracts/BaseApplication.php index 36042264..c1c11c6e 100644 --- a/System/Abstracts/BaseApplication.php +++ b/System/Abstracts/BaseApplication.php @@ -22,6 +22,7 @@ use HttpServer\Service\Http; use HttpServer\Service\Packet; use HttpServer\Service\Receive; use HttpServer\Service\Websocket; +use HttpServer\Shutdown; use JetBrains\PhpStorm\Pure; use Kafka\Producer; use Annotation\Annotation as SAnnotation; @@ -51,437 +52,438 @@ use Swoole\Table; abstract class BaseApplication extends Service { - use TraitApplication; - - - /** - * @var string - */ - public string $storage = APP_PATH . 'storage'; - - public string $envPath = APP_PATH . '.env'; - - /** - * Init constructor. - * - * @param array $config - * - * @throws - */ - public function __construct(array $config = []) - { - Snowflake::init($this); - - $this->moreComponents(); - $this->parseInt($config); - $this->parseEvents($config); - $this->initErrorHandler(); - $this->enableEnvConfig(); - - parent::__construct($config); - } - - - /** - * @return array - */ - public function enableEnvConfig(): array - { - 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(string $filePath): array - { - // 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(string $line): bool - { - $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 - */ - #[Pure] protected function looksLikeSetter(string $line): bool - { - return str_contains($line, '='); - } - - - /** - * @param $config - * - * @throws - */ - public function parseInt($config) - { - foreach ($config as $key => $value) { - Config::set($key, $value); - } - if ($storage = Config::get('storage', false, 'storage')) { - if (!str_contains($storage, APP_PATH)) { - $storage = APP_PATH . $storage . '/'; - } - if (!is_dir($storage)) { - mkdir($storage); - } - if (!is_dir($storage) || !is_writeable($storage)) { - throw new InitException("Directory {$storage} does not have write permission"); - } - } - } - - - /** - * @param $config - * - * @throws - */ - public function parseEvents($config) - { - if (!isset($config['events']) || !is_array($config['events'])) { - return; - } - $event = Snowflake::app()->getEvent(); - foreach ($config['events'] as $key => $value) { - if (is_string($value)) { - $value = Snowflake::createObject($value); - } - if (is_array($value) && isset($value[0]) && !($value[0] instanceof \Closure)) { - if (!is_callable($value, true)) { - throw new InitException("Class does not hav callback."); - } - $event->on($key, $value); - } else { - foreach ($value as $item) { - if (!is_callable($item, true)) { - throw new InitException("Class does not hav callback."); - } - $event->on($key, $item); - } - } - } - } - - - /** - * @param $name - * @return mixed - * @throws Exception - */ - public function clone($name): mixed - { - return clone $this->get($name); - } - - /** - * - * @throws Exception - */ - public function initErrorHandler() - { - $this->get('error')->register(); - } - - /** - * @return mixed - */ - public function getLocalIps(): mixed - { - return swoole_get_local_ip(); - } - - /** - * @return mixed - */ - public function getFirstLocal(): mixed - { - return current($this->getLocalIps()); - } - - - /** - * @return Logger - * @throws Exception - */ - public function getLogger(): Logger - { - return $this->get('logger'); - } - - - /** - * @return Producer - * @throws Exception - */ - public function getKafka(): Producer - { - return $this->get('kafka'); - } - - - /** - * @return \Redis|Redis - * @throws Exception - */ - public function getRedis(): Redis|\Redis - { - return $this->get('redis'); - } - - /** - * @param $ip - * @return bool - */ - public function isLocal($ip): bool - { - return $this->getFirstLocal() == $ip; - } - - - /** - * @return ErrorHandler - * @throws Exception - */ - public function getError(): ErrorHandler - { - return $this->get('error'); - } - - - /** - * @return Connection - * @throws ComponentException - * @throws NotFindClassException - * @throws ReflectionException - */ - public function getMysqlFromPool(): Connection - { - return $this->get('pool')->getDb(); - } - - - /** - * @return SRedis - * @throws NotFindClassException - * @throws ReflectionException - * @throws ComponentException - */ - public function getRedisFromPool(): SRedis - { - return $this->get('pool')->getRedis(); - } - - - /** - * @return Response - * @throws Exception - */ - public function getResponse(): Response - { - return $this->get('response'); - } - - /** - * @return Request - * @throws Exception - */ - public function getRequest(): Request - { - return $this->get('request'); - } - - - /** - * @param $name - * @return Table - * @throws Exception - */ - public function getTable($name): Table - { - return $this->get($name); - } - - - /** - * @return Config - * @throws Exception - */ - public function getConfig(): Config - { - return $this->get('config'); - } - - - /** - * @return Router - * @throws Exception - */ - public function getRouter(): Router - { - return $this->get('router'); - } - - - /** - * @return Event - * @throws Exception - */ - public function getEvent(): Event - { - return $this->get('event'); - } - - - /** - * @return Jwt - * @throws Exception - */ - public function getJwt(): Jwt - { - return $this->get('jwt'); - } - - - /** - * @return Server - * @throws Exception - */ - public function getServer(): Server - { - return $this->get('server'); - } - - - /** - * @return Http|Packet|Receive|Websocket|null - * @throws Exception - */ - public function getSwoole(): Packet|Websocket|Receive|Http|null - { - return $this->getServer()->getServer(); - } - - - /** - * @return SAnnotation - * @throws Exception - */ - public function getAttributes(): SAnnotation - { - return $this->get('attributes'); - } - - - /** - * @return Async - * @throws Exception - */ - public function getAsync(): Async - { - return $this->get('async'); - } - - - /** - * @return ObjectPool - * @throws Exception - */ - public function getObject(): ObjectPool - { - return $this->get('object'); - } - - - /** - * @return \Rpc\Producer - * @throws ComponentException - * @throws NotFindClassException - * @throws ReflectionException - */ - public function getRpc(): \Rpc\Producer - { - return $this->get('rpc'); - } - - - /** - * @return Channel - * @throws ComponentException - * @throws NotFindClassException - * @throws ReflectionException - */ - public function getChannel(): Channel - { - return $this->get('channel'); - } - - - /** - * @throws Exception - */ - protected function moreComponents(): void - { - $this->setComponents([ - 'error' => ['class' => ErrorHandler::class], - 'event' => ['class' => Event::class], - 'connections' => ['class' => Connection::class], - 'redis_connections' => ['class' => SRedis::class], - 'pool' => ['class' => SPool::class], - 'response' => ['class' => Response::class], - 'request' => ['class' => Request::class], - 'config' => ['class' => Config::class], - 'logger' => ['class' => Logger::class], - 'attributes' => ['class' => SAnnotation::class], - 'router' => ['class' => Router::class], - 'redis' => ['class' => Redis::class], - 'jwt' => ['class' => Jwt::class], - 'async' => ['class' => Async::class], - 'filter' => ['class' => HttpFilter::class], - 'object' => ['class' => ObjectPool::class], - 'goto' => ['class' => BaseGoto::class], - 'channel' => ['class' => Channel::class], - 'rpc' => ['class' => \Rpc\Producer::class], - 'rpc-service' => ['class' => \Rpc\Service::class], - 'http2' => ['class' => Http2::class], - ]); - } + use TraitApplication; + + + /** + * @var string + */ + public string $storage = APP_PATH . 'storage'; + + public string $envPath = APP_PATH . '.env'; + + /** + * Init constructor. + * + * @param array $config + * + * @throws + */ + public function __construct(array $config = []) + { + Snowflake::init($this); + + $this->moreComponents(); + $this->parseInt($config); + $this->parseEvents($config); + $this->initErrorHandler(); + $this->enableEnvConfig(); + + parent::__construct($config); + } + + + /** + * @return array + */ + public function enableEnvConfig(): array + { + 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(string $filePath): array + { + // 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(string $line): bool + { + $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 + */ + #[Pure] protected function looksLikeSetter(string $line): bool + { + return str_contains($line, '='); + } + + + /** + * @param $config + * + * @throws + */ + public function parseInt($config) + { + foreach ($config as $key => $value) { + Config::set($key, $value); + } + if ($storage = Config::get('storage', false, 'storage')) { + if (!str_contains($storage, APP_PATH)) { + $storage = APP_PATH . $storage . '/'; + } + if (!is_dir($storage)) { + mkdir($storage); + } + if (!is_dir($storage) || !is_writeable($storage)) { + throw new InitException("Directory {$storage} does not have write permission"); + } + } + } + + + /** + * @param $config + * + * @throws + */ + public function parseEvents($config) + { + if (!isset($config['events']) || !is_array($config['events'])) { + return; + } + $event = Snowflake::app()->getEvent(); + foreach ($config['events'] as $key => $value) { + if (is_string($value)) { + $value = Snowflake::createObject($value); + } + if (is_array($value) && isset($value[0]) && !($value[0] instanceof \Closure)) { + if (!is_callable($value, true)) { + throw new InitException("Class does not hav callback."); + } + $event->on($key, $value); + } else { + foreach ($value as $item) { + if (!is_callable($item, true)) { + throw new InitException("Class does not hav callback."); + } + $event->on($key, $item); + } + } + } + } + + + /** + * @param $name + * @return mixed + * @throws Exception + */ + public function clone($name): mixed + { + return clone $this->get($name); + } + + /** + * + * @throws Exception + */ + public function initErrorHandler() + { + $this->get('error')->register(); + } + + /** + * @return mixed + */ + public function getLocalIps(): mixed + { + return swoole_get_local_ip(); + } + + /** + * @return mixed + */ + public function getFirstLocal(): mixed + { + return current($this->getLocalIps()); + } + + + /** + * @return Logger + * @throws Exception + */ + public function getLogger(): Logger + { + return $this->get('logger'); + } + + + /** + * @return Producer + * @throws Exception + */ + public function getKafka(): Producer + { + return $this->get('kafka'); + } + + + /** + * @return \Redis|Redis + * @throws Exception + */ + public function getRedis(): Redis|\Redis + { + return $this->get('redis'); + } + + /** + * @param $ip + * @return bool + */ + public function isLocal($ip): bool + { + return $this->getFirstLocal() == $ip; + } + + + /** + * @return ErrorHandler + * @throws Exception + */ + public function getError(): ErrorHandler + { + return $this->get('error'); + } + + + /** + * @return Connection + * @throws ComponentException + * @throws NotFindClassException + * @throws ReflectionException + */ + public function getMysqlFromPool(): Connection + { + return $this->get('pool')->getDb(); + } + + + /** + * @return SRedis + * @throws NotFindClassException + * @throws ReflectionException + * @throws ComponentException + */ + public function getRedisFromPool(): SRedis + { + return $this->get('pool')->getRedis(); + } + + + /** + * @return Response + * @throws Exception + */ + public function getResponse(): Response + { + return $this->get('response'); + } + + /** + * @return Request + * @throws Exception + */ + public function getRequest(): Request + { + return $this->get('request'); + } + + + /** + * @param $name + * @return Table + * @throws Exception + */ + public function getTable($name): Table + { + return $this->get($name); + } + + + /** + * @return Config + * @throws Exception + */ + public function getConfig(): Config + { + return $this->get('config'); + } + + + /** + * @return Router + * @throws Exception + */ + public function getRouter(): Router + { + return $this->get('router'); + } + + + /** + * @return Event + * @throws Exception + */ + public function getEvent(): Event + { + return $this->get('event'); + } + + + /** + * @return Jwt + * @throws Exception + */ + public function getJwt(): Jwt + { + return $this->get('jwt'); + } + + + /** + * @return Server + * @throws Exception + */ + public function getServer(): Server + { + return $this->get('server'); + } + + + /** + * @return Http|Packet|Receive|Websocket|null + * @throws Exception + */ + public function getSwoole(): Packet|Websocket|Receive|Http|null + { + return $this->getServer()->getServer(); + } + + + /** + * @return SAnnotation + * @throws Exception + */ + public function getAttributes(): SAnnotation + { + return $this->get('attributes'); + } + + + /** + * @return Async + * @throws Exception + */ + public function getAsync(): Async + { + return $this->get('async'); + } + + + /** + * @return ObjectPool + * @throws Exception + */ + public function getObject(): ObjectPool + { + return $this->get('object'); + } + + + /** + * @return \Rpc\Producer + * @throws ComponentException + * @throws NotFindClassException + * @throws ReflectionException + */ + public function getRpc(): \Rpc\Producer + { + return $this->get('rpc'); + } + + + /** + * @return Channel + * @throws ComponentException + * @throws NotFindClassException + * @throws ReflectionException + */ + public function getChannel(): Channel + { + return $this->get('channel'); + } + + + /** + * @throws Exception + */ + protected function moreComponents(): void + { + $this->setComponents([ + 'error' => ['class' => ErrorHandler::class], + 'event' => ['class' => Event::class], + 'connections' => ['class' => Connection::class], + 'redis_connections' => ['class' => SRedis::class], + 'pool' => ['class' => SPool::class], + 'response' => ['class' => Response::class], + 'request' => ['class' => Request::class], + 'config' => ['class' => Config::class], + 'logger' => ['class' => Logger::class], + 'attributes' => ['class' => SAnnotation::class], + 'router' => ['class' => Router::class], + 'redis' => ['class' => Redis::class], + 'jwt' => ['class' => Jwt::class], + 'async' => ['class' => Async::class], + 'filter' => ['class' => HttpFilter::class], + 'object' => ['class' => ObjectPool::class], + 'goto' => ['class' => BaseGoto::class], + 'channel' => ['class' => Channel::class], + 'rpc' => ['class' => \Rpc\Producer::class], + 'rpc-service' => ['class' => \Rpc\Service::class], + 'http2' => ['class' => Http2::class], + 'shutdown' => ['class' => Shutdown::class], + ]); + } } diff --git a/System/Abstracts/TraitApplication.php b/System/Abstracts/TraitApplication.php index 7c0ffb46..d6a1feba 100644 --- a/System/Abstracts/TraitApplication.php +++ b/System/Abstracts/TraitApplication.php @@ -14,6 +14,7 @@ use HttpServer\Http\Response; use HttpServer\HttpFilter; use HttpServer\Route\Router; use HttpServer\Server; +use HttpServer\Shutdown; use Kafka\Producer; use Snowflake\Async; use Snowflake\Cache\Redis; @@ -50,6 +51,7 @@ use Rpc\Producer as RPCProducer; * @property HttpFilter $filter * @property RPCProducer $rpc * @property Channel $channel + * @property Shutdown $shutdown */ trait TraitApplication { diff --git a/System/Process/Process.php b/System/Process/Process.php index 068a7b16..cf29d76a 100644 --- a/System/Process/Process.php +++ b/System/Process/Process.php @@ -34,7 +34,7 @@ abstract class Process extends \Swoole\Process implements SProcess { parent::__construct([$this, '_load'], false, 1, $enable_coroutine); $this->application = $application; - Snowflake::setWorkerId($this->pid); + Snowflake::setProcessId($this->pid); } /** diff --git a/System/Snowflake.php b/System/Snowflake.php index 05a3950e..49d2968d 100644 --- a/System/Snowflake.php +++ b/System/Snowflake.php @@ -37,432 +37,490 @@ defined('SOCKET_PATH') or define('SOCKET_PATH', APP_PATH . 'app/Websocket/'); class Snowflake { - /** @var Container */ - public static Container $container; - - - /** @var ?Application */ - private static ?Application $service = null; - - - /** - * @param $service - * - * 初始化服务 - */ - public static function init($service) - { - static::$service = $service; - } - - /** - * @return Application|null - */ - public static function app(): ?Application - { - return static::$service; - } - - /** - * @param $name - * @return bool - */ - public static function has($name): bool - { - return static::$service->has($name); - } - - - /** - * @param $className - * @param $id - */ - public static function setAlias($className, $id) - { - static::$service->setAlias($className, $id); - } - - - /** - * @param $port - * @return bool|array - * @throws Exception - */ - public static function port_already($port): bool - { - if (empty($port)) { - return false; - } - if (Snowflake::getPlatform()->isLinux()) { - exec('netstat -tunlp | grep ' . $port, $output); - } else { - exec('lsof -i :' . $port . ' | grep -i "LISTEN"', $output); - } - return !empty($output); - } - - - /** - * @param $service - * @return string - */ - #[Pure] public static function listen($service): string - { - return sprintf('Check listen %s::%d -> ok', $service['host'], $service['port']); - } - - - /** - * @param $className - * @param array $construct - * @return mixed - * @throws NotFindClassException - * @throws ReflectionException - * @throws Exception - */ - public static function createObject($className, $construct = []): mixed - { - if (is_object($className)) { - return $className; - } - 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(): string - { - $default = APP_PATH . 'storage' . DIRECTORY_SEPARATOR; - $path = Config::get('storage', false, $default); - if (!is_dir($path)) { - mkdir($path); - } - return $path; - } - - - /** - * @return bool - */ - public static function inCoroutine(): bool - { - return Coroutine::getCid() > 0; - } - - - /** - * @return Container - */ - public static function getDi(): Container - { - return static::$container; - } - - - /** - * @param $workerId - * @return mixed - * @throws Exception - */ - public static function setProcessId($workerId): mixed - { - return self::writeFile(storage('socket.sock'), $workerId); - } - - - /** - * @param $workerId - * @return mixed - * @throws Exception - */ - public static function setWorkerId($workerId): mixed - { - if (empty($workerId)) { - return $workerId; - } - return self::writeFile(storage($workerId . '.sock', 'worker'), $workerId); - } - - - /** - * @param $fileName - * @param $content - * @param null $is_append - * @return mixed - */ - public static function writeFile($fileName, $content, $is_append = null): mixed - { - $params = [$fileName, (string)$content]; - if ($is_append !== null) { - $params[] = $is_append; - } - return !self::inCoroutine() ? file_put_contents(...$params) : Coroutine::writeFile(...$params); - } - - - /** - * @param $object - * @param $config - * @return mixed - */ - public static function configure($object, $config): mixed - { - foreach ($config as $key => $value) { - if (!property_exists($object, $key)) { - continue; - } - $object->$key = $value; - } - return $object; - } - - - /** - * @param $workerId - * @throws Exception - */ - public static function clearProcessId($workerId) - { - @unlink(storage($workerId . '.sock', 'worker')); - } - - - /** - * @return Server|null - * @throws - */ - public static function getWebSocket(): ?Server - { - $server = static::app()->getSwoole(); - if (!($server instanceof Server)) { - return null; - } - return $server; - } - - - /** - * @return false|string - * @throws Exception - */ - public static function getMasterPid(): bool|string - { - $pid = Snowflake::app()->getSwoole()->setting['pid_file']; - - return file_get_contents($pid); - } - - - /** - * @param int $fd - * @param $data - * @return mixed - * @throws Exception - */ - public static function push(int $fd, $data): mixed - { - $server = static::getWebSocket(); - if (empty($server)) { - return false; - } - if (!is_string($data)) { - $data = Json::encode($data); - } - return $server->push($fd, $data); - } - - - /** - * @return mixed - */ - public static function localhost(): mixed - { - return current(swoole_get_local_ip()); - } - - - /** - * @param string $class - * @param array $params - * @throws NotFindClassException - * @throws ReflectionException - * @throws Exception - */ - public static function async(string $class, array $params = []) - { - $server = static::app()->getSwoole(); - if (!isset($server->setting['task_worker_num']) || !class_exists($class)) { - return; - } - - /** @var Task $class */ - $class = static::createObject($class); - $class->setParams($params); - - $server->task(swoole_serialize($class)); - } - - - /** - * @param $v1 - * @param $v2 - * @return float - */ - #[Pure] public static function distance(array $v1, array $v2): float - { - $maxX = max($v1['x'], $v2['x']); - $minX = min($v1['x'], $v2['x']); - - $maxZ = max($v1['z'], $v2['z']); - $minZ = min($v1['z'], $v2['z']); - - $dx = abs($maxX - $minX); - $dy = abs($maxZ - $minZ); - - $sqrt = sqrt($dx * $dx + $dy * $dy); - if ($sqrt < 0) { - $sqrt = abs($sqrt); - } - return (float)$sqrt; - } - - - /** - * @param $process - * @throws Exception - */ - public static function shutdown($process): void - { - static::app()->getSwoole()->shutdown(); - if ($process instanceof Process) { - $process->exit(0); - } - } - - - /** - * @param $tmp - * @return string - */ - public static function rename($tmp): string - { - $hash = md5_file($tmp['tmp_name']); - - $later = '.' . exif_imagetype($tmp['tmp_name']); - - $match = '/(\w{12})(\w{5})(\w{9})(\w{6})/'; - $tmp = preg_replace($match, '$1-$2-$3-$4', $hash); - - return strtoupper($tmp) . $later; - } - - - /** - * @return Environmental - * @throws Exception - */ - public static function getPlatform(): Environmental - { - return Snowflake::createObject(Environmental::class); - } - - - /** - * @return mixed - * @throws Exception - */ - public static function reload(): mixed - { - return Snowflake::app()->getSwoole()->reload(); - } - - - private static array $_autoload = []; - - - const PROCESS = 'process'; - const TASK = 'task'; - const WORKER = 'worker'; - - - /** - * @return string|null - */ - #[Pure] public static function getEnvironmental(): ?string - { - return env('environmental'); - } - - - /** - * @return bool - */ - #[Pure] public static function isTask(): bool - { - return static::getEnvironmental() == static::TASK; - } - - - /** - * @return bool - */ - #[Pure] public static function isWorker(): bool - { - return static::getEnvironmental() == static::WORKER; - } - - - /** - * @return bool - */ - #[Pure] public static function isProcess(): bool - { - return static::getEnvironmental() == static::PROCESS; - } - - - /** - * @param $class - * @param $file - */ - public static function setAutoload($class, $file) - { - if (isset(static::$_autoload[$class])) { - return; - } - static::$_autoload[$class] = $file; - include_once "$file"; - } - - - /** - * @param $className - */ - public static function autoload($className) - { - if (!isset(static::$_autoload[$className])) { - return; - } - $file = static::$_autoload[$className]; - require_once "$file"; - } + /** @var Container */ + public static Container $container; + + + /** @var ?Application */ + private static ?Application $service = null; + + + /** + * @param $service + * + * 初始化服务 + */ + public static function init($service) + { + static::$service = $service; + } + + /** + * @return Application|null + */ + public static function app(): ?Application + { + return static::$service; + } + + /** + * @param $name + * @return bool + */ + public static function has($name): bool + { + return static::$service->has($name); + } + + + /** + * @param $className + * @param $id + */ + public static function setAlias($className, $id) + { + static::$service->setAlias($className, $id); + } + + + /** + * @param $port + * @return bool|array + * @throws Exception + */ + public static function port_already($port): bool + { + if (empty($port)) { + return false; + } + if (Snowflake::getPlatform()->isLinux()) { + exec('netstat -tunlp | grep ' . $port, $output); + } else { + exec('lsof -i :' . $port . ' | grep -i "LISTEN"', $output); + } + return !empty($output); + } + + + /** + * @param $service + * @return string + */ + #[Pure] public static function listen($service): string + { + return sprintf('Check listen %s::%d -> ok', $service['host'], $service['port']); + } + + + /** + * @param $className + * @param array $construct + * @return mixed + * @throws NotFindClassException + * @throws ReflectionException + * @throws Exception + */ + public static function createObject($className, $construct = []): mixed + { + if (is_object($className)) { + return $className; + } + 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(): string + { + $default = APP_PATH . 'storage' . DIRECTORY_SEPARATOR; + $path = Config::get('storage', false, $default); + if (!is_dir($path)) { + mkdir($path, 0777, true); + } + return $path; + } + + + /** + * @return bool + */ + public static function inCoroutine(): bool + { + return Coroutine::getCid() > 0; + } + + + /** + * @return Container + */ + public static function getDi(): Container + { + return static::$container; + } + + + /** + * @param $workerId + * @return mixed + * @throws Exception + */ + public static function setManagerId($workerId): mixed + { + return self::writeFile(storage($workerId . '.sock', 'pid/manager'), $workerId); + } + + + /** + * @param $workerId + * @return mixed + * @throws Exception + */ + public static function setProcessId($workerId): mixed + { + return self::writeFile(storage($workerId . '.sock', 'pid/process'), $workerId); + } + + + /** + * @param $workerId + * @return mixed + * @throws Exception + */ + public static function setWorkerId($workerId): mixed + { + if (empty($workerId)) { + return $workerId; + } + return self::writeFile(storage($workerId . '.sock', 'pid/worker'), $workerId); + } + + + /** + * @param $workerId + * @return mixed + * @throws Exception + */ + public static function setTaskId($workerId): mixed + { + if (empty($workerId)) { + return $workerId; + } + return self::writeFile(storage($workerId . '.sock', 'pid/task'), $workerId); + } + + /** + * @param $fileName + * @param $content + * @param null $is_append + * @return mixed + */ + public static function writeFile($fileName, $content, $is_append = null): mixed + { + $params = [$fileName, (string)$content]; + if ($is_append !== null) { + $params[] = $is_append; + } + return !self::inCoroutine() ? file_put_contents(...$params) : Coroutine::writeFile(...$params); + } + + + /** + * @param $object + * @param $config + * @return mixed + */ + public static function configure($object, $config): mixed + { + foreach ($config as $key => $value) { + if (!property_exists($object, $key)) { + continue; + } + $object->$key = $value; + } + return $object; + } + + + /** + * @param $workerId + * @param bool $isWorker + * @throws Exception + */ + public static function clearProcessId($workerId, $isWorker = false) + { + clearstatcache(); + $directory = $isWorker === true ? 'pid/worker' : 'pid/task'; + if (!file_exists($file = storage($workerId, $directory))) { + return; + } + shell_exec('rm -rf ' . $file); + } + + + /** + * @param string|null $taskPid + * @throws Exception + */ + public static function clearTaskPid(string $taskPid = null) + { + if (empty($taskPid)) { + exec('rm -rf ' . storage(null, 'pid/task')); + } else { + static::clearProcessId($taskPid); + } + } + + + /** + * @param $taskPid + * @throws Exception + */ + public static function clearWorkerPid($taskPid = null) + { + if (empty($taskPid)) { + exec('rm -rf ' . storage(null, 'pid/worker')); + } else { + static::clearProcessId($taskPid, true); + } + } + + + /** + * @return Server|null + * @throws + */ + public static function getWebSocket(): ?Server + { + $server = static::app()->getSwoole(); + if (!($server instanceof Server)) { + return null; + } + return $server; + } + + + /** + * @return false|string + * @throws Exception + */ + public static function getMasterPid(): bool|string + { + $pid = Snowflake::app()->getSwoole()->setting['pid_file']; + + return file_get_contents($pid); + } + + + /** + * @param int $fd + * @param $data + * @return mixed + * @throws Exception + */ + public static function push(int $fd, $data): mixed + { + $server = static::getWebSocket(); + if (empty($server)) { + return false; + } + if (!is_string($data)) { + $data = Json::encode($data); + } + return $server->push($fd, $data); + } + + + /** + * @return mixed + */ + public static function localhost(): mixed + { + return current(swoole_get_local_ip()); + } + + + /** + * @param string $class + * @param array $params + * @throws NotFindClassException + * @throws ReflectionException + * @throws Exception + */ + public static function async(string $class, array $params = []) + { + $server = static::app()->getSwoole(); + if (!isset($server->setting['task_worker_num']) || !class_exists($class)) { + return; + } + + /** @var Task $class */ + $class = static::createObject($class); + $class->setParams($params); + + $server->task(swoole_serialize($class)); + } + + + /** + * @param $v1 + * @param $v2 + * @return float + */ + #[Pure] public static function distance(array $v1, array $v2): float + { + $maxX = max($v1['x'], $v2['x']); + $minX = min($v1['x'], $v2['x']); + + $maxZ = max($v1['z'], $v2['z']); + $minZ = min($v1['z'], $v2['z']); + + $dx = abs($maxX - $minX); + $dy = abs($maxZ - $minZ); + + $sqrt = sqrt($dx * $dx + $dy * $dy); + if ($sqrt < 0) { + $sqrt = abs($sqrt); + } + return (float)$sqrt; + } + + + /** + * @param $process + * @throws Exception + */ + public static function shutdown($process): void + { + static::app()->getSwoole()->shutdown(); + if ($process instanceof Process) { + $process->exit(0); + } + } + + + /** + * @param $tmp + * @return string + */ + public static function rename($tmp): string + { + $hash = md5_file($tmp['tmp_name']); + + $later = '.' . exif_imagetype($tmp['tmp_name']); + + $match = '/(\w{12})(\w{5})(\w{9})(\w{6})/'; + $tmp = preg_replace($match, '$1-$2-$3-$4', $hash); + + return strtoupper($tmp) . $later; + } + + + /** + * @return Environmental + * @throws Exception + */ + public static function getPlatform(): Environmental + { + return Snowflake::createObject(Environmental::class); + } + + + /** + * @return mixed + * @throws Exception + */ + public static function reload(): mixed + { + return Snowflake::app()->getSwoole()->reload(); + } + + + private static array $_autoload = []; + + + const PROCESS = 'process'; + const TASK = 'task'; + const WORKER = 'worker'; + + + /** + * @return string|null + */ + #[Pure] public static function getEnvironmental(): ?string + { + return env('environmental'); + } + + + /** + * @return bool + */ + #[Pure] public static function isTask(): bool + { + return static::getEnvironmental() == static::TASK; + } + + + /** + * @return bool + */ + #[Pure] public static function isWorker(): bool + { + return static::getEnvironmental() == static::WORKER; + } + + + /** + * @return bool + */ + #[Pure] public static function isProcess(): bool + { + return static::getEnvironmental() == static::PROCESS; + } + + + /** + * @param $class + * @param $file + */ + public static function setAutoload($class, $file) + { + if (isset(static::$_autoload[$class])) { + return; + } + static::$_autoload[$class] = $file; + include_once "$file"; + } + + + /** + * @param $className + */ + public static function autoload($className) + { + if (!isset(static::$_autoload[$className])) { + return; + } + $file = static::$_autoload[$className]; + require_once "$file"; + } } diff --git a/function.php b/function.php index 71f10380..9bcaa78c 100644 --- a/function.php +++ b/function.php @@ -9,6 +9,10 @@ use HttpServer\Http\HttpParams; use HttpServer\Http\Request; use HttpServer\Http\Response; use HttpServer\Route\Router; +use HttpServer\Service\Http; +use HttpServer\Service\Packet; +use HttpServer\Service\Receive; +use HttpServer\Service\Websocket; use JetBrains\PhpStorm\Pure; use Snowflake\Abstracts\Config; use Snowflake\Error\Logger; @@ -467,6 +471,20 @@ if (!function_exists('Input')) { } +if (!function_exists('Server')) { + + + /** + * @return Http|Packet|Receive|Websocket|null + * @throws Exception + */ + function Server(): Http|Packet|Receive|Websocket|null + { + return Snowflake::app()->getSwoole(); + } + +} + if (!function_exists('storage')) { /** @@ -477,14 +495,18 @@ if (!function_exists('storage')) { */ function storage($fileName = '', $path = ''): string { - $basePath = Snowflake::getStoragePath(); - if (empty($path)) { - $fileName = rtrim($basePath, '/') . '/' . $fileName; - } else if (empty($fileName)) { - return rtrim(initDir($basePath, $path)); - } else { - $fileName = rtrim(initDir($basePath, $path)) . '/' . $fileName; + + $basePath = rtrim(Snowflake::getStoragePath(), '/'); + if (!empty($path)) { + $path = ltrim($path, '/'); + if (!is_dir($basePath . '/' . $path)) { + mkdir($basePath . '/' . $path, 0777, true); + } } + if (empty($fileName)) { + return $basePath . '/' . $path . '/'; + } + $fileName = $basePath . '/' . $path . '/' . $fileName; if (!file_exists($fileName)) { touch($fileName); } @@ -498,20 +520,9 @@ if (!function_exists('storage')) { * @return false|string * @throws Exception */ - function initDir($basePath, $path): bool|string + function initDir($path): bool|string { - $explode = array_filter(explode('/', $path)); - $_path = '/' . trim($basePath, '/') . '/'; - foreach ($explode as $value) { - $_path .= $value . '/'; - if (!is_dir(rtrim($_path, '/'))) { - mkdir(rtrim($_path, '/')); - } - if (!is_dir($_path)) { - throw new Exception('System error, directory ' . $_path . ' is not writable'); - } - } - return realpath($_path); + return mkdir($path, 0777, true); }