1、安裝inotify擴展php
一、下載inotify擴展源碼數組
https://pecl.php.net/package/inotify
對於php7以上版本,請下載 inotify-2.0.0.tgz。安全
二、編譯安裝cookie
tar xf inotify-2.0.0.tgz cd inotify-2.0.0 /data/nmp/php/bin/phpize ./configure --with-php-config=/data/nmp/php/bin/php-config make && make install
三、修改php.ini,添加php7
extension = inotify.so
2、使用inotify進行文件監控函數
php的inotify擴展提供了監控文件或目錄的功能,能夠用來實現,服務的熱更新,或安全監控。this
inotify一共提供了5個函數:.net
一、inotify_init() 用於初始化一個實例,返回的是一個 stream 資源對象,能夠被標準stream函數使用,如stream_set_blocking()。code
二、inotify_add_watch() 將一個文件或目錄添加到監控列表中,若是存在,則替換。對象
三、inotify_read() 從 inotify 實例中讀取事件,若是沒有返回false,若是有,返回一個 inotify 事件數組。
四、inotify_queue_len() 返回隊列中事件的個數,能夠理解爲系統把 inotify_add_watch 關注的事件一個個加入隊列,經過 inotify_read() 讀取事件。
五、inotify_rm_watch() 取消監控。
一個簡單的阻塞監控代碼:
<?php //初始化一個inotify實例 $fd = inotify_init(); //設置爲阻塞模式,當inotify_read()沒法從$fd中讀取到事件時,會一直阻塞在那裏。 stream_set_blocking($fd, 1); //事件掩碼 $eventMask = [ IN_ACCESS => 'File was accessed (read)', IN_MODIFY => 'File was modified', IN_ATTRIB => 'Metadata changed', IN_CLOSE_WRITE => 'File opened for writing was closed', IN_CLOSE_NOWRITE => 'File not opened for writing was closed', IN_OPEN => 'File was opened', IN_MOVED_TO => 'File moved into watched directory', IN_MOVED_FROM => 'File moved out of watched directory', IN_CREATE => 'File or directory created in watched directory', IN_DELETE => 'File or directory deleted in watched directory', IN_DELETE_SELF => 'Watched file or directory was deleted', IN_MOVE_SELF => 'Watch file or directory was moved', IN_CLOSE => 'Equals to IN_CLOSE_WRITE | IN_CLOSE_NOWRITE', IN_MOVE => 'Equals to IN_MOVED_FROM | IN_MOVED_TO', IN_ALL_EVENTS => 'Bitmask of all the above constants', IN_UNMOUNT => 'File system containing watched object was unmounted', IN_Q_OVERFLOW => 'Event queue overflowed (wd is -1 for this event)', IN_IGNORED => 'Watch was removed (explicitly by inotify_rm_watch() or because file was removed or filesystem unmounted', IN_ISDIR => 'Subject of this event is a directory', IN_ONLYDIR => 'Only watch pathname if it is a directory', IN_DONT_FOLLOW => 'Do not dereference pathname if it is a symlink', IN_MASK_ADD => 'Add events to watch mask for this pathname if it already exists', IN_ONESHOT => 'Monitor pathname for one event, then remove from watch list.', 1073741840 => 'High-bit: File not opened for writing was closed', 1073741856 => 'High-bit: File was opened', 1073742080 => 'High-bit: File or directory created in watched directory', 1073742336 => 'High-bit: File or directory deleted in watched directory', ]; //添加監控,修改,建立,刪除事件 $watch = inotify_add_watch($fd, './tmp', IN_MODIFY | IN_CREATE | IN_DELETE | IN_ISDIR); //循環讀取事件 while ($events = inotify_read($fd)) { // $events會是一個二維數組 // wd 表示inotify_add_watch()返回的監控描述符 // mask 表示事件掩碼 // cookie 表示惟一的ID,用於關聯相關的事件 // name 表示文件的名稱 echo date('Y-m-d H:i:s'), PHP_EOL; foreach ($events as $event) { $eventName = isset($eventMask[$event['mask']]) ? $eventMask[$event['mask']] : ''; echo $event['name'], PHP_EOL; echo $eventName, PHP_EOL; } } //取消監控 inotify_rm_watch($fd, $watch); //關閉資源 fclose($fd);
3、咱們經過非阻塞的方式來監控文件
<?php //初始化一個inotify實例 $fd = inotify_init(); //設置爲非阻塞模式 stream_set_blocking($fd, 0); //事件掩碼 $eventMask = [ IN_ACCESS => 'File was accessed (read)', IN_MODIFY => 'File was modified', IN_ATTRIB => 'Metadata changed', IN_CLOSE_WRITE => 'File opened for writing was closed', IN_CLOSE_NOWRITE => 'File not opened for writing was closed', IN_OPEN => 'File was opened', IN_MOVED_TO => 'File moved into watched directory', IN_MOVED_FROM => 'File moved out of watched directory', IN_CREATE => 'File or directory created in watched directory', IN_DELETE => 'File or directory deleted in watched directory', IN_DELETE_SELF => 'Watched file or directory was deleted', IN_MOVE_SELF => 'Watch file or directory was moved', IN_CLOSE => 'Equals to IN_CLOSE_WRITE | IN_CLOSE_NOWRITE', IN_MOVE => 'Equals to IN_MOVED_FROM | IN_MOVED_TO', IN_ALL_EVENTS => 'Bitmask of all the above constants', IN_UNMOUNT => 'File system containing watched object was unmounted', IN_Q_OVERFLOW => 'Event queue overflowed (wd is -1 for this event)', IN_IGNORED => 'Watch was removed (explicitly by inotify_rm_watch() or because file was removed or filesystem unmounted', IN_ISDIR => 'Subject of this event is a directory', IN_ONLYDIR => 'Only watch pathname if it is a directory', IN_DONT_FOLLOW => 'Do not dereference pathname if it is a symlink', IN_MASK_ADD => 'Add events to watch mask for this pathname if it already exists', IN_ONESHOT => 'Monitor pathname for one event, then remove from watch list.', 1073741840 => 'High-bit: File not opened for writing was closed', 1073741856 => 'High-bit: File was opened', 1073742080 => 'High-bit: File or directory created in watched directory', 1073742336 => 'High-bit: File or directory deleted in watched directory', ]; //添加監控,修改,建立,刪除事件 $watch = inotify_add_watch($fd, './tmp', IN_MODIFY | IN_CREATE | IN_DELETE | IN_ISDIR); //循環 while (true) { //全部可讀的流,監聽讀事件 $reads = [$fd]; //可寫的流,設爲空,表示咱們不須要監聽寫事件 $write = []; //異常事件 $except = []; //參數四,表示超時時間 //若是設置成大於0,表示等待事件發生的超時時間 //若是設置等於0,表示不斷調用select,執行後立馬返回 //若是設置null,表示阻塞一直到監聽發生變化 if (stream_select($reads, $write, $except, 3) > 0) { //注意$reads是引用傳遞,select每次會把可讀的流放到$reads中,因此$reads變量是會改變的 if (!empty($reads)) { foreach ($reads as $read) { //從可讀流中讀取數據 $events = inotify_read($read); foreach ($events as $event) { echo $event['name'], ' --- ', $eventMask[$event['mask']], PHP_EOL; } } } } else { echo 'select timeout ...', PHP_EOL; } } //取消監控 inotify_rm_watch($fd, $watch); //關閉資源 fclose($fd);
4、封裝一個類,用來監控文件或目錄。
<?php class InotifyMonitor { //須要監控的事件 const MONITOR_EVENT = IN_MODIFY | IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_CLOSE_WRITE; //事件掩碼 const EVENT_MASK = [ IN_ACCESS => 'File was accessed (read)', IN_MODIFY => 'File was modified', IN_ATTRIB => 'Metadata changed', IN_CLOSE_WRITE => 'File opened for writing was closed', IN_CLOSE_NOWRITE => 'File not opened for writing was closed', IN_OPEN => 'File was opened', IN_MOVED_TO => 'File moved into watched directory', IN_MOVED_FROM => 'File moved out of watched directory', IN_CREATE => 'File or directory created in watched directory', IN_DELETE => 'File or directory deleted in watched directory', IN_DELETE_SELF => 'Watched file or directory was deleted', IN_MOVE_SELF => 'Watch file or directory was moved', IN_CLOSE => 'Equals to IN_CLOSE_WRITE | IN_CLOSE_NOWRITE', IN_MOVE => 'Equals to IN_MOVED_FROM | IN_MOVED_TO', IN_ALL_EVENTS => 'Bitmask of all the above constants', IN_UNMOUNT => 'File system containing watched object was unmounted', IN_Q_OVERFLOW => 'Event queue overflowed (wd is -1 for this event)', IN_IGNORED => 'Watch was removed (explicitly by inotify_rm_watch() or because file was removed or filesystem unmounted', IN_ISDIR => 'Subject of this event is a directory', IN_ONLYDIR => 'Only watch pathname if it is a directory', IN_DONT_FOLLOW => 'Do not dereference pathname if it is a symlink', IN_MASK_ADD => 'Add events to watch mask for this pathname if it already exists', IN_ONESHOT => 'Monitor pathname for one event, then remove from watch list.', 1073741840 => 'High-bit: File not opened for writing was closed', 1073741856 => 'High-bit: File was opened', 1073742080 => 'High-bit: File or directory created in watched directory', 1073742336 => 'High-bit: File or directory deleted in watched directory', ]; //用於保存inotify_init返回的資源 public $fds = []; //用於保存監控的文件路徑 public $paths = []; //用於保存inotify_add_watch返回的監控描述符 public $wds = []; //超時時間 public $timeout = 3; /** * $paths添加監控的路徑數組,能夠是目錄或文件 */ public function __construct($paths = []) { if (!empty($paths)) { foreach ($paths as $path) { if (file_exists($path)) { if (is_dir($path)) { $this->addDir($path); } else { $this->addFile($path); } } } } } public function __destruct() { if (!empty($this->fds)) { foreach ($this->fds as $fd) { fclose($fd); } } } /** * 添加文件監控 */ public function addFile($file) { $file = realpath($file); $fd = inotify_init(); $fid = (int)$fd; //保存inotify資源 $this->fds[$fid] = $fd; //設置爲非阻塞模式 stream_set_blocking($this->fds[$fid], 0); //保存文件路徑 $this->paths[$fid] = $file; //保存監控描述符 $this->wds[$fid] = inotify_add_watch($this->fds[$fid], $file, self::MONITOR_EVENT); } /** * 添加目錄監控 */ public function addDir($dir) { $dir = realpath($dir); if ($dh = opendir($dir)) { //將目錄加入監控中 $fd = inotify_init(); //通常文件的資源描述符是一個整形,能夠用來當索引 $fid = (int)$fd; $this->fds[$fid] = $fd; stream_set_blocking($this->fds[$fid], 0); $this->paths[$fid] = $dir; $this->wds[$fid] = inotify_add_watch($this->fds[$fid], $dir, self::MONITOR_EVENT); //遍歷目錄下文件 while (($file = readdir($dh)) !== false) { if ($file == '.' || $file == '..') { continue; } $file = $dir . DIRECTORY_SEPARATOR . $file; if (is_dir($file)) { $this->addDir($file); } } closedir($dh); } } /** * 移除監控 */ public function remove($fid) { unset($this->paths[$fid]); fclose($this->fds[$fid]); unset($this->fds[$fid]); } /** * 運行監控 */ public function run() { while (true) { $reads = $this->fds; $write = []; $except = []; if (stream_select($reads, $write, $except, $this->timeout) > 0) { if (!empty($reads)) { foreach ($reads as $read) { //從可讀流中讀取數據 $events = inotify_read($read); //資源描述符,整形 $fid = (int)$read; //獲取inotify實例的路徑 $path = $this->paths[$fid]; foreach ($events as $event) { $file = $path . DIRECTORY_SEPARATOR . $event['name']; switch ($event['mask']) { case IN_CREATE: case 1073742080: if (is_dir($file)) { echo 'add ...', PHP_EOL; echo 'fid : ', $fid, PHP_EOL; $this->addDir($file); } break; case IN_DELETE_SELF: if (is_dir($file)) { echo 'remove ...', PHP_EOL; echo 'fid : ', $fid, PHP_EOL; $this->remove($fid); $key = array_search($read, $reads); unset($reads[$key]); } break; } echo $event['name'], ' --- ', self::EVENT_MASK[$event['mask']], PHP_EOL; } } } } else { echo '------------------', PHP_EOL; echo '當前監控的路徑列表', PHP_EOL; print_r($this->paths); echo '------------------', PHP_EOL; } } } } (new InotifyMonitor(['./tmp', './test.php']))->run();