通过inotify扩展监控文件或目录的变化,如果发生变化,就执行命令。

可以应用于 swoole 中,如果文件发生变化,就执行 kill -USR1 进程PID 来实现热更新。

<?php
class Monitor
{
    public $dir = '';
    public $cmd = '';
    public $timeout = 1;

    public function __construct()
    {
        if (!extension_loaded('inotify')) {
            echo '请安装inotify扩展', PHP_EOL;
            exit;
        }
        //解析命令行参数,有一个:号表示必填项
        $opts = getopt('', ['dir:', 'cmd:']);
        if (!$opts) {
            echo '参数输入错误', PHP_EOL;
            exit;
        }
        if (empty($opts['dir'])) {
            echo '--dir 是必填项', PHP_EOL;
            exit;
        }
        if (empty($opts['cmd'])) {
            echo '--cmd 是必填项', PHP_EOL;
            exit;
        }
        $this->dir = $opts['dir'];
        $this->cmd = trim($opts['cmd']);
        $this->run();
    }

    //对目录进行监控
    public function run()
    {
        $dirs = $this->getDirs($this->dir);
        if (empty($dirs)) {
            return false;
        }
        $fd = inotify_init();
        //设置为非阻塞模式
        stream_set_blocking($fd, 0);
        foreach ($dirs as $dir) {
            $watch = inotify_add_watch($fd, $dir, IN_MODIFY | IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_CLOSE_WRITE);
            if (!$watch) {
                echo "{$dir} 添加监控错误", PHP_EOL;
                exit;
            }
        }
        while (true) {
            $reads = [$fd];
            $write = [];
            $except = [];
            if (stream_select($reads, $write, $except, $this->timeout) > 0) {
                if (!empty($reads)) {
                    foreach ($reads as $read) {
                        //文件改变
                        $fileChg = false;
                        //目录改变
                        $dirChg = false;
                        //从可读流中读取数据
                        $events = inotify_read($read);
                        $fileName = '';
                        foreach ($events as $event) {
                            $fileName = $event['name'];
                            switch ($event['mask']) {
                                case IN_CREATE:
                                case IN_DELETE:
                                    $fileChg = true;
                                    break;
                                case 1073742080:
                                case 1073742336:
                                    $dirChg = true;
                                    break;
                            }
                        }
                        if ($fileChg) {
                            echo "文件 {$fileName} 发生改变, 执行命令 {$this->cmd}", PHP_EOL;
                            echo shell_exec($this->cmd), PHP_EOL;
                        }
                        if ($dirChg) {
                            echo "目录 {$fileName} 发生改变, 执行命令 {$this->cmd}", PHP_EOL;
                            echo shell_exec($this->cmd), PHP_EOL;
                            return $this->run();
                        }
                    }
                }
            }
        }
        return true;
    }

    //递归的获取当前目录下所有子目录路径
    public function getDirs($dir)
    {
        $dir = realpath($dir);
        $dh = opendir($dir);
        if (!$dh) {
            return [];
        }
        $dirs = [];
        $dirs[] = $dir;
        while (($file = readdir($dh)) !== false) {
            if ($file == '.' || $file == '..') {
                continue;
            }
            $full = $dir . DIRECTORY_SEPARATOR . $file;
            if (is_dir($full)) {
                $dirs = array_merge($dirs, $this->getDirs($full));
            }
        }
        closedir($dh);
        return $dirs;
    }
}

(new Monitor());

演示如下所示:

posted on 2019-06-01 18:00  怀素真  阅读(1122)  评论(0编辑  收藏  举报