可以用inotify扩展
提供的功能来监控文件/目录,实现某些特殊的功能:如热编译,安全预警。
早期phpStudy有提供防挂马功能,就是用了与inotifyf
类似的机制。
inotify
扩展提供了一系列inotify
函数:inotify_init()
inotify_add_watch()
inotify_rm_watch()
。
类似C语言里的inotify_init()
函数会返回文件描述符,PHP的inotify_init()
则返回 stream资源。可以被标准的Stream函数使用,例如 stream_select()
stream_set_blocking()
,以及fclose()
。
$ pecl install inotify
安装后在php.ini
中加上
extension=inotify.so
用于初始化一个inotify
实例。
函数原型:
resource inotify_init(void)
失败则返回FALSE
添加一个文件或文件夹至监控列表中。如果已经存在,则视为修改。
函数原型:
int inotify_add_watch(resource $inotify_instance, string $pathname, int $mask)
inotify_init()
返回的资源该函数的返回值是一个唯一的监控描述符。可以在inotify_rm_watch()
中使用,用于移除对它的监控。
从inotify instance中读取事件。
函数原型:
array inotify_read(resource $inotify_instance)
如果$inotify_instance
是阻塞的(可用stream_set_blocking
设置为阻塞),则有事件发生时才会返回。
如果是非阻塞的,没有事件,返回FALSE
,有事件则返回一个inotify
事件数组,包含一个或多个inotify
事件。
inotify
事件的内容如下:
inotify_add_watch()
返回的监控描述符返回队列中事件的个数。可以想象成 系统把实际发生了的、inotify_add_watch
函数设置的关注事件放在一个队列中,程序用inotify_read
从队列中读取事件。
函数原型:
int inotify_queue_len(resource $inotify_instance)
取消一个监控。
函数原型:
bool inotify_rm_watch(resource $inotify_instance, int $watch_descriptor)
inotify_init()
返回的资源inotify_add_watch()
返回的监控描述符该扩展的常量可作为inotify_add_watch()
中的mask
参数,用于设置关注的事件类型(如文件打开,修改,删除)。inotify_read()
返回数组的mask
字段也与该常量有关,表示发生的事件类型。
常量 | 含义 |
---|---|
IN_ACCESS | 文件被访问 |
IN_MODIFY | 文件被修改 |
IN_ATTRIB | 文件的元数据发送改变(如权限、修改时间) |
IN_CLOSE_WRITE | 文件被打开用于写,被关闭了 |
IN_CLOSE_NOWRITE | 文件被打开,不用于写,被关闭了 |
IN_OPEN | 文件打开了 |
IN_MOVE_TO | 文件移动到被监控的目录 |
IN_MOVED_FROM | 文件被移出被监控的目录 |
IN_CREATE | 文件被创建 |
IN_DELETE | 文件被删除 |
更多常量可参考 http://php.net/manual/zh/inotify.constants.php
测试程序如下:
$events = [
IN_ACCESS => 'File Accessed',
IN_MODIFY => 'File Modified',
IN_ATTRIB => 'File Metadata Modified',
IN_CLOSE_WRITE => 'File Closed, Opened for Writing',
IN_CLOSE_NOWRITE => 'File Closed, Opened for Read',
IN_OPEN => 'File Opened',
IN_MOVE_TO => 'File Moved In',
IN_MOVED_FROM => 'File Moved Out',
IN_CREATE => 'File Created',
IN_DELETE => 'File Deleted'
];
$my_event = array_sum(array_keys($events));
$ifd = inotify_init();
$wd = inotify_add_watch($ifd, "/root/logs", $my_event);
stream_set_blocking($ifd, 1);
echo "watching /root ...", PHP_EOL;
while ($event_list = inotify_read($ifd)) {
echo date('Y-m-d H:i:s'), PHP_EOL;
foreach ($event_list as $arr) {
$ev_mask = $arr['mask'];
$ev_file = $arr['name'];
if (isset($events[$ev_mask])) {
echo " ",$events[$ev_mask], ", Filename: ", $ev_file, PHP_EOL;
} else {
print_r($arr);
}
}
}
inotify_rm_watch($ifd, $wd);
fclose($ifd);
保存为notify.php
,在一个终端中用php notify.php
运行该脚本。
在另一个终端中依次执行如下命令,观察终端1的输出。
执行命令:
$ touch hello.txt
监控程序输出:
2018-08-16 06:45:56
File Created, Filename: hello.txt
2018-08-16 06:45:56
File Opened, Filename: hello.txt
File Metadata Modified, Filename: hello.txt
File Closed, Opened for Writing, Filename: hello.txt
执行命令:
$ cat hello.txt
监控程序输出:
2018-08-16 06:46:16
File Opened, Filename: hello.txt
2018-08-16 06:46:16
File Closed, Opened for Read, Filename: hello.txt
执行命令:
$ rm -y hello.txt
监控程序输出:
2018-08-16 06:46:43
File Deleted, Filename: hello.txt