通過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());
演示如下所示:

