#!/usr/bin/env php false, 'log_level' => SWOOLE_LOG_INFO]); $hashes = []; $serve = null; echo CONSOLE_COLOR_YELLOW . "🚀 Start @ " . date('Y-m-d H:i:s') . PHP_EOL; start(); state(); Timer::tick(SCAN_INTERVAL, 'watch'); function killOldProcess() { // pid存在则关闭存在的进程 if (file_exists(PID_FILE_PATH) && $pid = @file_get_contents(PID_FILE_PATH)) { if (!@posix_kill($pid)) forceKill(); } else forceKill(); } function forceKill($match = '') { if (!$match) { $match = @$_ENV['APP_NAME'] . '.Master'; } // 适配MacOS if (PHP_OS == 'Darwin') $match = ENTRY_POINT_FILE; $command = "ps -ef | grep '$match' | grep -v grep | awk '{print $2}' | xargs kill -9 2>&1"; // 找不到pid,强杀进程 exec($command); } function start() { // 杀旧进程 killOldProcess(); global $serve; $serve = new Process('serve', true); $serve->start(); if (false === $serve->pid) { echo swoole_strerror(swoole_errno()) . PHP_EOL; exit(1); } addEvent($serve); } function addEvent($serve) { Event::add($serve->pipe, function () use (&$serve) { $message = @$serve->read(); if (!empty($message)) { $debug = strpos($message, '[DEBUG]') !== false; $info = strpos($message, '[INFO]') !== false; $warn = strpos($message, '[WARNING]') !== false; $error = strpos($message, '[ERROR]') !== false; if ($debug) { echo CONSOLE_COLOR_BLUE . $message; } elseif ($info) { echo CONSOLE_COLOR_GREEN . $message; } elseif ($warn) { echo CONSOLE_COLOR_YELLOW . $message; } elseif ($error) { echo CONSOLE_COLOR_RED . $message; } else echo CONSOLE_COLOR_DEFAULT . $message; echo CONSOLE_COLOR_DEFAULT; } }); } function watch() { global $hashes; foreach ($hashes as $pathName => $currentHash) { if (!file_exists($pathName)) { unset($hashes[$pathName]); continue; } $newHash = fileHash($pathName); if ($newHash != $currentHash) { change(); state(); break; } } } function state() { global $hashes; $files = phpFiles(WATCH_DIR); $hashes = array_combine($files, array_map('fileHash', $files)); $count = count($hashes); echo CONSOLE_COLOR_YELLOW . "📡 Watching $count files..." . PHP_EOL; } function change() { global $serve; echo CONSOLE_COLOR_YELLOW . "🔄 Restart @ " . date('Y-m-d H:i:s') . PHP_EOL; Process::kill($serve->pid); start(); } function serve(Process $serve) { $opt = getopt('c'); # if (isset($opt['c'])) echo exec(PHP . ' ' . ENTRY_POINT_FILE . ' di:init-proxy') . '..' . PHP_EOL; if (isset($opt['c'])) delDir('./runtime/container'); $serve->exec(PHP, START_COMMAND); } function fileHash(string $pathname): string { $contents = file_get_contents($pathname); if (false === $contents) { return 'deleted'; } return md5($contents); } function phpFiles(string $dirname): array { $directory = new RecursiveDirectoryIterator($dirname); $filter = new Filter($directory); $iterator = new RecursiveIteratorIterator($filter); return array_map(function ($fileInfo) { return $fileInfo->getPathname(); }, iterator_to_array($iterator)); } function delDir($path) { if (is_dir($path)) { //扫描一个目录内的所有目录和文件并返回数组 $dirs = scandir($path); foreach ($dirs as $dir) { //排除目录中的当前目录(.)和上一级目录(..) if ($dir != '.' && $dir != '..') { //如果是目录则递归子目录,继续操作 $sonDir = $path . '/' . $dir; if (is_dir($sonDir)) { //递归删除 delDir($sonDir); //目录内的子目录和文件删除后删除空目录 @rmdir($sonDir); } else { //如果是文件直接删除 @unlink($sonDir); } } } @rmdir($path); } } class Filter extends RecursiveFilterIterator { public function accept() { if ($this->current()->isDir()) { if (preg_match('/^\./', $this->current()->getFilename())) { return false; } return !in_array($this->current()->getFilename(), EXCLUDE_DIR); } $list = array_map(function (string $item): string { return "\.$item"; }, explode(',', WATCH_EXT)); $list = implode('|', $list); return preg_match("/($list)$/", $this->current()->getFilename()); } }