123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- #!/usr/bin/env php
- <?php
- /**
- * Hyperf Watch Hot Reload Scripts
- * From: https://github.com/ha-ni-cc/hyperf-watch
- * Author: hanicc@qq.com
- * Usage:
- * Open the terminal console in the project root directory and enter:php watch
- * 在项目根目录下打开终端控制台,输入:php watch
- * If you want to clean the /runtime/container cache, enter: php watch -c
- * 如果你想要清除/runtime/container缓存,则输入:php watch -c
- */
- # PHP Bin File PHP程序所在路径(默认自动获取)
- const PHP_BIN_FILE = 'which php';
- # Watch Dir 监听目录(默认监听脚本所在的根目录)
- const WATCH_DIR = __DIR__ . '/';
- # Watch Ext 监听扩展名(多个可用英文逗号隔开)
- const WATCH_EXT = 'php,env';
- # Exclude Dir 排除目录(不监听的目录,数组形式)
- const EXCLUDE_DIR = ['vendor', 'runtime', 'public'];
- # Entry Point File 入口文件
- const ENTRY_POINT_FILE = __DIR__ . '/bin/hyperf.php';
- # Start Command 启动命令
- const START_COMMAND = [ENTRY_POINT_FILE, 'start'];
- # PID File Path PID文件路径
- const PID_FILE_PATH = __DIR__ . '/runtime/hyperf.pid';
- # Scan Interval 扫描间隔(毫秒,默认2000)
- const SCAN_INTERVAL = 2000;
- # Console Color 控制台颜色
- const CONSOLE_COLOR_DEFAULT = "\033[0m";
- const CONSOLE_COLOR_RED = "\033[0;31m";
- const CONSOLE_COLOR_GREEN = "\033[0;32m";
- const CONSOLE_COLOR_YELLOW = "\033[0;33m";
- const CONSOLE_COLOR_BLUE = "\033[0;34m";
- if (!function_exists('exec')) {
- echo '[x] 请在php.ini配置中取消禁用exec方法' . PHP_EOL;
- exit(1);
- }
- define('PHP', PHP_BIN_FILE == 'which php' ? exec('which php') : PHP_BIN_FILE);
- if (!file_exists(PHP) || !is_executable(PHP)) {
- echo '[x] PHP bin (" ' . PHP . ' ") 路径没有找到或无法执行,请确认路径正确?' . PHP_EOL;
- exit(1);
- }
- if (!file_exists(ENTRY_POINT_FILE)) {
- echo '[x] 入口文件 ("' . ENTRY_POINT_FILE . '") 没有找到,请确认文件存在?' . PHP_EOL;
- exit(1);
- }
- # 加载env
- $content = @file_get_contents('.env');
- $values = array_filter(preg_split("/(\r\n|\n|\r)/", $content));
- foreach ($values as $val) {
- if (substr($val, 0, 1) === '#') {
- continue;
- }
- list($name, $value) = explode('=', $val);
- $_ENV[$name] = $value;
- }
- use Swoole\Process;
- use Swoole\Timer;
- use Swoole\Event;
- swoole_async_set(['enable_coroutine' => 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());
- }
- }
|