PHP用inotify扩展监控文件

PHP用inotify扩展监控文件

可以用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()

1. 安装

$ pecl install inotify

安装后在php.ini中加上

extension=inotify.so

2. 函数概览

2.1 inotify_init

用于初始化一个inotify实例。

函数原型:

resource inotify_init(void)

失败则返回FALSE

2.2 inotify_add_watch

添加一个文件或文件夹至监控列表中。如果已经存在,则视为修改。

函数原型:

int inotify_add_watch(resource $inotify_instance, string $pathname, int $mask)
  • $inotify_instance inotify_init()返回的资源
  • $pathname 要监控的文件或文件夹
  • $mask 要监控的事件

该函数的返回值是一个唯一的监控描述符。可以在inotify_rm_watch()中使用,用于移除对它的监控。

2.3 inotify_read

从inotify instance中读取事件。

函数原型:

array inotify_read(resource $inotify_instance)

如果$inotify_instance是阻塞的(可用stream_set_blocking设置为阻塞),则有事件发生时才会返回。

如果是非阻塞的,没有事件,返回FALSE,有事件则返回一个inotify事件数组,包含一个或多个inotify事件。

inotify事件的内容如下:

  • wd 由inotify_add_watch() 返回的监控描述符
  • mask 事件掩码
  • cookie 唯一的ID,用于关联相关的事件
  • name 文件的名称。发生该事件的对象。

2.4 inotify_queue_len

返回队列中事件的个数。可以想象成 系统把实际发生了的、inotify_add_watch函数设置的关注事件放在一个队列中,程序用inotify_read从队列中读取事件。

函数原型:

int inotify_queue_len(resource $inotify_instance)

2.5 inotify_rm_watch

取消一个监控。

函数原型:

bool inotify_rm_watch(resource $inotify_instance, int $watch_descriptor)
  • $inotify_instance 由inotify_init()返回的资源
  • $watch_descriptor 由inotify_add_watch()返回的监控描述符

2.6 常量

该扩展的常量可作为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

3. 使用示例

测试程序如下:



$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

你可能感兴趣的:(PHP)