diff --git a/Kiri.php b/Kiri.php index d835b148..63b70222 100644 --- a/Kiri.php +++ b/Kiri.php @@ -23,9 +23,9 @@ use Swoole\WebSocket\Server; defined('DB_ERROR_BUSY') or define('DB_ERROR_BUSY', 'The database is busy. Please try again later.'); defined('SELECT_IS_NULL') or define('SELECT_IS_NULL', 'Query data does not exist, please check the relevant conditions.'); defined('PARAMS_IS_NULL') or define('PARAMS_IS_NULL', 'Required items cannot be empty, please add.'); -defined('CONTROLLER_PATH') or define('CONTROLLER_PATH', APP_PATH . 'controllers/'); -defined('MODEL_PATH') or define('MODEL_PATH', APP_PATH . 'models/'); -defined('COMPONENT_PATH') or define('COMPONENT_PATH', APP_PATH . 'components/'); +defined('CONTROLLER_PATH') or define('CONTROLLER_PATH', realpath(APP_PATH . 'controllers/')); +defined('MODEL_PATH') or define('MODEL_PATH', realpath(APP_PATH . 'models/')); +defined('COMPONENT_PATH') or define('COMPONENT_PATH', realpath(APP_PATH . 'components/')); /** diff --git a/kiri-engine/FileListen/HotReload.php b/kiri-engine/FileListen/HotReload.php index 77e06bb2..91b8d69b 100644 --- a/kiri-engine/FileListen/HotReload.php +++ b/kiri-engine/FileListen/HotReload.php @@ -3,18 +3,17 @@ namespace Kiri\FileListen; use Exception; +use Kiri; use Kiri\Abstracts\Config; +use Kiri\Annotation\Inject; use Kiri\Core\Json; use Kiri\Error\Logger; use Kiri\Exception\ConfigException; -use Kiri; -use Kiri\Annotation\Inject; use Swoole\Coroutine; use Swoole\Process; use Swoole\Timer; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -25,201 +24,203 @@ class HotReload extends Command { - public bool $isReloading = FALSE; - public bool $isReloadingOut = FALSE; - public ?array $dirs = []; + public bool $isReloading = FALSE; + public bool $isReloadingOut = FALSE; + public ?array $dirs = []; - public int $events; + public int $events; - public int $int = -1; + public int $int = -1; - private ?Process $process = NULL; + private ?Process $process = NULL; - public Inotify|Scaner $driver; + public Inotify|Scaner $driver; - #[Inject(Logger::class)] - public Logger $logger; + #[Inject(Logger::class)] + public Logger $logger; - protected mixed $source = NULL; + protected mixed $source = NULL; - protected mixed $pipes = []; + protected mixed $pipes = []; - protected ?Coroutine\Channel $channel = NULL; + protected ?Coroutine\Channel $channel = NULL; - /** - */ - protected function configure() - { - $this->setName('sw:wather')->setDescription('server start'); - } + /** + */ + protected function configure() + { + $this->setName('sw:wather')->setDescription('server start'); + } - /** - * @throws ConfigException - * @throws \ReflectionException - * @throws Exception - */ - protected function initCore() - { - set_error_handler([$this, 'errorHandler']); - $this->dirs = Config::get('inotify', [APP_PATH . 'app']); - if (!extension_loaded('inotify')) { - $this->driver = Kiri::getDi()->make(Scaner::class, [$this->dirs, $this]); - } else { - $this->driver = Kiri::getDi()->make(Inotify::class, [$this->dirs, $this]); - } - $this->clearOtherService(); - $this->setProcessName(); - } + /** + * @throws ConfigException + * @throws Exception + */ + protected function initCore() + { + set_error_handler([$this, 'errorHandler']); + $this->dirs = Config::get('inotify', [APP_PATH . 'app']); + if (!extension_loaded('inotify')) { + $this->driver = Kiri::getDi()->make(Scaner::class, [$this->dirs, $this]); + } else { + $this->driver = Kiri::getDi()->make(Inotify::class, [$this->dirs, $this]); + } + $this->clearOtherService(); + $this->setProcessName(); + } - /** - * @throws ConfigException - */ - public function setProcessName() - { - swoole_async_set(['enable_coroutine' => FALSE]); - if (Kiri::getPlatform()->isLinux()) { - swoole_set_process_name('[' . Config::get('id', 'sw service.') . '].sw:wather'); - } - } + /** + * @throws ConfigException + */ + public function setProcessName() + { + swoole_async_set(['enable_coroutine' => FALSE]); + if (Kiri::getPlatform()->isLinux()) { + swoole_set_process_name('[' . Config::get('id', 'sw service.') . '].sw:wather'); + } + } - /** - * @throws Exception - */ - public function clearOtherService() - { - if (file_exists(storage('.manager.pid'))) { - $pid = (int)file_get_contents(storage('.manager.pid')); - if ($pid > 0 && Process::kill($pid, 0)) { - Process::kill($pid, 15) && Process::wait(TRUE); - } - } - file_put_contents(storage('.manager.pid'), getmypid()); - } + /** + * @throws Exception + */ + public function clearOtherService() + { + if (file_exists(storage('.manager.pid'))) { + $pid = (int)file_get_contents(storage('.manager.pid')); + if ($pid > 0 && Process::kill($pid, 0)) { + Process::kill($pid, 15) && Process::wait(TRUE); + } + } + file_put_contents(storage('.manager.pid'), getmypid()); + } - /** - * @throws Exception - */ - public function errorHandler() - { - $error = func_get_args(); + /** + * @throws Exception + */ + public function errorHandler() + { + $error = func_get_args(); - $path = ['file' => $error[2], 'line' => $error[3]]; + $path = ['file' => $error[2], 'line' => $error[3]]; - if ($error[0] === 0) { - $error[0] = 500; - } - $data = Json::to(500, $error[1], $path); + if ($error[0] === 0) { + $error[0] = 500; + } + $data = Json::to(500, $error[1], $path); - $this->logger->error($data, 'error'); - } + $this->logger->error($data, 'error'); + } - /** - * @param InputInterface $input - * @param OutputInterface $output - * @return int - * @throws ConfigException - * @throws Exception - */ - public function execute(InputInterface $input, OutputInterface $output): int - { - $this->initCore(); + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + * @throws ConfigException + * @throws Exception + */ + public function execute(InputInterface $input, OutputInterface $output): int + { + $this->initCore(); - $this->trigger_reload(); - Timer::tick(1000, fn() => $this->healthCheck()); + $this->trigger_reload(); + Timer::tick(1000, fn() => $this->healthCheck()); - Process::signal(SIGTERM, [$this, 'onSignal']); - Process::signal(SIGKILL, [$this, 'onSignal']); + Process::signal(SIGTERM, [$this, 'onSignal']); + Process::signal(SIGKILL, [$this, 'onSignal']); - $this->driver->start(); - return 0; - } + $this->driver->start(); + return 0; + } - /** - * @throws Exception - */ - public function healthCheck() - { - $pid = (int)file_get_contents(storage('.swoole.pid')); - if ($this->int == 1) { - return; - } - if (empty($pid)) { - $this->logger->warning('service is shutdown you need reload.'); - $this->trigger_reload(); - } else if (!Process::kill($pid, 0)) { - $this->logger->warning('service is shutdown you need reload.'); - $this->trigger_reload(); - } - } + /** + * @throws Exception + */ + public function healthCheck() + { + $pid = (int)file_get_contents(storage('.swoole.pid')); + if ($this->int == 1) { + return; + } + if (empty($pid)) { + $this->logger->warning('service is shutdown you need reload.'); + $this->trigger_reload(); + } else if (!Process::kill($pid, 0)) { + $this->logger->warning('service is shutdown you need reload.'); + $this->trigger_reload(); + } + } - /** - * @param $data - * @throws Exception - */ - public function onSignal($data) - { - if (!$data) { - return; - } - Timer::clearAll(); - $this->driver->clear(); - $this->stopServer(); - while ($ret = Process::wait(TRUE)) { - echo "PID={$ret['pid']}\n"; - sleep(1); - } - } + /** + * @param $data + * @throws Exception + */ + public function onSignal($data) + { + if (!$data) { + return; + } + Timer::clearAll(); + $this->driver->clear(); + $this->stopServer(); + while ($ret = Process::wait(TRUE)) { + echo "PID={$ret['pid']}\n"; + sleep(1); + } + } - /** - * @throws Exception - */ - protected function stopServer() - { - $pid = file_get_contents(storage('.swoole.pid')); - if (!empty($pid) && Process::kill($pid, 0)) { - Process::kill($pid, SIGTERM); - } - if ($this->process && Process::kill($this->process->pid, 0)) { - Process::kill($this->process->pid) && Process::wait(TRUE); - } - } + /** + * @throws Exception + */ + protected function stopServer() + { + $pid = file_get_contents(storage('.swoole.pid')); + if (!empty($pid) && Process::kill($pid, 0)) { + Process::kill($pid, SIGTERM); + } + if ($this->process && Process::kill($this->process->pid, 0)) { + Process::kill($this->process->pid) && Process::wait(TRUE); + } + } - - /** - * 重启 - * - * @throws Exception - */ - public function trigger_reload() - { - if ($this->int == 1) { - return; - } - $this->int = 1; - $this->logger->warning('change reload'); - - $this->stopServer(); - $this->process = new Process(function (Process $process) { - $process->exec(PHP_BINARY, [APP_PATH . "kiri.php", "sw:server", "start"]); - }); - - $this->process->start(); - $this->int = -1; - } + /** + * 重启 + * + * @throws Exception + */ + public function trigger_reload(string $path = '') + { + if ($this->int == 1) { + return; + } + $this->int = 1; + $this->logger->warning('change reload'); + if (!empty($path) && str_starts_with($path, CONTROLLER_PATH)) { + $pid = file_get_contents(storage('.swoole.pid')); + if (!empty($pid) && Process::kill($pid, 0)) { + Process::kill($pid, SIGUSR1); + } + } else { + $this->stopServer(); + $this->process = new Process(function (Process $process) { + $process->exec(PHP_BINARY, [APP_PATH . "kiri.php", "sw:server", "start"]); + }); + $this->process->start(); + } + } } diff --git a/kiri-engine/FileListen/Inotify.php b/kiri-engine/FileListen/Inotify.php index 51499a36..452c169a 100644 --- a/kiri-engine/FileListen/Inotify.php +++ b/kiri-engine/FileListen/Inotify.php @@ -9,161 +9,157 @@ use Swoole\Timer; class Inotify { - private mixed $inotify; - private mixed $events; + private mixed $inotify; + private mixed $events; - private array $watchFiles = []; + private array $watchFiles = []; - public bool $isReloading = FALSE; + public bool $isReloading = FALSE; - protected int $cid; + protected int $cid; - const IG_DIR = [APP_PATH . 'commands', APP_PATH . '.git', APP_PATH . '.gitee']; + const IG_DIR = [APP_PATH . 'commands', APP_PATH . '.git', APP_PATH . '.gitee']; - /** - * @param array $dirs - * @param HotReload $process - */ - public function __construct(protected array $dirs, public HotReload $process) - { - set_error_handler([$this, 'error']); - set_exception_handler([$this, 'error']); - } + /** + * @param array $dirs + * @param HotReload $process + */ + public function __construct(protected array $dirs, public HotReload $process) + { + set_error_handler([$this, 'error']); + set_exception_handler([$this, 'error']); + } - /** - * @return void - */ - public function error(): void - { + /** + * @return void + */ + public function error(): void + { - } + } - /** - * @throws Exception - */ - public function start() - { - $this->inotify = inotify_init(); - $this->events = IN_MODIFY | IN_DELETE | IN_CREATE | IN_MOVE; - foreach ($this->dirs as $dir) { - if (!is_dir($dir)) continue; - $this->watch($dir); - } - Event::add($this->inotify, [$this, 'check']); - Event::wait(); - } + /** + * @throws Exception + */ + public function start() + { + $this->inotify = inotify_init(); + $this->events = IN_MODIFY | IN_DELETE | IN_CREATE | IN_MOVE; + foreach ($this->dirs as $dir) { + if (!is_dir($dir)) continue; + $this->watch($dir); + } + Event::add($this->inotify, [$this, 'check']); + Event::wait(); + } - public function clear() - { - Event::del($this->inotify); - Event::exit(); - } + public function clear() + { + Event::del($this->inotify); + Event::exit(); + } - /** - * 开始监听 - * @throws Exception - */ - public function check() - { - if (!($events = inotify_read($this->inotify))) { - return; - } - if ($this->isReloading) { - return; - } + /** + * 开始监听 + * @throws Exception + */ + public function check() + { + if (!($events = inotify_read($this->inotify))) { + return; + } + if ($this->isReloading) { + return; + } - $LISTEN_TYPE = [IN_CREATE, IN_DELETE, IN_MODIFY, IN_MOVED_TO, IN_MOVED_FROM]; - foreach ($events as $ev) { - if (!in_array($ev['mask'], $LISTEN_TYPE)) { - continue; - } - //非重启类型 - if (str_ends_with($ev['name'], '.php')) { - Timer::after(3000, fn() => $this->reload()); - $this->isReloading = TRUE; - } - } - } + $LISTEN_TYPE = [IN_CREATE, IN_DELETE, IN_MODIFY, IN_MOVED_TO, IN_MOVED_FROM]; + foreach ($events as $ev) { + if (!in_array($ev['mask'], $LISTEN_TYPE)) { + continue; + } + //非重启类型 + if (str_ends_with($ev['name'], '.php')) { + Timer::after(3000, fn() => $this->reload($ev['name'])); + $this->isReloading = TRUE; + } + } + } - /** - * @throws Exception - */ - public function reload() - { - $this->process->trigger_reload(); - $this->clearWatch(); - foreach ($this->dirs as $root) { - $this->watch($root); - } - $this->process->int = -1; - $this->isReloading = FALSE; - } + /** + * @throws Exception + */ + public function reload($path) + { + $this->process->trigger_reload($path); + $this->clearWatch(); + foreach ($this->dirs as $root) { + $this->watch($root); + } + $this->process->int = -1; + $this->isReloading = FALSE; + } - /** - * @throws Exception - */ - public function clearWatch() - { - foreach ($this->watchFiles as $wd) { - try { - @inotify_rm_watch($this->inotify, $wd); - } catch (\Throwable $exception) { -// logger()->addError($exception->getMessage(), 'throwable'); - } - } - $this->watchFiles = []; - } + /** + * @throws Exception + */ + public function clearWatch() + { + foreach ($this->watchFiles as $wd) { + @inotify_rm_watch($this->inotify, $wd); + } + $this->watchFiles = []; + } - /** - * @param $dir - * @return bool - * @throws Exception - */ - public function watch($dir): bool - { - //目录不存在 - if (!is_dir($dir)) { - return logger()->addError("[$dir] is not a directory."); - } - //避免重复监听 - if (isset($this->watchFiles[$dir])) { - return FALSE; - } + /** + * @param $dir + * @return bool + * @throws Exception + */ + public function watch($dir): bool + { + //目录不存在 + if (!is_dir($dir)) { + return logger()->addError("[$dir] is not a directory."); + } + //避免重复监听 + if (isset($this->watchFiles[$dir])) { + return FALSE; + } - if (in_array($dir, self::IG_DIR)) { - return FALSE; - } + if (in_array($dir, self::IG_DIR)) { + return FALSE; + } - $wd = @inotify_add_watch($this->inotify, $dir, $this->events); - $this->watchFiles[$dir] = $wd; + $wd = @inotify_add_watch($this->inotify, $dir, $this->events); + $this->watchFiles[$dir] = $wd; - $files = scandir($dir); - foreach ($files as $f) { - if ($f == '.' || $f == '..') { - continue; - } - $path = $dir . '/' . $f; - //递归目录 - if (is_dir($path)) { - $this->watch($path); - } else if (!str_ends_with($f, '.php')) { - continue; - } - //检测文件类型 - if (strstr($f, '.') == '.php') { - $wd = @inotify_add_watch($this->inotify, $path, $this->events); - $this->watchFiles[$path] = $wd; - } - } - return TRUE; - } + $files = scandir($dir); + foreach ($files as $f) { + if ($f == '.' || $f == '..') { + continue; + } + $path = $dir . '/' . $f; + //递归目录 + if (is_dir($path)) { + $this->watch($path); + } else if (!str_ends_with($f, '.php')) { + continue; + } + //检测文件类型 + if (strstr($f, '.') == '.php') { + $wd = @inotify_add_watch($this->inotify, $path, $this->events); + $this->watchFiles[$path] = $wd; + } + } + return TRUE; + } } diff --git a/kiri-engine/FileListen/Scaner.php b/kiri-engine/FileListen/Scaner.php index 838c5932..7097ac98 100644 --- a/kiri-engine/FileListen/Scaner.php +++ b/kiri-engine/FileListen/Scaner.php @@ -8,142 +8,145 @@ use Swoole\Timer; class Scaner { - private array $md5Map = []; + private array $md5Map = []; - public bool $isReloading = FALSE; + public bool $isReloading = FALSE; - /** - * @param array $dirs - * @param HotReload $process - */ - public function __construct(protected array $dirs, public HotReload $process) - { - } + /** + * @param array $dirs + * @param HotReload $process + */ + public function __construct(protected array $dirs, public HotReload $process) + { + } - /** - * @throws Exception - */ - public function start(): void - { - $this->loadDirs(); - $this->tick(); - } + /** + * @throws Exception + */ + public function start(): void + { + $this->loadDirs(); + $this->tick(); + } - /** - * @param bool $isReload - * @throws Exception - */ - private function loadDirs(bool $isReload = FALSE) - { - foreach ($this->dirs as $value) { - if (is_bool($path = realpath($value))) { - continue; - } + /** + * @param bool $isReload + * @throws Exception + */ + private function loadDirs(bool $isReload = FALSE) + { + foreach ($this->dirs as $value) { + if (is_bool($path = realpath($value))) { + continue; + } - if (!is_dir($path)) continue; + if (!is_dir($path)) continue; - $this->loadByDir($path, $isReload); - } - } + $this->loadByDir($path, $isReload); + } + } - /** - * @param $path - * @param bool $isReload - * @return void - * @throws Exception - */ - private function loadByDir($path, bool $isReload = FALSE): void - { - if (!is_string($path)) { - return; - } - $path = rtrim($path, '/'); - foreach (glob(realpath($path) . '/*') as $value) { - if (is_dir($value)) { - $this->loadByDir($value, $isReload); - } - if (is_file($value)) { - if ($this->checkFile($value, $isReload)) { - Timer::after(2000, fn() => $this->timerReload()); - $this->isReloading = TRUE; - break; - } - } - } - } + /** + * @param $path + * @param bool $isReload + * @return void + * @throws Exception + */ + private function loadByDir($path, bool $isReload = FALSE): void + { + if (!is_string($path)) { + return; + } + $path = rtrim($path, '/'); + foreach (glob(realpath($path) . '/*') as $value) { + if (is_dir($value)) { + $this->loadByDir($value, $isReload); + } + if (is_file($value)) { + if ($this->checkFile($value, $isReload)) { + Timer::after(2000, fn() => $this->timerReload()); + $this->isReloading = TRUE; + break; + } + } + } + } - /** - * @param $value - * @param $isReload - * @return bool - */ - private function checkFile($value, $isReload): bool - { - $md5 = md5($value); - $mTime = filectime($value); - if (!isset($this->md5Map[$md5])) { - if ($isReload) { - return TRUE; - } - $this->md5Map[$md5] = $mTime; - } else { - if ($this->md5Map[$md5] != $mTime) { - if ($isReload) { - return TRUE; - } - $this->md5Map[$md5] = $mTime; - } - } - return FALSE; - } + /** + * @param $value + * @param $isReload + * @return bool + */ + private function checkFile($value, $isReload): bool + { + $md5 = md5($value); + $mTime = filectime($value); + if (!isset($this->md5Map[$md5])) { + if ($isReload) { + return TRUE; + } + $this->md5Map[$md5] = $mTime; + } else { + if ($this->md5Map[$md5] != $mTime) { + if ($isReload) { + return TRUE; + } + $this->md5Map[$md5] = $mTime; + } + } + return FALSE; + } - /** - * @throws Exception - */ - public function timerReload() - { - $this->isReloading = TRUE; - $this->process->trigger_reload(); + /** + * @throws Exception + */ + public function timerReload($path) + { + $this->isReloading = TRUE; - $this->process->int = -1; + if (str_contains($path, CONTROLLER_PATH)) - $this->loadDirs(); + $this->process->trigger_reload($path); - $this->isReloading = FALSE; - $this->process->isReloadingOut = FALSE; + $this->loadDirs(); - $this->tick(); - } + $this->process->int = -1; + + $this->isReloading = FALSE; + $this->process->isReloadingOut = FALSE; + + $this->tick(); + } - private bool $isStop = FALSE; + private bool $isStop = FALSE; - public function clear() - { - $this->isStop = TRUE; - } + public function clear() + { + $this->isStop = TRUE; + } - /** - * @throws Exception - */ - public function tick() - { - if ($this->isReloading || $this->isStop) { - return; - } + /** + * @throws Exception + */ + public function tick() + { + if ($this->isReloading || $this->isStop) { + return; + } - $this->loadDirs(TRUE); + $this->loadDirs(TRUE); - sleep(2); + sleep(2); - $this->tick(); - } + $this->tick(); + } }